Production checklist
Список требований перед выводом сервиса в эксплуатацию. Пять групп: сборка, запуск, наблюдаемость, отказоустойчивость, доставка.
Пункт засчитывается, когда выполнена проверка, а не когда «в целом сделано». Каждый пункт содержит команду.
Как пользоваться
Пройти целиком до первой выкатки, затем — при существенных изменениях. Полное прохождение занимает около часа; невыполненный пункт дешевле найти здесь, чем ночью.
1. Сборка
| ☐ | Требование | Проверка |
|---|---|---|
| ☐ | Собирается из чистого клона | git clone . /tmp/пр && cd /tmp/пр && docker build . |
| ☐ | Базовый образ закреплён | grep '^FROM' Dockerfile — тег или digest |
| ☐ | Зависимости зафиксированы | Файл блокировки в репозитории |
| ☐ | Multi-stage: инструментов сборки нет | docker run --rm ОБРАЗ which gcc |
| ☐ | Кэш работает | touch src/*.py && time docker build . — секунды |
| ☐ | .dockerignore полон | .git, кэши, окружения, данные |
| ☐ | Размер соответствует ожиданию | docker images ОБРАЗ --format '{{.Size}}' — на диске; docker image inspect … | numfmt --to=si — сколько качается |
| ☐ | Тесты выполняются в образе | docker build --target test . |
| ☐ | Провал теста останавливает сборку | Сломать тест намеренно |
| ☐ | Метки прослеживаемости заданы | docker image inspect ОБРАЗ --format '{{json .Config.Labels}}' |
Первый пункт — самый частый провал. Работа собирается у автора и не собирается больше нигде: файл вне репозитория, переменная в личном окружении, образ в локальном кэше.
2. Запуск
| ☐ | Требование | Проверка |
|---|---|---|
| ☐ | Пользователь не root, числовой | docker image inspect ОБРАЗ --format '{{.Config.User}}' |
| ☐ | Точка входа в exec-форме | docker image inspect ОБРАЗ --format '{{json .Config.Entrypoint}}' |
| ☐ | PID 1 — само приложение | docker exec ИМЯ ps -eo pid,cmd | head -2 |
| ☐ | Слушает 0.0.0.0, не петлю | см. networking cheat sheet |
| ☐ | Работает при --read-only | docker run --rm --read-only --tmpfs /tmp ОБРАЗ |
| ☐ | Capabilities сняты | --cap-drop ALL плюс явные при необходимости |
| ☐ | Ограничения памяти и CPU заданы | docker inspect ИМЯ --format '{{.HostConfig.Memory}}' |
| ☐ | Лимиты применились | docker exec ИМЯ cat /sys/fs/cgroup/memory.max |
| ☐ | Лимиты обоснованы измерением | Число в README со ссылкой на замер |
| ☐ | Конфигурация из окружения | Один образ для всех сред |
| ☐ | Неверная конфигурация останавливает старт | docker run -e ПАРАМЕТР=мусор ОБРАЗ — ненулевой код |
| ☐ | Секреты передаются файлом | _FILE-переменные, не значения |
Восьмой пункт отличается от седьмого. docker inspect показывает запрошенное; применилось ли оно, видно только изнутри (урок 17.4).
Одиннадцатый пункт. Сервис, стартовавший с испорченной настройкой, выглядит здоровым и отвечает ошибками — худший из исходов.
3. Наблюдаемость
| ☐ | Требование | Проверка |
|---|---|---|
| ☐ | Логи в stdout и stderr | docker logs ИМЯ не пуст |
| ☐ | Логи структурированы | docker logs ИМЯ | head -5 | python3 -m json.tool |
| ☐ | Все строки разбираются | Включая строки сервера приложений |
| ☐ | Буферизация отключена | PYTHONUNBUFFERED=1 |
| ☐ | Ротация логов настроена | docker inspect ИМЯ --format '{{json .HostConfig.LogConfig}}' |
| ☐ | Идентификатор запроса сквозной | Один запрос — один идентификатор во всех записях |
| ☐ | Уровень журналирования настраивается | Переменная окружения |
| ☐ | Секретов в логах нет | docker logs ИМЯ | grep -iE 'password|token' |
| ☐ | Метрики доступны | Эндпоинт или экспортёр |
| ☐ | Показания cgroup читаются изнутри | docker exec ИМЯ cat /sys/fs/cgroup/memory.events |
Третий пункт проверяется не первыми строками, а всеми. Частая ошибка — настроить формат в lifespan: сервер приложений успевает напечатать первые строки своим форматом, и поток получается смешанным.
docker logs ИМЯ 2>&1 | python3 -c '
import json, sys
bad = 0
lines = [line for line in sys.stdin if line.strip()]
for i, line in enumerate(lines, 1):
try:
json.loads(line)
except Exception:
bad += 1
print(f" строка {i} не JSON: {line[:60]}")
print(f"строк: {len(lines)}, не JSON: {bad}")'
4. Отказоустойчивость
| ☐ | Требование | Проверка |
|---|---|---|
| ☐ | SIGTERM завершает за секунды | docker run -d --name t ОБРАЗ && time docker stop t |
| ☐ | Активные запросы дорабатываются | Долгий запрос плюс docker stop |
| ☐ | stop_grace_period достаточен | Больше самой долгой операции |
| ☐ | Healthcheck есть | docker inspect ИМЯ --format '{{json .State.Health}}' |
| ☐ | Liveness и readiness различаются | Остановить зависимость: одна 200, другая 503 |
| ☐ | start_period больше времени старта | Иначе проверки убьют инициализацию |
| ☐ | Приложение переживает отказ зависимости | docker compose restart db, запрос после |
| ☐ | Приложение переживает перезапуск | Состояние не в container'е |
| ☐ | Политика перезапуска задана | restart: unless-stopped |
| ☐ | Данные в томе, а не в слое | docker diff ИМЯ — ничего важного |
| ☐ | Данные переживают down/up | Записать, down, up, прочитать |
| ☐ | Резервное копирование настроено | Процедура существует |
| ☐ | Восстановление проверено | Восстановить в отдельный том |
Последний пункт. Резервная копия, восстановление которой не проверялось, копией не является. Это единственный пункт списка, который обычно откладывают «на потом» и не выполняют никогда.
Пятый пункт проверяется парой:
docker compose stop db
curl -s -o /dev/null -w 'healthz: %{http_code}\n' localhost:8000/healthz # ожидается 200
curl -s -o /dev/null -w 'readyz: %{http_code}\n' localhost:8000/readyz # ожидается 503
docker compose start db
Если обе дают одинаковый код — разделения нет, сколько бы эндпоинтов ни было объявлено.
5. Доставка
| ☐ | Требование | Проверка |
|---|---|---|
| ☐ | Тег неизменяемый | По SHA коммита, не latest и не prod |
| ☐ | Грязное дерево помечается | git status --porcelain в вычислении тега |
| ☐ | Развёртывание по digest | В конфигурации @sha256: |
| ☐ | Предыдущий digest записан | Файл или переменная окружения |
| ☐ | Откат — выполнимая команда | Не «развернуть предыдущую версию» |
| ☐ | Образ просканирован | trivy image --severity HIGH,CRITICAL |
| ☐ | Отсутствие сканера отличимо от чистого | Три кода возврата |
| ☐ | SBOM приложен | syft ОБРАЗ -o spdx-json |
| ☐ | Публикация только при пройденном конвейере | Код 0, а не «не провалено» |
| ☐ | Порядок этапов обоснован | Быстрые проверки первыми |
Про откат. План отката — это команда, которую можно выполнить не раздумывая:
ci/rollback.sh production
Всё остальное — пожелание. Проверить план можно единственным способом: выполнить его на работающей системе до того, как он понадобится.
Итоговая таблица
| Группа | Пунктов | Отметка |
|---|---|---|
| 1. Сборка | 10 | ☐ |
| 2. Запуск | 12 | ☐ |
| 3. Наблюдаемость | 10 | ☐ |
| 4. Отказоустойчивость | 13 | ☐ |
| 5. Доставка | 10 | ☐ |
| Всего | 55 |
Пять пунктов, которые пропускают чаще прочих
Если времени на весь список нет — начните с этих.
| Пункт | Почему пропускают | Что произойдёт |
|---|---|---|
| Сборка из чистого клона | «У меня же собирается» | Не соберётся ни у кого другого |
| Проверка восстановления копии | Долго и скучно | Выяснится в худший момент |
| Различие liveness и readiness | Кажется дублированием | Отказ базы перезапустит все экземпляры |
| Лимиты, применившиеся на самом деле | inspect показал число | Запрос мог не выполниться |
| Все строки логов как JSON | Первые строки разобрались | Сборщик потеряет половину |
Что этот список не проверяет
| Чего нет | Почему |
|---|---|
| Нагрузочное тестирование | Требует стенда |
| Готовность команды к дежурству | Организационное |
| Уместность самой контейнеризации | Отдельный вопрос (урок 19.3) |
| Архитектура приложения | Контейнеризация её не исправляет |
Последняя строка стоит того, чтобы её заметить: пятьдесят пять галочек не делают правильной систему, спроектированную неверно.
Навигация
Вернуться к справочникам
Security checklist
Python container checklist
Grading rubric
Раздел 11. Production
Главное оглавление