В чём разница между ConfigMap и Secret
И ConfigMap, и Secret — это объекты Kubernetes, предназначенные для отделения конфигурации и параметров окружения от скомпилированного кода приложения (согласно III фактору мето...
🟢 Junior Level
30-секундный ответ
И ConfigMap, и Secret — это объекты Kubernetes, предназначенные для отделения конфигурации и параметров окружения от скомпилированного кода приложения (согласно III фактору методологии Twelve-Factor App).
Главная разница между ними заключается в степени конфиденциальности данных:
ConfigMap: предназначен для открытых, неконфиденциальных настроек (URL баз данных, порты сервисов, флаги фичей, логирование). Данные хранятся в виде обычного открытого текста.Secret: предназначен для чувствительных данных и учётных записей (пароли СУБД, приватные SSH/TLS-ключи, API-токены, учетные записи Docker Registry). В манифестах значения кодируются в формат Base64.
Наглядная аналогия из жизни
- ConfigMap — это стикер на мониторе сотрудника: на нём написан внутренний телефон столовой и номер кабинета бухгалтерии. Любой проходящий мимо коллега может прочитать эту информацию, в этом нет никакой угрозы безопасности.
- Secret — это запертый сейф под столом: внутри лежат печати компании, трудовые книжки и банковские ключи. Доступ к нему разрешён только уполномоченным лицам по строгому регламенту.
Примеры базовых манифестов
1. ConfigMap (Открытый текст)
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
DB_HOST: "postgres.prod.svc.cluster.local"
DB_PORT: "5432"
SPRING_PROFILES_ACTIVE: "prod"
2. Secret (Base64 кодирование)
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
type: Opaque
data:
# echo -n "secret123" | base64 -> c2VjcmV0MTIz
DB_PASSWORD: c2VjcmV0MTIz
[!WARNING] Base64 — это НЕ шифрование! Это лишь обратимый алгоритм кодирования двоичных данных в текст. Любой разработчик, имеющий доступ к чтению секретов в кластере, может мгновенно декодировать пароль командой
echo "c2VjcmV0MTIz" | base64 -d.
🟡 Middle Level
Способы внедрения в Pod: Переменные окружения vs Тома
[ ConfigMap / Secret ]
│
┌──────────────────────────┴──────────────────────────┐
▼ ▼
[ 1. Environment Variables ] [ 2. Volume Mount ]
- Значения фиксируются при старте - Монтируется как каталог с файлами
- ❌ Не обновляются без перезапуска пода - 🟢 Обновляются на лету без перезапуска!
1. Через переменные окружения (env / envFrom)
containers:
- name: java-app
image: order-service:1.0
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: order-service-config
key: DB_HOST
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: order-service-secret
key: DB_PASSWORD
Критический недостаток: если вы измените пароль в Secret, значение переменной окружения в работающем Linux-процессе НЕ изменится. Требуется перезапуск пода (kubectl rollout restart).
2. Через монтирование тома (Volume Mounts)
containers:
- name: java-app
volumeMounts:
- name: config-dir
mountPath: /app/config
readOnly: true
volumes:
- name: config-dir
configMap:
name: order-service-config
Преимущество: Kubelet автоматически отслеживает изменения в ConfigMap и обновляет файлы на диске пода без перезапуска контейнера!
Секция stringData в Secret
Чтобы инженерам не приходилось вручную кодировать каждую строку через base64, Kubernetes поддерживает поле stringData:
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
stringData:
DB_PASSWORD: "plain-text-password-here" # Пишется открытым текстом!
При сохранении манифеста API-сервер сам автоматически переводит все значения из stringData в Base64 и записывает их в поле data.
Флаг immutable: true
Начиная с Kubernetes 1.21, для обоих объектов доступен флаг неизменяемости:
immutable: true
- Защита от случайных правок: защищает продакшен-базы данных от сбоев из-за неверной правки конфига.
- Снижение нагрузки на кластер: Kubelet перестаёт непрерывно опрашивать API Server в поисках изменений в файлах конфигурации, радикально экономя CPU и память в кластерах с тысячами подов.
🔴 Senior Level
Иллюзия безопасности Secret и Encryption at Rest
По умолчанию Kubernetes хранит все объекты Secret в базе данных etcd в открытом незашифрованном виде (только Base64).
Злоумышленник, получивший доступ к резервной копии etcd или диску мастер-ноды, получает доступ ко всем секретам кластера.
Как включить реальное шифрование (Encryption at Rest)
Настраивается на уровне kube-apiserver через конфигурационный файл --encryption-provider-config:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
# Рекомендуемый enterprise-стандарт: аппаратный KMS облака
- kms:
apiVersion: v2
name: aws-kms-provider
endpoint: unix:///var/run/kmsplugin/socket.sock
# Либо локальное симметричное AES-GCM/AES-CBC шифрование
- aescbc:
keys:
- name: key1
secret: <32-byte-base64-key>
- identity: {} # Запасной провайдер для чтения незашифрованных старых данных
Атомарное обновление файлов конфигурации через Симлинки (Symlink Swapping)
Когда Kubelet обновляет смонтированный ConfigMap на диске контейнера, он не перезаписывает файлы напрямую. Если бы процесс Java читал файл в момент перезаписи, возникла бы ошибка частичного чтения (Torn Read).
Kubernetes решает это с помощью трёхуровневой структуры символических ссылок:
/app/config/
├── application.yaml -> ..data/application.yaml
├── ..data -> ..2026_09_26_14_30_00.123456789 (Симлинк на актуальную папку)
└── ..2026_09_26_14_30_00.123456789/ (Каталог с реальными файлами)
└── application.yaml
При обновлении конфига Kubelet загружает новые файлы в каталог с новой временной меткой и выполняет одну атомарную системную операцию замены симлинка ..data (rename()).
Production-стандарт: External Secrets Operator (ESO)
В зрелых enterprise-архитектурах секреты никогда не создаются вручную через YAML и не хранятся в Git. Используется External Secrets Operator:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: order-db-secret
spec:
secretStoreRef:
name: hashicorp-vault # Интеграция с Vault, AWS Secrets Manager или GCP Secret Manager
kind: ClusterSecretStore
target:
name: order-service-db-creds # Имя создаваемого нативного K8s Secret
data:
- secretKey: password
remoteRef:
key: secret/data/production/database
property: password
ESO самостоятельно выполняет ротацию ключей, синхронизацию и отзыв прав без вмешательства разработчика.
4 Tricky Questions
1. Если обновить значение в объекте ConfigMap, обновятся ли автоматически переменные окружения (env) и смонтированные тома (volumeMounts) внутри уже работающего контейнера?
Ответ:
- Переменные окружения (
env/envFrom): НЕ обновятся никогда. Переменные окружения формируются ядром Linux в момент создания процесса контейнера (execve) и остаются неизменными до полной гибели процесса. Чтобы приложение подхватило новые переменные, под необходимо принудительно перезапустить (kubectl rollout restart). - Смонтированные тома (
volumeMounts): обновятся автоматически. Kubelet в рамках цикла синхронизации (по умолчанию от 10 до 60 секунд) обновит симлинк..dataв смонтированной директории. Однако само приложение (например, Spring Boot) должно уметь перечитывать файлы с диска (через@RefreshScope, Spring Cloud Kubernetes или собственный файловый вотчер).
2. В чём разница между полями data и stringData в спецификации Secret, и что произойдёт, если одно и то же имя ключа указано в обоих полях одновременно?
Ответ:
data: принимает значения, строго закодированные в Base64. Применяется в автоматизированных пайплайнах CI/CD.stringData: принимает значения в виде обычного открытого текста (Plain Text). Это вспомогательное поле «только для записи» (Write-Only), созданное для удобства человека.- Конфликт ключей: если один и тот же ключ объявлен и в
data, и вstringData, значение изstringDataимеет абсолютный приоритет и перезаписывает значение изdata. При чтении секрета из API полеstringDataвсегда удаляется, а итоговый результат возвращается строго в полеdataв Base64.
3. Почему Java-библиотека java.nio.file.WatchService, настроенная на конкретный файл конфигурации /app/config/application.properties, перестаёт получать события обновления после первого же изменения ConfigMap?
Ответ:
Kubelet обновляет файлы тома не модификацией байтов внутри существующего дескриптора файла, а атомарной подменой симлинка ..data на новую директорию.
Старый файл, на который был подписан WatchService Java по его Linux Inode, фактически удаляется из дерева директорий. Ссылка на старый Inode становится «осиротевшей», и операционная система больше никогда не генерирует для неё событий ENTRY_MODIFY.
Решение: подписывать WatchService не на конкретный целевой файл, а на родительскую директорию /app/config/, отслеживать событие пересоздания симлинка и переоткрывать файловый поток.
4. Каковы жесткие ограничения на максимальный размер данных в одном объекте ConfigMap или Secret, и чем обусловлен этот лимит?
Ответ:
Максимальный размер любого объекта ConfigMap или Secret составляет строго 1 Мегабайт (1 MiB).
Это ограничение продиктовано архитектурой хранилища базы данных etcd:
- По умолчанию максимальный размер одного gRPC-запроса и одной транзакции в
etcdжестко ограничен 1.5 МБ. - Хранение больших конфигурационных файлов, сертификатов или бинарных файлов в
ConfigMapприводит к катастрофической фрагментации базы данныхetcd, падению производительности консенсуса Raft и деградации всего кластера. Большие файлы должны передаваться через постоянные дисковые тома (PersistentVolumes) или внешние объектные хранилища (S3, MinIO).
🎯 Шпаргалка для интервью
Главное о ConfigMap и Secret
- ConfigMap: открытые настройки (URL, порты, параметры); хранится в открытом тексте.
- Secret: конфиденциальные данные (пароли, сертификаты, токены); в YAML пишется в Base64.
- Безопасность: Base64 $\ne$ шифрование! Для безопасности etcd требуется Encryption at Rest.
- Лимит размера: строго 1 МБ (ограничение etcd).
- Переменные окружения (
env): фиксируются при старте пода; не обновляются без перезапуска. - Тома (
volumes): обновляются на лету Kubelet’ом через замену симлинков..data. stringData: позволяет писать секреты обычным текстом; API-сервер сам сделает Base64.- Enterprise-стандарт: External Secrets Operator (ESO) + HashiCorp Vault.
Сравнительная таблица
| Параметр | ConfigMap | Secret |
| :— | :— | :— |
| Формат в манифесте | Открытый текст UTF-8 | Base64 (data) или текст (stringData) |
| Хранение в etcd | Plain Text | Plain Text (по умолчанию) / Зашифровано (AES/KMS) |
| Встроенные типы | Нет (универсальный) | Opaque, kubernetes.io/tls, dockerconfigjson |
| Назначение | Переменные сред, конфиги Spring | Пароли БД, токены, SSL-сертификаты |
Красные флаги на собеседовании (Чего говорить нельзя)
- ❌ «Kubernetes Secret надёжно шифрует пароли с помощью Base64» — Base64 это кодирование, декодируется в одну строчку.
- ❌ «Если изменить ConfigMap, переменные окружения в работающем Java-контейнере обновятся автоматически» — env vars не обновляются без рестарта пода.
- ❌ «В ConfigMap можно положить JAR-файл или видеоролик размером 50 МБ» — лимит любого объекта равен ровно 1 МБ.