Стратегии конфигурации контейнеров

TL;DR: Конфигурация внедряется при запуске, не при сборке. ENV — для простых значений, Bind Mounts — для файлов конфигов, Secrets — для паролей.

Один из ключевых принципов контейнеризации — Build once, run anywhere. Образ должен быть неизменным (immutable) артефактом. Он не должен содержать жестко зашитых настроек окружения (IP адресов базы данных, паролей, URL API), так как эти параметры меняются при переходе от разработки (Dev) к тестированию (Stage) и продакшену (Prod).

Конфигурация должна внедряться в контейнер извне в момент запуска. Docker предлагает для этого несколько механизмов.

1. Environment Variables (Переменные окружения)

Самый распространенный способ конфигурации, соответствующий методологии The 12-Factor App (III. Config).

  • Как работает: Значения передаются при старте (docker run -e DB_HOST=postgres ...) и становятся доступны процессу внутри как обычные переменные среды (process.env.DB_HOST в Node.js, os.environ['DB_HOST'] в Python).
  • Плюсы: Простота, универсальность (поддерживается всеми языками и ОС).
  • Минусы:
    • Видны в docker inspect.
    • Могут случайно попасть в логи приложения при дампе окружения.
    • Не подходят для сложных структур (JSON, XML конфиги).

ENV vs ARG

Частая ошибка — путаница между ENV и ARG.

ХарактеристикаARG (Build-time)ENV (Run-time)
Когда задаетсяПри сборке (docker build)При запуске (docker run)
Область видимостиДоступна только во время выполнения RUN в DockerfileДоступна запущенному приложению
Сохранение в образеНет (если не переназначена в ENV)Да (зашивается в метаданные образа)
Пример использованияВерсия библиотеки, базовый образХост БД, режим отладки

Если вы напишете ENV PASSWORD=123 в Dockerfile, этот пароль будет навсегда зашит в образ и виден всем, у кого есть этот образ. Никогда так не делайте для секретов.

2. Файлы конфигурации (Bind Mounts / Volumes)

Для сложных приложений (Nginx, Prometheus, ELK), которые требуют больших config-файлов (nginx.conf, prometheus.yml), переменные окружения неудобны.

  • Стратегия: В образе лежит дефолтный конфиг. При запуске мы “подменяем” его своим файлом через Bind Mount.
    docker run -v ./my-nginx.conf:/etc/nginx/nginx.conf:ro nginx
  • Плюсы: Поддержка сложных форматов, разделение кода и конфига.
  • Минусы: Зависимость от файловой системы хоста.

Шаблонизация (envsubst)

Часто используется гибридный подход. В образе хранится шаблон конфига (nginx.conf.template) с плейсхолдерами ${BACKEND_HOST}. При старте контейнера скрипт (Entrypoint) заменяет переменные из ENV в шаблоне и генерирует финальный файл конфигурации.

3. Secrets и Configs (Docker Swarm / Kubernetes)

Для безопасной передачи конфиденциальных данных (паролей, API ключей, SSL сертификатов) обычные ENV переменные небезопасны.

Docker Swarm (и Kubernetes) предоставляет нативные примитивы Secrets.

  • Секрет создается в кластере, шифруется в Raft-логе и доставляется только на те узлы, где запущен сервис.
  • Внутри контейнера секрет монтируется как файловая система в оперативной памяти (tmpfs), обычно в /run/secrets/my_secret.
  • Приложение должно уметь читать пароль из файла, а не из переменной окружения. Многие официальные образы (Postgres, MySQL) поддерживают суффикс _FILE (например, POSTGRES_PASSWORD_FILE), чтобы читать пароль из секрета.

В “чистом” Docker (без Swarm) секреты эмулируются через файлы или ENV, но настоящая защита появляется только на уровне оркестратора.

Иерархия приоритетов (Best Practice)

Хорошо спроектированное приложение в Docker должно уметь собирать конфиг из нескольких источников в таком порядке (от низкого приоритета к высокому):

  1. Default Values: Вшиты в код или Dockerfile (безопасные дефолты).
  2. Config File: Файл /app/config.yaml (монтируется через Volume).
  3. Environment Variables: Перекрывают значения из файла (DB_HOST перекроет значение в yaml).
  4. CLI Arguments: Аргументы командной строки (флаги запуска).

Подводные камни

ЗаблуждениеРеальность
«ENV PASSWORD=x в Dockerfile безопасен»Пароль навсегда вшит в образ, виден через docker history
«ARG и ENV — одно и то же»ARG доступен только при сборке, ENV — при запуске
«Переменные среды надёжны для секретов»Видны в docker inspect, могут утечь в логи. Используй Docker Secrets
«NEXT_PUBLIC_ работает в runtime»NEXT_PUBLIC_* вшивается при build, не при run — это частая ошибка Next.js