独立站容器化:Docker配置、镜像优化、编排与资源限制
一、独立站为什么要容器化,不是直接装在服务器上
Можно поставить PHP, Nginx и MySQL прямо на VPS — работать будет, но через полгода всплывут три проблемы: первая — новый сотрудник потратит два дня на настройку окружения; вторая — при переезде с Alibaba Cloud на Tencent Cloud придётся заново ставить все зависимости; третья — при обновлении патчей безопасности страшно трогать продакшн. Docker упаковывает приложение и зависимости в неизменяемый образ — именно эти три проблемы он и решает.
Простой критерий: если в вашей инструкции по деплою есть “сначала apt install php8.3-fpm, потом установить эти 12 расширений, затем поправить эти 8 пунктов в php.ini” — вам нужна контейнеризация. Запишите эти 12 расширений и 8 настроек в Dockerfile, и дальше на любой машине один docker run всё воспроизведёт. О связке CDN и статики см.Глобальное ускорение CDN.
| Способ развёртывания | Стоимость воспроизведения окружения | Переезд на другой сервер | Скорость отката | Для какого масштаба |
|---|---|---|---|---|
| Прямая установка на VPS | 2 человеко-дня | Заново ставить зависимости | Через снапшот, 30 минут | Этап прототипа |
| Установка через shell-скрипт | 0,5 человеко-дня | Запуск скрипта, но различия остаются | 15 минут | Один сайт работает год |
| Docker-образ | 5 минут | Скачал образ — и запустил | Переключить тег образа, 3 минуты | Продакшн, несколько окружений |
| Оркестрация через Docker Compose | 5 минут | Скопировал yaml — и запустил | Откат одной командой | Приложение + база данных + очередь |
II. Как писать Dockerfile, чтобы образ не раздувался
独立站最常见的Dockerfile错误是单阶段构建:FROM ubuntu,然后装PHP、装Composer、composer install、把源码COPY进去、暴露端口。这种做法打出来的镜像通常在1GB以上,因为包含了编译工具、npm缓存、.git目录。正确做法是多阶段构建:第一阶段用builder镜像编译前端、装composer依赖;第二阶段只把编译产物和运行时拷进alpine镜像。
Несколько жёстких правил: базовый образ — alpine, а не ubuntu; в .dockerignore обязательно исключите node_modules, .git, .env и каталог загрузок; при npm/composer install добавляйте –no-dev и –no-cache; в рантайме используйте непривилегированного пользователя. Цель по размеру образа: чистые PHP/Node-приложения — до 200 МБ, со статикой — не больше 400 МБ. Сканирование образов и базовые требования к безопасности см.Бэкап и восстановление сайта.
Реальный случай: команда через полгода после запуска обнаружила, что образ весит 1,4 ГБ. Проверили по слоям через docker history и увидели, что composer install шёл без –no-dev и затащил в продакшен-образ phpunit и кучу dev-зависимостей; а COPY . . скопировал ещё и каталог .git. Перешли на многоэтапную сборку: на этапе builder ставятся зависимости, в рантайм копируются только каталоги vendor и public — образ сразу упал до 180 МБ, а деплой сократился с 3 минут до 40 секунд.
| Уровень | Типичная ошибка | Как оптимизировать | Экономия размера |
|---|---|---|---|
| Базовый образ | FROM ubuntu:22.04 | FROM php:8.3-fpm-alpine | около 400 МБ |
| Системные зависимости | apt-get install build-essential | Многоэтапная сборка, установка на этапе builder | около 200 МБ |
| PHP-зависимости | composer install | composer install –no-dev –no-cache | около 80 МБ |
| Фронтенд-зависимости | COPY . . | на этапе builder: npm ci && npm run build | около 300 МБ |
| Каталог с исходниками | COPY . /var/www | COPY –from=builder /app/public ./public | около 100 МБ |
| Пользователь в рантайме | запуск от root | RUN adduser -D app | выигрыш в безопасности, размер не меняется |
三、docker compose怎么编排独立站
独立站典型架构是四个容器:Nginx反代、PHP-FPM或Node应用、MySQL/PostgreSQL、Redis。用一个docker-compose.yml把这四个服务串起来,数据卷挂出来持久化MySQL数据,端口只暴露Nginx的80和443,其他服务走内部网络。不要把MySQL端口暴露到公网。
Рабочий фрагмент конфигурации: Nginx depends_on php, php depends_on db и redis; проверка здоровья — команда curl -fsS http://localhost/health каждые 30 секунд; политика перезапуска везде unless-stopped. Стратегия бэкапа: том с данными MySQL по расписанию монтируется в каталог бэкапов на хосте, затем rsync синхронизирует его в другое место. Мониторинг для многоконтейнерной оркестрации см.Мониторинг сайта и оповещения.
Пример: сервер Alibaba Cloud с 4 ядрами и 8 ГБ памяти, на нём три контейнера — WordPress, MySQL и Redis. Контейнер MySQL монтирует named volume для данных, Nginx пробрасывает порты 80 и 443 на хост, контейнер WordPress портов не открывает и работает только во внутренней сети. После перезагрузки хоста docker compose up -d сам поднимает все три сервиса — ничего не нужно запускать вручную. В этом и есть самая простая ценность compose.
| Услуги | Базовый образ | Лимит CPU | Лимит памяти | Персистентность |
|---|---|---|---|---|
| nginx | nginx:1.27-alpine | 0,5 ядра | 128 МБ | Нет |
| php-fpm / node | php:8.3-fpm-alpine | 1–2 ядра | 512 МБ – 1 ГБ | нет (каталог загрузок монтируется томом) |
| mysql | mysql:8.4 | 1–2 ядра | 1–2 ГБ | да (каталог data) |
| redis | redis:7-alpine | 0,5 ядра | 256 МБ | да (персистентность AOF) |
| cron/очереди | тот же образ, что у приложения | 0,5 ядра | 256 МБ | Нет |
4. Ограничения ресурсов: контейнер без лимитов утащит за собой всю машину
Многие запускают Docker для простоты и не указывают –cpus и –memory. В итоге PHP-FPM ловит один медленный запрос, память вырастает с 512MB до 4GB и вытесняет MySQL на той же машине в OOM. Каждый контейнер обязан явно объявлять лимиты ресурсов — это жёсткое требование.
独立站的资源配额参考:应用容器1至2核CPU、512MB至1GB内存;MySQL容器按数据量给,10GB以下数据给1核1GB足够;Nginx和Redis各0.5核、128至256MB。宿主机预留20%余量给Docker daemon和系统。一个判断标准:宿主机总内存的80%分配给容器,20%留给系统,超过这个比例就加机器。CPU按峰值分配,不要按平均分配——独立站流量有工作日高峰和夜间低谷,按平均CPU配会导致高峰期排队。
| 独立站规模 | Рекомендуемая конфигурация | Распределение CPU | Распределение памяти | Ориентир по месячным визитам |
|---|---|---|---|---|
| Период проверки гипотезы | 1 VPS на 2 ядра и 2GB | Приложение 1 ядро + DB 0,5 ядра | Приложение 512MB + DB 1GB | <50 тыс. |
| Период стабильного роста | 1 VPS на 4 ядра и 8GB | Приложение 2 ядра + DB 1,5 ядра | Приложение 2GB + DB 4GB | 50–300 тыс. |
| Пик по многоязычным версиям | 2 машины по 4 ядра и 8GB | Приложение по 2 ядра на машину | По 2GB на машину, DB отдельно 4GB | 300 тыс. – 1 млн |
| Пик в день независимости | Приложение масштабируется до 3–4 машин | По 2 ядра на машину | По 2GB на машину | Трафик с акций и выставок |
5. Когда пора переходить на Swarm или K8s
Контринтуитивный вывод: в 90% случаев независимому сайту K8s не нужен. docker compose на одном VPS тянет 4–8 контейнеров — этого хватает B2B-сайту с посещаемостью до 100 000 в день. Переходить на Swarm стоит только при одном из трёх сигналов: приложение нужно запускать более чем на двух VPS, нужен blue-green деплой с автоматическим переключением трафика, нужен автоматический перезапуск упавших контейнеров по health check.
Критерий простой: если ваш docker-compose.yml содержит больше 30 сервисов или эту среду одновременно поддерживают больше 5 человек, тогда думайте об оркестраторе. В остальных случаях compose — оптимальное решение, и вкладывать силы в контент и SEO выгоднее, чем в поддержку K8s.
Порог K8s в том, что нужен отдельный человек на поддержку etcd, сетевых плагинов и Ingress-контроллера, и только разбор ошибок съедает день в неделю. Для небольшой команды вкладывать это время в контент и SEO выгоднее. Если в будущем действительно понадобится разворачивать несколько экземпляров, про проектирование балансировки читайте в разделе Балансировка нагрузки и развёртывание нескольких экземпляров.
| Инструмент оркестрации | Порог входа | Стоимость поддержки | Подходящее число узлов | Когда его выбирать |
|---|---|---|---|---|
| docker compose | 1–2 дня | Крайне низкая | 1 | Выбор 90% независимых сайтов |
| docker swarm | 3–5 дней | Низкая | 2–10 машин | Нужна оркестрация между машинами и rolling update |
| K8s | 2–4 недели | Высокий | Более 5 машин | Несколько команд, много сервисов, автомасштабирование |
| Managed K8s от облачного провайдера | 1–2 недели | Средний | Более 10 машин | Уже есть выделенный DevOps |
FAQ: частые вопросы
Docker на независимом сайте работает медленнее, чем установка прямо на сервер?
Какой размер образа считается разумным?
Потянет ли docker compose 100 тысяч визитов в день?
Как быть с логами и загруженными файлами в контейнере?
Когда пора переходить на K8s, а когда хватит compose?
Не окажется ли в alpine-образе нехватки зависимостей, из-за которой ничего не запустится?
В заключение
Ценность контейнеризации для независимого сайта на 80% в «единообразии окружения» и на 20% в «оркестрации». Достаточно правильно написать Dockerfile, сжать образ до 200 МБ и аккуратно описать четыре сервиса в compose-файле — и самая болезненная проблема стека, дрейф окружения, решена. Не гонитесь сразу за K8s: это отдельная работа на полную ставку.
План внедрения: на первой неделе пишем многоэтапный Dockerfile и сжимаем образ с 1 ГБ до 200 МБ; на второй — собираем в compose Nginx, приложение, MySQL и Redis и подключаем тома данных; на третьей — подключаем CI/CD к реестру образов, чтобы при пуше кода образ собирался автоматически. Три шага — и перенос сервера сокращается с двух дней до двух часов. О связке с CDN и мониторингом читайте Глобальное ускорение CDN. В повседневной эксплуатации раз в неделю запускайте docker system df и удаляйте висячие образы — так диск не будет постепенно забиваться мусором.
Запомните одну фразу: первый шаг к контейнеризации — не переход на K8s, а возможность одной командой поднять сайт на любом чистом сервере.
