Одиннадцатая, завершающая статья серии «Архитектура и производительность». Мы прошли длинный путь: решили, делить ли приложение на части (Монолит против микросервисов), научили части разговаривать (Проектирование API) и не терять тяжёлые задачи (Очереди сообщений), ускорили ответы (Кеширование), раскидали запросы по серверам (Балансировка нагрузки), навели порядок в метриках, логах, базе и крае сети, а в Нагрузочном тестировании узнали, сколько всё это выдерживает. Остался вопрос, без которого любой ответ из предыдущих статей приходится на каждом сервере искать заново: как сделать так, чтобы одна и та же сборка одинаково работала у разработчика, на тесте и в проде — и кто вообще должен запускать, перезапускать и масштабировать десятки процессов.
Ответы: контейнеры и оркестратор. Звучит как лексика больших компаний, но на деле это один текстовый рецепт сборки и один текстовый файл описания запуска. Разберём по-простому: что такое контейнер и чем он не является, как из рецепта получается образ, как одним файлом поднять приложение с базой и кэшем на ноутбуке, что именно делает оркестратор в продакшене и — честно — когда он вам не нужен вовсе.
Часть I. Зачем контейнеры: конец эпохи «а у меня работало»
1. Почему фраза «а у меня работало» стоит денег
Типичный рабочий день: разработчик дописал обработчик, проверил на ноутбуке — всё зелёное. Выкатили на сервер — падает. Причина оказывается одной из десятка:
- на сервере версия языка старше, чем локально;
- не хватает расширения — например, для сжатия изображений или для подключения к базе;
- локально путь к соку базы прописан в конфиге, а на сервере база живёт по другому адресу;
- разные версии системных библиотек, с которыми код говорит напрямую;
- локально переменная окружения задана в оболочке, а на сервере её никто не выставлял.
Каждая такая история стоит часы: кто-то идёт выяснять, чем машина разработчика отличается от сервера. В монолите на одном VPS отличия ещё терпимы, но когда у вас пять сервисов на разных языках и два тестовых контура, число комбинаций «что с чем стоит» растёт лавинно. Поэтому в части про микросервисы главным плюсом разделения назвали независимый деплой: независимость имеет смысл только тогда, когда каждая часть гарантированно запускается так же, как была проверена.
2. Идея упаковки: процесс вместе с его окружением
Контейнер решает проблему грубо и эффективно: вместе с кодом кладётся всё, что нужно коду для жизни. Не только файлы проекта, но и операционная система в урезанном виде — библиотеки, интерпретатор, утилиты, настройки по умолчанию. Получается один архив, из которого в любой момент можно запустить процесс.
Машину разработчика, тестовый стенд и продакшен объединяет одно: они запускают тот же самый архив, а не «похожую установку». Разница между «у меня стоит PHP 8.3 с расширением intl» и «там стоит что-то близкое» исчезает, потому что спрашивать про сервер бессмысленно — окружение приехало в составе образа.
Важное следствие: окружение перестаёт быть результатом ручной настройки и становится кодом. Набор библиотек описывается текстовым файлом, лежит в системе контроля версий и проходит те же проверки, что и исходники. Ошибка исправляется правкой файла, а не танцами на проде. Тот же принцип, что и в Нагрузочном тестировании: окружение воспроизводится по описанию, а не «так сложилось».
3. Чем контейнер отличается от виртуальной машины
Контейнеры часто путают с виртуальными машинами, а разница принципиальная.
| Виртуальная машина | Контейнер | |
|---|---|---|
| Что изолируется | вся машина вместе со своей ядром системы | только пространство процесса: файлы, сеть, идентификаторы |
| Ядро операционной системы | своё, гостевое | общее с машиной-хостом |
| Запуск | десятки секунд — минуты | десятки — сотни миллисекунд |
| Размер носителя | гигабайты | мегабайты |
| Сколько живёт на сервере | 3–10 | десятки |
Контейнер — не маленькая операционная система, а изолированный процесс на том же ядре, что и остальные. Изоляция строится на двух механизмах ядра: пространство имён (процесс видит только свою файловую ветку, свою сеть, свой список процессов) и контрольные группы (процессу ограничивают память и процессорное время). Отсюда и цена: контейнер нельзя запустить на ядре принципиально другой системы. Отсюда же — его скорость и лёгкость.
Практический вывод для PHP-проекта: два контейнера с разными ядрами не получится, зато десять контейнеров с одной базовой системой, разными версиями языка и разными наборами библиотек — обычное дело.
Часть II. Образ против контейнера: чертёж и изделие
4. Определения, которые путают все новички
Образ (image) — собранный по рецепту неизменяемый набор слоёв: базовая система, библиотеки, файлы приложения, настройки запуска. Он похож на чертёж или установочный архив: сам ничего не делает, но по нему в любой момент получают работающий процесс. Образ имеет имя и метку (tag) — например shop-php:1.4 — и весит от десятков мегабайт до сотен.
Контейнер (container) — запущенный образ: процесс или несколько процессов, у которых появилась своя временная файловая ветка, свой адрес, свои переменные. Контейнер живой: его можно остановить, удалить, посмотреть его логи, залезть внутрь. Удалите контейнер — от него ничего не останется, кроме данных, явно сохранённых снаружи (об этом — в Части IV).
Простейшая аналогия: образ — это класс в коде, контейнер — объект. Класс один, объектов сколько угодно, и каждый живёт своей жизнью.
5. Слои: почему пересборка занимает секунды
Образ собирается из Dockerfile, где каждая строчка рецепта превращается в слой — кусок файловой системы, наложенный на предыдущие. Каждый слой имеет контрольную сумму, и сборщик их кэширует: если строчки не менялись и входные файлы те же, слой берётся готовым, а не пересобирается.
Отсюда правило, которое экономит часы ожидания: сначала кладите то, что меняется редко, и только потом то, что меняется часто. Если первым шагом копировать весь исходник, а вторым ставить зависимости, любая правка одной строки обнулит кэш всех последующих слоёв — и полная установка библиотек будет повторяться на каждой сборке.
Слой 1 базовая система ~80 МБ почти никогда не пересобирается
Слой 2 системные библиотеки ~120 МБ меняются раз в месяц
Слой 3 зависимости проекта ~40 МБ меняются при обновлении пакета
Слой 4 исходники приложения ~5 МБ меняются в каждом коммите
При запуске используется общая база: десять контейнеров из одного образа занимают не десять раз по двести мегабайт — они разделяют нижние слои, а каждый пишет только в свой тонкий рабочий слой. Это и объясняет, почему на сервере живут десятки контейнеров вместо трёх виртуальных машин.
6. Реестр образов: как чертёж попадает на сервер
Собранный образ нужно куда-то класть, чтобы его могли забрать тест и продакшен. Для этого есть реестр (registry) — хранилище образов по имени и метке, вроде registry.example.com/shop/catalog:1.4.2. Работает как репозиторий для бинарников: сборка выкладывает образ (push), сервер его скачивает (pull).
Три практических правила:
- Метка сборки уникальна. Вечно переиспользовать метку
latest— распространённая ошибка: непонятно, что сейчас запущено и куда откатываться. Хорошая метка — версия плюс хэш коммита:1.4.2-a81c9f. - Продакшен скачивает образ из реестра, а не собирается сам. Собирает конвейер, разворачивает продакшен. Иначе на живой машине появляются компиляторы, ключи доступа и случайные изменения.
- Один и тот же образ, а не один и тот же рецепт. Настоящая гарантия «как на тесте, так и в проде» — идентичные байты образа, а не идентичный Dockerfile. Прогон тестов и раскатка указывают на одну метку; именно эту цепочку мы описывали в CI/CD-части нагрузочного тестирования.
Часть III. Dockerfile: рецепт сборки и приём multi-stage
7. Читаем рецепт построчно
Пример Dockerfile:
FROM php:8.3-fpm
RUN apt-get update && apt-get install -y \
libzip-dev \
libpng-dev \
&& docker-php-ext-install pdo_mysql zip gd opcache
WORKDIR /var/www/html
COPY . /var/www/html
RUN chown -R www-data:www-data /var/www/html
EXPOSE 9000
CMD ["php-fpm", "-F"]
Что происходит:
FROM— базовый образ, от которого отталкиваемся: официальный образ PHP-FPM уже содержит интерпретатор и мастер-процесс.RUN— команда, выполняемая во время сборки; её результат закрепляется в новом слое. Здесь ставят системные библиотеки и включают расширения.WORKDIR— рабочая папка внутри образа, от неё отсчитываются последующие пути.COPY— положить файлы проекта в образ.EXPOSE— подсказка о порте, который процесс будет слушать; сама по себе строка порт не открывает.CMD— что запускать при старте контейнера.
Обратите внимание на && внутри одного RUN: установки склеены в одну команду, чтобы получился один слой вместо трёх. Итоговый образ меньше, а кэш сборки предсказуемее.
8. Multi-stage: несколько стадий сборки в одном файле
Multi-stage — приём, когда в одном Dockerfile несколько секций FROM, то есть несколько независимых стадий, а финальная забирает у предыдущих только нужное. По-русски: многостадийная сборка, несколько стадий сборки в одном файле.
Зачем. Для сборки PHP-проекта нужен менеджер зависимостей, часто компиляторы, иногда Node для фронтенда. Во время работы приложения всё это не нужно: оно только раздувает образ и добавляет лишние уязвимые места. Multi-stage позволяет собрать в одной стадии, а жить в другой — чистой и лёгкой.
Пример Dockerfile с двумя стадиями:
# ---------- Стадия 1: сборка ----------
FROM composer:2 AS builder
WORKDIR /build
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-autoloader --prefer-dist
COPY . .
RUN composer dump-autoload --optimize
# ---------- Стадия 2: то, что поедет на сервер ----------
FROM php:8.3-fpm AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends libzip-dev \
&& docker-php-ext-install pdo_mysql zip opcache \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /var/www/html
# забираем из стадии builder только готовый код с зависимостями
COPY --from=builder /build /var/www/html
RUN chown -R www-data:www-data /var/www/html
EXPOSE 9000
CMD ["php-fpm", "-F"]
Ключевые места: AS builder даёт стадии имя, а COPY --from=builder копирует из неё в финальный образ. Итог — образ без менеджера зависимостей, без компиляторов, с чистой установкой расширений. Размер уменьшается в разы, а список того, что внутри, становится понятным.
Для фронтенда схема та же: первая стадия на образе Node собирает статику, вторая — лёгкий веб-сервер, куда копируются только готовые файлы. Node в финальном образе не остаётся.
9. Что класть в образ нельзя
- Секреты. Пароли к базе, ключи, приватные ключи — не через
COPYи не аргументами сборки: они остаются в слоях навсегда и видны любому, кто скачает образ. Источник — переменные окружения из внешнего хранилища либо файл, смонтированный только на время работы. - Тестовые данные и дампы. Локальная копия базы на восемь гигабайт делает невозможными и раскатку, и проверку под нагрузкой.
- Инструменты сборки и отладки в финальной стадии. Как раз для них и нужен multi-stage.
Часть IV. docker-compose: весь проект одним файлом
10. Что такое compose и когда он уместен
docker-compose — инструмент, который описывает группу контейнеров в одном YAML-файле и запускает их вместе одной командой. Вместо десяти ручных шагов и пятидесяти флагов — декларация: вот приложение, вот веб-сервер, вот база, вот Redis, вот общий диск и сеть между ними.
Это ровно тот случай, который обещала шестая часть статьи про монолит: начать с контейнеров, не разводя микросервисы. Один монолитный WordPress или одно PHP-приложение в compose — обычное дело, и это уже огромный шаг вперёд против настройки сервера руками.
Пример docker-compose.yml (приложение, веб-сервер, база и кэш):
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
app:
build: .
volumes:
- ./:/var/www/html
environment:
DB_HOST: db
DB_NAME: shop
REDIS_HOST: cache
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: mysql:8.4
environment:
MYSQL_DATABASE: shop
MYSQL_USER: shop
MYSQL_PASSWORD: devpass
MYSQL_ROOT_PASSWORD: devroot
volumes:
- dbdata:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
command: ["redis-server", "--maxmemory", "256mb", "--maxmemory-policy", "allkeys-lru"]
volumes:
dbdata:Что здесь важно понять:
build: .— приложение собирается из Dockerfile в текущей папке; база и кэш берутся готовыми из реестра.ports: "8080:80"— снаружи порт 8080, внутри контейнера 80. У базы и кэша портов наружу нет: они видны только внутри проекта, а не всей сети.depends_onс условиемservice_healthy— не просто «запустить раньше», а дождаться, пока проверка живости базы ответит успешно. Иначе приложение стартует в момент, когда база ещё инициализирует таблицы. Тот же механизм подробного разобран в части про проверки живости.dbdata— именованный том: данные базы живут вне контейнера, удаление контейнера их не трогает.allkeys-lruв команде Redis — политика вытеснения из части про алгоритмы очистки.
11. Имя сервиса — это адрес: сеть внутри проекта
Compose создаёт внутреннюю сеть, и каждый сервис доступен в ней по своему имени как по домену: приложение пишет DB_HOST: db и REDIS_HOST: cache, никаких адресов. Один и тот же конфиг работает и на ноутбуке, и на сервере, потому что внутри проекта имена стабильны.
Отсюда один из самых частых «непонятных» багов при переезде в контейнер: в конфиге остался хост localhost. Внутри контейнера localhost — это сам контейнер, а не база на машине-хосте.
12. Команды ежедневного обихода
bash-пример:
# один раз: собрать образ приложения по Dockerfile
docker build -t shop-php:1.4.2-a81c9f .
# поднять весь проект в фоне, пересобрав образ при необходимости
docker compose up -d --build
# что запущено и в каком состоянии
docker compose ps
# логи приложения, следуя за новыми строками
docker compose logs -f app
# залезть внутрь работающего контейнера
docker compose exec app sh
# выполнить команду в базе: импорт дампа
docker compose exec -T db mysql -ushop -pdevpass shop < dump.sql
# остановить проект, сохранив данные
docker compose down
# остановить проект ВМЕСТЕ с томами данных — быть внимательным
docker compose down -v
Полезно помнить: down -v удаляет тома, то есть стирает базу. В скриптах сборки это штатный шаг «чистое окружение», на живой машине — потеря данных.
Часть V. Что делает оркестратор в проде
13. Пробел, который оркестратор закрывает
Compose хорош, когда всё живёт на одной машине. Но когда у вас пять серверов и сорок контейнеров, появляются вопросы, на которые YAML-файл не отвечает:
- на какой машине что запускать;
- что делать, если контейнер умер, а машин, где его можно поднять, три;
- как выкатить новую версию, не останавливая обслуживание;
- как добавить мощности, когда нагрузка выросла втрое;
- как сервисам находить друг друга, если адреса меняются от запуска к запуску.
Оркестратор — система, которая получает описание желаемого состояния («хочу три копии сервиса каталог версии 1.4.2») и сама добивается его: выбирает узлы, запускает контейнеры, следит за здоровьем, заменяет мёртвые, перекладывает при нехватке места. Kubernetes — самый распространённый такой механизм. Он не выполняет ваш код, он управляет запуском.
Три вещи, которые он делает вместо вас и ради которых его обычно и заводят: само-восстановление, раскатка без простоя, масштабирование.
14. Само-восстановление: упавший сервис поднимается сам
Оркестратор постоянно опрашивает контейнеры. Если процесс завершился с ошибкой, завис или перестал отвечать на проверке — система запускает новый вместо него, при необходимости на другом узле. Человек не участвует.
Это прямое продолжение части про проверки живости: там живой узел определялся по эндпоинту готовности, и мёртвый исключался из пула. В оркестрированной системе тот же эндпоинт работает дважды: балансировка не шлёт запросы на нездоровую копию, а оркестратор эту копию заменяет новой. Отсюда требование к вашему коду — честная точка готовности: эндпоинт отвечает «ок», только когда сервис реально может работать (подключение к базе есть, миграции применены, кэш доступен).
15. Раскатка без простоя
Обновление без остановки называется раскаткой (rolling update): оркестратор запускает копии новой версии, проверяет их готовность, переводит трафик и только после этого выключает старые копии. При трёх репликах и правиле «по одной копии» в любой момент обслуживают минимум две.
Здесь и нужна уникальная метка образа из Части II. Если раскатывать latest, система не увидит разницы между «что было» и «что надо» и ничего не заменит; откат превратится в гадание. Уникальная метка делает раскатку обычным делом, а откат — возвратом к предыдущей метке. Та же механика даёт канареечный выпуск: несколько минут часть трафика идёт на новую версию. После этого раскатка встраивается в конвейер сборки и в проверки под нагрузкой, о которых речь шла в статье №10.
16. Реплики под нагрузкой и автоматическое масштабирование
Реплика — это просто ещё одна копия того же сервиса. Три реплики — три одинаковых контейнера, обслуживающих один и тот же набор запросов. Условие: состояние вынесено вовне, в базу и кэш (см. часть про липкие сессии), иначе копии перестают быть взаимозаменяемыми.
Оркестратор умеет менять число реплик сам: следит за потреблением процессора и памяти (или за длиной очереди) и добавляет копии, когда текущим тяжело, убирает, когда нагрузка спала. Это автоматическое масштабирование — «автоматический регулятор числа копий по горизонтали».
Связка всей серии получается такой: нагрузочное тестирование измеряет, сколько выдерживает одна копия; автоматическое масштабирование превращает это число в правило; кеширование и оптимизация запросов снижают саму потребность в копиях; CDN снимает часть запросов до того, как они дойдут до оркестратора. И наоборот: если масштабировать только приложение, а база остаётся одна, вы упрётесь в неё — об этом прямо говорилось в статье про оптимизацию баз данных.
Часть VI. Kubernetes в минимуме: реплики и проверка живости
17. Развёртывание: желаемое состояние одним файлом
Основная сущность в Kubernetes — развёртывание (deployment): сколько копий нужно, каким образом, какой образ и как проверять здоровье. Минимальный пример.
Пример Kubernetes Deployment (YAML):
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalog
spec:
replicas: 3 # держим три копии сервиса
selector:
matchLabels:
app: catalog
strategy:
type: RollingUpdate # раскатка без простоя
rollingUpdate:
maxUnavailable: 0 # ни одной рабочей копии не убираем заранее
maxSurge: 1 # сверху добавляем максимум одну
template: # этот шаблон и есть «что запускать»
metadata:
labels:
app: catalog
spec:
containers:
- name: catalog
image: registry.example.com/shop/catalog:1.4.2-a81c9f
ports:
- containerPort: 9000
readinessProbe: # готов ли принимать трафик
httpGet:
path: /healthz
port: 9000
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe: # жив ли процесс: иначе перезапустить
httpGet:
path: /healthz
port: 9000
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 3
resources:
requests:
cpu: 100m # заявленная потребность
memory: 128Mi
limits:
memory: 256Mi # жёсткий потолок по памятиСлово replicas переводится как копии: параметр задаёт, сколько одинаковых контейнеров сервиса должно жить одновременно. Именно это число оркестратор меняет под нагрузкой.
18. Проверка живости и проверка готовности: две пробы, два разных решения
Эти два поля путают чаще всего, а различие принципиальное.
Liveness probe (буквально «проба живости», по-русски — проверка живости) отвечает на вопрос: «процесс ещё вообще имеет смысл?» Если ответы не приходят нужное число раз подряд, оркестратор убивает контейнер и запускает новый. Это лечит зависание: процесс жив, но работать не может — например, умер пул соединений и все потоки сидят в ожидании.
Readiness probe (проверка готовности) отвечает на другой вопрос: «можно ли ему сейчас слать запросы?» Неготовую копию не убивают — её исключают из раздачи трафика и возвращают, когда она снова ответит успешно. Это нужно при долгом старте, плановом обслуживании, отключении зависимой очереди.
Вывод для PHP-приложения: обе проверки могут смотреть на один и тот же эндпоинт, но пороги разные. Проверка живости должна быть с запасом и без обращения к внешним системам — иначе временная проблема базы перезапустит все копии подряд и получится каскадный отказ. Проверка готовности как раз обязана зависимости видеть. Про то, как писать такие эндпоинты и снимать с них метрики, — статья про метрики и мониторинг и статья про логирование и трассировку.
19. Команды ежедневного обихода
bash-пример:
# применить описание
kubectl apply -f deployment-catalog.yaml
# посмотреть, сколько копий реально живёт
kubectl get pods -l app=catalog
# раскатка новой версии образа
kubectl set image deployment/catalog \
catalog=registry.example.com/shop/catalog:1.4.3-b21f07
# следить, как идёт раскатка
kubectl rollout status deployment/catalog
# откат на предыдущую версию
kubectl rollout undo deployment/catalog
# вручную задать число копий, пока автоматическое масштабирование выключено
kubectl scale deployment/catalog --replicas=5
# заглянуть в логи конкретной копии
kubectl logs -l app=catalog --tail=200Дальше к этому добавляются сервис (внутренняя балансировка между копиями — то, что алгоритмы из части про балансировку делали руками Nginx и HAProxy), точка входа снаружи, конфигурационные карты и секреты. Но минимальный рабочий круг — это ровно команды выше.
Часть VII. Честно про цену: когда оркестратор избыточен
20. Что стоит дорого на самом деле
Оркестратор — не бесплатная функция, а отдельная система со своей сложностью. Цена складывается из пяти позиций:
- Минимум узлов. Нормальный Kubernetes — три и больше узла: два рабочих и один управляющий. Один VPS — не конфигурация для оркестратора.
- Компетенции. Нужен человек, который понимает сети, хранение, обновления самой платформы и не боется читать логи планировщика. Без него кластер — источник сюрпризов, а не надёжности.
- Сетевой слой. Внутри всё решается именами сервисов; снаружи нужны вход, сертификаты, правила распределения и разбор того, почему запрос не доходит.
- Наблюдаемость. Без метрик, логов и трассировки кластер с сорока копиями — чёрный ящик. Обязательные соседи — Метрики и мониторинг и Логирование и трассировка.
- Хранение и базы. Состояние остаётся там же, где было. Kubernetes не делает MySQL распределённым; он только перезапускает контейнер с ним.
21. Таблица выбора: что взять под свою ситуацию
| Ситуация | Контейнеры | compose | Оркестратор |
|---|---|---|---|
| Один сайт WordPress на одном VPS | полезен | не нужен | не нужен |
| Монолит + база + Redis + очередь на одном сервере | да | оптимально | не нужен |
| Два сервера, хочется переживать падение одного | да | плюс внешняя балансировка | уже можно посмотреть |
| Пять–десять сервисов, команда с инженером по эксплуатации | да | только для локальной разработки | оправдан |
| Нужны раскатки без простоя и автоматические копии по метрике нагрузки | да | не умеет | оправдан |
| Регулярные пики трафика в разы выше среднего уровня | да | не справится | оправдан при готовой базе и кэше |
Вывод, который стоит повторить: контейнеры почти всегда, оркестратор — только по размеру задачи. Для одного VPS с WordPress полноценный Kubernetes — избыточное усложнение: вы получите тридцать строк YAML вместо одной настройки веб-сервера, а выигрыша не увидите, потому что масштабировать нечего: узел один.
22. Что взять вместо оркестратора, если серверов немного
Порядок шагов для малого проекта:
- Один Dockerfile с multi-stage для приложения и один compose-файл с приложением, веб-сервером, базой, Redis и очередью.
- Именованные тома для данных базы и вынос состояния из процесса — тогда копии действительно взаимозаменяемы.
- Сборка образа вне сервера, раскатка по метке; откат — смена метки обратно.
- Внешний балансировщик или два узла с плавающим адресом — см. часть про единую точку отказа.
- Проверка живости по эндпоинту и автоматический перезапуск на уровне веб-сервера или системного менеджера службы — вместо оркестратора.
- Тест под нагрузкой до выбора архитектуры, а не после: статья №10 описывает, как измерить ёмкость одной копии.
Когда эти пункты перестают решать задачу — сервисов много, узла несколько, простой дорог, — появляется Kubernetes. Компромисс посередине — управляемый сервис запуска контейнеров: облако само держит заданное число копий, а вы эту платформу не администрируете.
23. Типичные ошибки при заходе в контейнеры
- Данные внутри контейнера. Слой контейнера удаляется вместе с ним; база, лежащая не на томе, теряется при каждом остановочном шаге.
- Состояние в памяти процесса. Сессии держатся в копии — и копию нельзя заменить; решение — наружу, в Redis (часть про Redis).
- Образ-комбайн. Один Dockerfile на разработку и прод: в проде живёт компилятор и тестовые дампы.
latestкак метод выпуска. Невозможно понять, что запущено, невозможно откатить, невозможно воспроизвести проблему.- Нет ограничения по памяти. Контейнер съедает всю машину и утаскивает соседей; заявленная потребность и жёсткий потолок обязательны.
- Проверка живости обращается к базе. Временный сбой базы перезапускает все копии, а после восстановления они стартуют одновременно — холодный старт всего сервиса разом.
- Секреты в слоях. Самое дорогое, повторим отдельно.
Инструмент под задачу
Собрать аккуратный Dockerfile/docker-compose под тот же путь «от монолита к оркестрации» (multi-stage, healthcheck, правильный порядок слоёв COPY ради кэша) удобнее по живому генератору. Попробуйте инструмент прямо здесь, на своих данных:
Финал серии. Путь от монолита до оркестрации
24. Одна мысль, объединяющая одиннадцать статей
Если прочитать серию одной фразой: сложность архитектуры должна соответствовать размеру задачи, а всякое усложнение окупается только тогда, когда появилась конкретная боль. Мы начали с монолита и с честного ответа «не надо делить рано» — и закончили оркестратором с таким же честным ответом «не надо его заводить, когда узел один».
25. Что за чем идёт, если складывать в порядок внедрения
| Шаг | Статья | Что приобретаете |
|---|---|---|
| 1. Определиться с формой приложения | Монолит против микросервисов | ответ: остаётесь вместе или делите по домену |
| 2. Оформить договор между частями | Проектирование API | предсказуемые контракты и версии |
| 3. Вынести тяжёлое и долгое | Очереди сообщений | фоновые задачи не блокируют ответ |
| 4. Перестать считать одно и то же | Кеширование | удар по задержке без переписывания кода |
| 5. Убрать единую точку отказа | Балансировка нагрузки | рост по горизонтали и живучесть |
| 6. Перестать гадать | Метрики и мониторинг | числа о системе и пороги оповещений |
| 7. Научиться видеть прошлое | Логирование и трассировка | путь запроса через все сервисы |
| 8. Выжать из базы максимум | Оптимизация баз данных | индексы, реплики, пул соединений |
| 9. Приблизить ответ к пользователю | CDN и edge-вычисления | кэш у края сети и защита от пиков |
| 10. Узнать реальную ёмкость | Нагрузочное тестирование | цифры вместо ощущений |
| 11. Сделать запуск воспроизводимым | эта статья | один образ, один шаг раскатки, дежурный над процессами |
Порядок не случаен: без метрик (шаг 6) не понять эффект кеширования (шаг 4), без измеренной ёмкости (шаг 10) честно не настроишь число копий (шаг 11), а возможности шага 11 делают практичным шаг 1 — быструю раскатку и откат того самого решения о делении.
26. Практический чек-лист серии
- Приложение описано Dockerfile с multi-stage; в финальном образе нет инструментов сборки и секретов.
- Один compose-файл поднимает проект на машине разработчика с нуля, без ручной настройки.
- Состояние процесса вынесено в базу и кэш; копии взаимозаменяемы.
- Данные сервиса лежат на внешних томах, а не в слое контейнера.
- Есть эндпоинты проверки готовности и проверки живости, и пороги у них разные.
- Метка образа уникальна для сборки; откат — это выбор предыдущей метки, а не поиск в истории.
- Тот же образ, что прошёл тесты, раскатывается в прод.
- Известна ёмкость одной копии по результатам теста под нагрузкой.
- Решение об оркестраторе принято осознанно: сервисов много, узлов несколько, простой имеет цену.
- Наблюдаемость настроена до того, как сервисов стало двадцать.
Материал написан простым языком. Примеры рассчитаны на связку Docker, docker-compose, Kubernetes и PHP/WordPress на VPS. Синтаксис инструментов меняется: конкретные поля и флаги стоит сверять с официальной документацией используемых версий. Термины расшифрованы при первом упоминании: image — образ, container — запущенный контейнер, orchestrator — оркестратор, replica — копия сервиса, liveness probe — проверка живости, readiness probe — проверка готовности, multi-stage — многостадийная сборка.
👤 Зарегистрируйтесь, чтобы добавить материал в закладки.
