Прямой ответ
ConfigMap (открытые настройки) и Secret (чувствительные) — K8s-объекты конфигурации, отделяющие конфиг от образа (12-factor в кластерном воплощении — один образ на все окружения, различия — в объектах); в под они попадают двумя путями: env (envFrom: configMapRef/secretRef — все ключи как переменные, или выборочно valueFrom.configMapKeyRef — точечно) и файлы (volume-монтирование — ключ = имя файла — конфиг-файлы приложений, nginx.conf, settings.yaml). Выбор: переменные — для плоских настроек (их читает pydantic-settings из env — единая контрактная точка), файлы — для структурных конфигов и инструментов, живущих файлами; критичное ограничение env-пути: переменные не обновляются в живом поде (изменил ConfigMap — под видит старое до пересоздания — hot-reload для env не существует физически); файловый путь обновляется kubelet'ом (~минута задержки) — но приложение должно перечитывать файл само (watch/SIGHUP — немногие так умеют из коробки); практический стандарт — «изменил конфиг → перезапустил деплоймент» (kubectl rollout restart — последовательное пересоздание без простоя — стратегии); «ConfigMap обновил, а приложение ведёт себя по-старому» — вопрос №1 поддержки конфигов, ответ — пересоздание. Secrets: base64 — кодирование, не шифрование (дефолт в etcd — почти открытое хранение; защита: etcd-encryption at rest (managed-кластеры обычно), RBAC (get secrets — только кому нужно — «разработчик с kubectl get secrets -o yaml» — дамп всех паролей — RBAC-ревизия обязательна), внешние хранилища для серьёзного (External Secrets Operator → Vault/SOPS — Vault-практика); в git — никогда значений (Helm-values с секретами в репозитории — классическая утечка — секрет-дисциплина); секрет в env против файла — env виден в kubectl describe pod/краш-дампах/дочерних процессах — файлы-монтирования предпочтительнее для чувствительного. И порядок приоритета: env > файлы > дефолты кода — та же слоёная модель, только слои — кластерные объекты.
ConfigMap: создание, использование, ограничения
Создание: kubectl create configmap app-config --from-env-file=.env.prod / --from-file=settings.yaml / манифестом (data: ключ: значение); в Helm — шаблон из values (configmap.yaml + хеш-аннотация checksum/config в pod template — изменение конфига меняет хеш → автоматический rollout при helm upgrade — «забыл перезапустить после смены конфига» закрывается механизмом — чарты). Потребление: envFrom (весь ConfigMap в env — удобно, но «мусорные» ключи летят в окружение — умеренность), valueFrom (выборочно — явно и читаемо), volume (файлы; subPath — один файл в существующую директорию — осторожно: subPath-монтирования не обновляются при изменении ConfigMap — hot-update ломается именно на subPath — тонкость, на которой горят); ограничения: размер 1 МБ на объект (больше — не конфиг, а данные — хранилище/образ), отсутствие типизации (всё строки — валидация на приложении — pydantic-settings ловит — валидация на старте), необязательность по умолчанию (под стартует без отсутствующего ConfigMap, если не помечен optional: false — «конфиг потерялся, приложение на дефолтах» — required-семантика через обязательные keyRef + валидацию); версионирование: ConfigMap иммутабелен по содержанию практики (имя app-config-v2 / хеш-суффикс kustomize — откат конфигурации = откат объекта — версии применимы и к конфигам).
Secret: хранение, доступ, утечки
Механика: тот же ConfigMap структурно (type: Opaque, data — base64), потребление то же (env/файлы); различие — в отношении: RBAC — get/list secrets — отдельные роли (не «все разработчики — кластер-админы» — namespace-роли без secrets; аудит — кто читал — audit-log кластера), encryption at rest — etcd-шифрование (проверить включённость в своём кластере — managed обычно да), типизация секретов (kubernetes.io/tls — сертификаты, docker-registry — pull-секреты — специализированные типы удобнее), External Secrets Operator — секреты живут во Vault/Cloud-хранилище, в кластер синхронизируются (ротация без kubectl apply, доступ через внешнюю политику — зрелый контур — Vault), SOPS+Sealed Secrets — зашифрованные секреты в git (ключи дешифровки в кластере/у оператора — «секреты в репо» легализуется шифрованием — паттерн gitops-команд). Ротация: смена значения секрета → пересоздание подов (env-кеширование — см. выше) → приложение подхватывает; плановая ротация (90 дней) — процесс, а не событие (секрет-гигиена); утечка-инцидент: скомпрометированный секрет — немедленная ротация + отзыв (старый ключ мёртв — не «удалили из git» — из истории и дампов он не исчезает — репозиторий-гигиена — сканеры секретов в CI (gitleaks/trufflehog — CI-защита) ловат коммиты до пуша). Антипаттерны: секреты в Dockerfile ENV/образах (слой навсегда — docker-безопасность), значения в Helm values.yaml в репо (шифровать SOPS или внешние ссылки), один «мега-секрет» на всё (ротация одного ключа трогает всех — гранулярность по сервисам/назначениям), логирование env на старте без маскировки (конфиг-логирование — маска обязательна — «дамп окружения в Sentry» сливал ключи не раз).
Практический сценарий: конфигурация сервиса переезжает в кластер
Исходник: FastAPI-сервис с .env-файлом на сервере (древний деплой scp-ом) переносится в K8s (деплой); конфиг — 34 переменные, из них 6 секретов. Устройство: (1) Settings-класс уже типизирован (pydantic-settings) — разделение: 28 открытых → ConfigMap (Helm-шаблон из values-dev/values-prod — окружения различаются values-файлами, не образами), 6 секретов → Secret (значения — из Vault через External Secrets — в git только ExternalSecret-манифесты без значений); (2) подача — envFrom на оба объекта (плоские настройки — env-путь естественен; файловый не понадобился — nginx-конфиг живёт в своём ConfigMap ингресса); (3) RBAC: разработчики — роль view (логи/поды/события) без secrets; деплой-пайплайн — своя service-account с secrets-доступом; аудит включён; (4) гига-грабли, на которые наступили учебно на стейдже: обновили ConfigMap (новый URL аналитики) — «не применилось» — env в живых подах не обновляется — добавлен checksum/config в pod-аннотацию (helm upgrade сам делает rollout) + правило в runbook (инцидент-дисциплина): «смена конфига = rollout restart, проверка — через новый под»; (5) проверка старта без конфига: удалили ConfigMap на стейдже — поды упали на валидации Settings с ясной ошибкой «ANALYTICS_URL: field required» (не «работают на дефолтах молча» — валидация на старте окупилась); (6) ротация отработана: смена DB-пароля в Vault → синхронизация секрета → rollout → ноль простоев (rolling по реплике — стратегия). Итог: образ один на dev/prod, конфигурация — объекты кластера, секреты — вне git и вне kubectl-доступа разработчиков, смена любой настройки — один helm upgrade.
FAQ
ConfigMap/Secret против переменных в манифесте деплоймента?
Прямо в env-манифест (value: "...") — годится для единичных несекретных значений, привязанных к деплойменту; ConfigMap — когда конфиг переиспользуется (несколько деплойментов/контейнеров читают одно), версионируется отдельно, меняется независимо от спеки пода; секреты в открытом env-манифесте — запрещены (манифесты в git/describe — утечка); правило: >3–4 связанных настроек или хоть один секрет — объекты.
Почему приложение не видит обновлённый ConfigMap?
По пути env — никогда не увидит без пересоздания пода (переменные — снимок на старте контейнера); по файловому пути — обновится за ~минуту (kubelet-синхронизация), если не subPath (subPath-монтирование заморожено навсегда); приложение с файловым конфигом должно перечитать файл (SIGHUP-обработчик/periodic reload) — большинство простых приложений не перечитывает — отсюда универсальный ответ: kubectl rollout restart deployment/... — и в Helm — checksum-аннотация для автоматики.
Достаточно ли Secret для хранения паролей? Secret — transport/API-уровень (объект кластера), не хранилище-сейф: base64 тривиально декодируется, защита — etcd-encryption + RBAC (получить секрет могут многие с правами — и это почти открыто для инсайдеров/компрометации кластера); зрелый контур: внешнее хранилище (Vault/облачные) + External Secrets (кластер получает, не владеет) + ротация + аудит доступа; для маленьких команд минимум — etcd-encryption проверен, RBAC сужен, значения не в git (SOPS при необходимости) — но «Secret = безопасно» — заблуждение, которое регулярно всплывает в отчётах об утечках.
Как организовать конфиги для dev/staging/prod?
Один Helm-чарт (шаблоны) + values-{env}.yaml (различия — значениями: реплики/ресурсы/URL/feature flags) — деплой helm upgrade -f values-prod.yaml (CI-параметризация — пайплайн); альтернативы — kustomize-overlays (тот же принцип, без шаблонизатора); антипаттерны: три копии манифестов (дрейф окружений неизбежен — монорепо-дисциплина — один источник, различия данными), «prod-отличия прямо в шаблонах if-ами» (логика окружений в коде чарта — нечитабельно — значениями, не ветками — конфиг-принцип).
Источники: документация Kubernetes (ConfigMap, Secret, inject data) — ссылки в шапке.