ConfigMap и Secret в Kubernetes: как управлять конфигурацией
Практика4 минРедакция Innovialabs
Коротко
Обычные настройки храните в ConfigMap, пароли и токены — в Secret, передавайте их через переменные окружения или файлы и помните, что base64 — это кодирование, а не шифрование: защищайте секреты через RBAC, шифрование etcd и Sealed Secrets или External Secrets Operator.
Коротко
Kubernetes отделяет конфигурацию от образа контейнера двумя объектами:
- ConfigMap — несекретные настройки: feature-флаги, адреса других сервисов, уровень логирования, конфиг-файлы.
- Secret — чувствительные данные: пароли, API-токены, TLS-сертификаты.
Оба объекта попадают в под одним из двух способов: как переменные окружения или как файлы в смонтированном томе. Благодаря этому один и тот же образ работает в dev, staging и production с разной конфигурацией.
Главная оговорка: значения в Secret только закодированы в base64. Любой, кто может прочитать объект, расшифрует его одной командой. Реальная защита — это права доступа, шифрование при хранении и отсутствие открытых секретов в Git.
Как создать ConfigMap и Secret
Для быстрых проверок подойдут команды:
kubectl create configmap app-config --from-literal=LOG_LEVEL=info --from-file=app.yaml
kubectl create secret generic db-credentials --from-literal=DB_PASSWORD=change-me
В рабочих проектах лучше декларативные манифесты. Поле stringData в Secret позволяет писать значения как есть — Kubernetes сам закодирует их при сохранении:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
API_BASE_URL: "https://api.internal"
---
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
DB_PASSWORD: "change-me"
Такой манифест Secret нельзя коммитить в Git — об этом ниже.
Переменные окружения или файлы
containers:
- name: app
image: registry.example.com/app:1.4.0
envFrom:
- configMapRef:
name: app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config
| Переменные окружения | Файлы в томе | |
|---|---|---|
| Для чего | Простые пары ключ-значение | Целые конфиг-файлы, сертификаты |
| Обновляются при изменении объекта | Нет, только после перезапуска пода | Да, с задержкой (кроме subPath) |
| Риск | Могут утечь в логи, дампы, дочерние процессы | Доступны всему, что читает файлы в контейнере |
Практичное правило: мелкие настройки — через env, файлы и сертификаты — через тома, а секреты по возможности передавайте файлами.
Как обновлять конфигурацию
Изменение ConfigMap ничего не перезапускает. Результат зависит от способа подключения:
- Переменные окружения внутри работающего контейнера не меняются. Нужен новый rollout.
- Файлы в томе kubelet со временем обновит, но приложение должно перечитать файл. Многие читают конфиг только при старте.
Рабочие подходы:
- Перезапустить деплоймент после изменения:
kubectl rollout restart deployment/app. - Добавить аннотацию с хешем конфига в шаблон пода (это удобно делать в Helm и Kustomize). Конфиг изменился — изменился хеш — поды пересоздаются автоматически.
- Использовать неизменяемые версионированные ConfigMap (
app-config-v7,immutable: true) и переключать деплоймент на новое имя. Откат становится тривиальным. - Следить за файлом в приложении, если изменения нужно подхватывать без перезапуска.
Почему base64 — не шифрование
Base64 лишь позволяет хранить бинарные данные как текст. Для декодирования ключ не нужен:
kubectl get secret db-credentials -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
Поэтому настоящая защита строится так:
- RBAC: ограничьте
get,listиwatchдля Secret. Учтите, чтоlistтоже отдаёт значения. - Шифрование при хранении в etcd (настраивается на API-сервере или включается у managed-провайдера).
- Никаких открытых Secret в Git и секретов, зашитых в образ.
- Ограничьте, кто может создавать поды в namespace: любой, кто запускает там под, может смонтировать его секреты.
Как безопасно хранить секреты в Git
| Инструмент | Как работает | Кому подходит |
|---|---|---|
| Sealed Secrets | Secret шифруется публичным ключом кластера, расшифровать может только контроллер внутри него | GitOps-командам без внешнего хранилища |
| External Secrets Operator | Синхронизирует значения из внешнего хранилища (облачные secret managers, Vault) в Secret | Командам с централизованным хранилищем секретов |
| SOPS | Шифрует значения в файлах ключами KMS или age, расшифровка при деплое | Пайплайнам на Helm или Kustomize |
Частые ошибки
- Положить пароль в ConfigMap «временно».
- Закоммитить Secret в base64 и считать его скрытым.
- Ждать, что env обновятся после правки ConfigMap.
- Монтировать через
subPathи удивляться, что файлы не обновляются. - Выдать CI или разработчикам права на чтение всех Secret в кластере.