Деплой приложений в Docker — это стандарт индустрии, но это не значит, что он прост. Многие разработчики интегрируют Docker в CI/CD, но забывают о специфике production-окружения. Ошибки на этапе сборки или конфигурации редко видны сразу, но проявляются в самые неподходящие моменты.
Ошибка 1: Запуск контейнеров от root
Самая очевидная, но часто игнорируемая ошибка. Запуск контейнера с UID 0 (root) открывает путь к катастрофе. Если в вашем образе будет найдена уязвимость (например, CVE-2021-3124 в Glibc), злоумышленник получит неограниченные права на хост-машине.
Решение: Всегда создавайте отдельного пользователя внутри контейнера и переключайтесь на него. Это стандарт безопасности, прописанный в лучших практиках Docker.
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
RUN useradd -m appuser
USER appuser
COPY . .
CMD ["python", "app.py"]
Ошибка 2: Образы без multi-stage build
Использование одного слоя для сборки и запуска приводит к огромному размеру образов. Больший образ = больше поверхности атаки, больше времени на скачивание и загрузку (pull/push) в реестр.
Решение: Используйте multi-stage build. Сборщик (builder) может удалять промежуточные артефакты, а в итоговом образе останется только исполняемый бинарник и минимальные зависимости.
# Dockerfile (Multi-stage)
FROM golang:1.21 AS builder
WORKDIR /src
COPY . .
RUN go build -o myapp
FROM alpine:latest
COPY --from=builder /src/myapp /usr/local/bin/myapp
CMD ["myapp"]
Ошибка 3: Нет health check — нет понимания состояния
Docker считает контейнер «здоровым», только если он запущен. Приложение может падать, потреблять 100% CPU или зависать в цикле обработки, но контейнер продолжит гореть зелёным в `docker ps`.
Решение: Определите HEALTHCHECK в Dockerfile. Это команда, которая выполняется внутри контейнера с заданной периодичностью. Если она возвращает ошибку, Docker перестаёт отвечать на запросы и перезапускает контейнер (если настроено).
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
Ошибки с логированием и ротацией
По умолчанию Docker использует драйвер json-file. Это удобный формат, но он имеет суровое ограничение: размер файла ограничен конфигурацией демона (по умолчанию 10МБ). Без ротации контейнер заполнит диск, ОС заблокирует запись, и приложение упадёт с ошибкой No space left on device.
Решение: Используйте внешние драйверы, такие как syslog, journald или loki. Или, если используете json-file, обязательно монтируйте том с логами и настраивайте ротацию через logrotate или параметры Docker (log-opt max-size).
Правильная стратегия restart policy
Часто можно встретить restart: always. Это работает, но заставляет Docker бесконечно перезапускать контейнер, даже если приложение не может запуститься из-за ошибок конфигурации (например, отсутствует переменная окружения).
Решение: Используйте restart: unless-stopped. Это наиболее безопасная стратегия для production. Контейнер перезапустится, если он упал, но не будет пытаться стартовать, если он уже остановлен вручную.
Контроль версий
Никогда не используйте тег latest в production-конфигурациях. Это нарушает принцип воспроизводимости. Всегда указывайте фикс SHA или семантический номер версии (v1.2.3).
Secrets Management
Не храните пароли и API ключи в переменных окружения или в Dockerfile. Используйте docker secrets или внешние системы типа HashiCorp Vault.
Чеклист для production-деплоя
Перед запуском убедитесь, что вы проверили всё следующее:
- ✓ Собран образ с использованием multi-stage build.
-
✓
В Dockerfile прописан
USERдля непривилегированного пользователя. -
✓
Добавлен
HEALTHCHECKдля критических сервисов. - ✓ Настроен логгер (syslog/loki) и ротация файлов.
-
✓
Используется
restart: unless-stopped. - ✓ Секреты извлечены из переменных окружения и не закэшированы в образе.