Один и тот же образ должен работать в dev, на стенде и в проде. Отличается только конфигурация, и её держат вне образа: в ConfigMap обычные настройки, в Secret пароли, токены и ключи.
ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: api-config
data:
LOG_LEVEL: info
FEATURE_NEW_CHECKOUT: "true"
app.yaml: |
cache:
ttl: 60s
Значения в data всегда строки, поэтому "true" и числа в кавычках. Создать из файлов: kubectl create configmap api-config --from-file=app.yaml --from-literal=LOG_LEVEL=info. Размер ConfigMap ограничен 1 МиБ (ограничение etcd), это не место для больших файлов.
Как передать в под
containers:
- name: api
envFrom:
- configMapRef: { name: api-config } # все ключи как переменные
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef: { name: db-secret, key: password }
volumeMounts:
- name: config
mountPath: /etc/api
readOnly: true
volumes:
- name: config
configMap: { name: api-config } # каждый ключ как файл
Два способа с разным поведением при изменении:
- Переменные окружения читаются один раз при старте контейнера. Изменили ConfigMap, а поды видят старое, пока их не пересоздать.
- Тома kubelet обновляет сам (с задержкой до минуты-двух), файлы меняются атомарно через символьные ссылки. Приложение должно перечитать файл. Исключение: том с
subPathне обновляется.
Изменение ConfigMap не перезапускает поды Deployment, потому что шаблон пода не изменился. Решения: kubectl rollout restart, хеш конфигурации в аннотации шаблона (так делают Helm-чарты: checksum/config), неизменяемые ConfigMap с новым именем на каждую версию (подход kustomize configMapGenerator) или контроллеры вроде Reloader.
Secret
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
password: S3cret-from-ci
Значения в data хранятся в base64. Это кодирование, а не шифрование: echo c2VjcmV0 | base64 -d раскодирует любой. stringData позволяет писать открытый текст, API сам закодирует.
Чем Secret лучше ConfigMap:
- отдельные права RBAC: можно разрешить читать ConfigMap, но не Secret;
- kubelet держит смонтированные секреты в tmpfs (память), а не на диске узла;
- можно включить шифрование в etcd (encryption at rest через
EncryptionConfigurationили KMS-провайдер). Без него секреты лежат в etcd просто в base64.
Типы: Opaque (произвольные), kubernetes.io/tls (сертификат и ключ), kubernetes.io/dockerconfigjson (доступ к приватному реестру, используется в imagePullSecrets), токены ServiceAccount.
Где на самом деле хранить секреты
Secret-манифест с реальным паролем нельзя коммитить в git. Варианты:
- Внешнее хранилище (HashiCorp Vault, облачные Secret Manager) + External Secrets Operator, который создаёт Secret в кластере из хранилища и обновляет его.
- Sealed Secrets: в git лежит зашифрованный
SealedSecret, расшифровать его может только контроллер в кластере. - SOPS: файлы шифруются ключом (age, KMS), расшифровываются в CI или GitOps-инструментом.
- Secrets Store CSI Driver: секрет монтируется из хранилища прямо как файл.
Безопасность секретов
- Тот, кто может создать под в namespace, может смонтировать любой Secret этого namespace. Права на создание подов это фактически права на чтение секретов.
- Значения переменных из Secret не видны в
kubectl describe pod(там только ссылка), но доступны процессу и его потомкам и легко попадают в дампы и отчёты об ошибках. Файлы предпочтительнее. - Не логировать конфигурацию целиком при старте.
- Ротация: секреты меняются, приложение должно уметь перечитать или перезапуститься.
Неизменяемые объекты
immutable: true у ConfigMap или Secret запрещает изменения: защищает от случайной правки и снимает нагрузку на API server (kubelet не следит за изменениями). Новая версия это новый объект с новым именем.
Downward API
Под может получить данные о себе: имя, namespace, IP, метки, лимиты ресурсов.
env:
- name: POD_NAME
valueFrom: { fieldRef: { fieldPath: metadata.name } }
- name: MEM_LIMIT
valueFrom: { resourceFieldRef: { resource: limits.memory } }
Частые ошибки
- Считать base64 защитой.
- Коммитить Secret-манифесты с настоящими значениями.
- Ждать, что смена ConfigMap применится к переменным окружения без рестарта.
- Монтировать через
subPathи ждать автообновления. - Класть в ConfigMap большие файлы.
Что спросят на собесе
- Чем ConfigMap отличается от Secret и защищён ли Secret.
- Как передать конфиг в под и что будет при его изменении.
- Как сделать, чтобы под перезапустился после смены конфигурации.
- Где хранить секреты, чтобы их не было в git.
- Что такое encryption at rest для Secret.