ConfigMap и Secret
| Тип | объекты API Kubernetes (core/v1) |
|---|---|
| Хранилище | etcd |
| Максимальный размер | 1 МиБ на объект |
| Способы передачи | переменные окружения, файлы в томе, аргументы команды |
| Неизменяемые объекты | поле immutable (стабильно с 1.21) |
| Шифрование в etcd | по умолчанию выключено, включается EncryptionConfiguration |
| Внешние решения | Sealed Secrets, External Secrets Operator, HashiCorp Vault, SOPS |
ConfigMap и Secret — объекты Kubernetes, которые отделяют конфигурацию приложения от его образа. Один и тот же образ запускается в тестовом и боевом окружении, а различия — адрес базы, уровень логирования, ключи внешних API — подставляются при старте пода. ConfigMap предназначен для обычных настроек,[1] Secret — для чувствительных данных: паролей, токенов, TLS-сертификатов, ключей доступа к реестру образов.[2]
Зачем
Принцип «конфигурация хранится в окружении, а не в коде» сформулирован в методологии Twelve-Factor App.[6] Kubernetes реализует его так: конфигурация — отдельный объект в API кластера, который можно менять, версионировать и подключать к любому числу подов. Код не пересобирается ради смены одного параметра, а секреты не попадают в слои образа и в историю Git.
ConfigMap
ConfigMap — это словарь «ключ → строка». Значением может быть и короткий параметр, и целый файл конфигурации.
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
LOG_LEVEL: info
FEATURE_X: "true"
nginx.conf: |
server {
listen 8080;
location / { proxy_pass http://127.0.0.1:8000; }
}
Числа и логические значения в data нужно брать в кавычки — там допускаются только строки. Для двоичных данных есть поле binaryData.
Secret
Устроен так же, но значения в поле data записываются в кодировке Base64, а в stringData можно писать открытым текстом — API сам их закодирует. У Secret есть типы, задающие ожидаемые ключи:
| Тип | Назначение |
|---|---|
Opaque |
произвольные данные (по умолчанию) |
kubernetes.io/tls |
сертификат и ключ: tls.crt, tls.key (используется Ingress) |
kubernetes.io/dockerconfigjson |
учётные данные для закрытого реестра образов |
kubernetes.io/basic-auth, ssh-auth |
логин и пароль, SSH-ключ |
kubernetes.io/service-account-token |
токен сервисного аккаунта (устаревший способ) |
kubectl create secret generic db --from-literal=PASSWORD='s3cr3t'
kubectl create secret tls example-com --cert=fullchain.pem --key=privkey.pem
kubectl get secret db -o jsonpath='{.data.PASSWORD}' | base64 -d
Base64 — не шифрование. Любой, кто может прочитать объект через API, получит секрет в открытом виде. По умолчанию Secret хранится в etcd незашифрованным; шифрование «на диске» включают отдельной настройкой API-сервера (EncryptionConfiguration), в том числе с ключом во внешнем KMS.[4] Главная защита — права доступа RBAC: чтение секретов разрешают только тем сервисным аккаунтам и людям, которым это действительно нужно.[3]
Как передать в под
spec:
containers:
- name: app
image: ghcr.io/example/web:1.4.0
envFrom:
- configMapRef: {name: web-config} # все ключи как переменные
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef: {name: db, key: PASSWORD}
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-conf
configMap:
name: web-config
items: [{key: nginx.conf, path: default.conf}]
Два способа ведут себя по-разному при изменении объекта:
- Переменные окружения читаются только при старте контейнера. Чтобы применить новое значение, поды нужно перезапустить, например
kubectl rollout restart deployment/web. - Файлы в томе kubelet обновляет автоматически (с задержкой до минуты), но приложение должно само перечитать файл. Исключение — тома, смонтированные через
subPath: они не обновляются.
Популярный приём — добавить в аннотацию шаблона пода хеш конфигурации: при её изменении меняется шаблон, и Deployment сам выполняет плавное обновление. Так делают многие чарты Helm.
С версии 1.21 объект можно пометить immutable: true: его нельзя изменить, только пересоздать, а kubelet перестаёт следить за ним — это снижает нагрузку на API-сервер в больших кластерах и защищает от случайных правок.[1]
Секреты и Git
Хранить манифесты Secret в репозитории в открытом виде нельзя: Base64 раскодирует кто угодно. Распространённые решения:
- Sealed Secrets — секрет шифруется открытым ключом контроллера в кластере; в Git лежит зашифрованный
SealedSecret, расшифровать его может только кластер. - SOPS — шифрует значения в YAML-файлах ключами age, PGP или облачного KMS; удобно в связке с GitOps (Argo CD, Flux).
- External Secrets Operator — синхронизирует секреты из внешнего хранилища (HashiCorp Vault, AWS Secrets Manager, Yandex Lockbox и др.) в объекты Secret.[5]
- CSI-драйвер Secrets Store — монтирует секреты из хранилища прямо в под, минуя etcd.
Подробнее о подходах — в статье Управление секретами.
Ограничения
- Размер одного объекта — не больше 1 МиБ: для больших файлов нужен PersistentVolume или образ.
- ConfigMap и Secret принадлежат пространству имён и доступны только подам из него же.
- Под не запустится, если указанный ConfigMap или Secret отсутствует (если ссылка не помечена
optional: true). - Секрет, переданный через переменную окружения, может утечь в логи, дампы и вывод отладочных инструментов; файлы в томе безопаснее.