- Конфиги
- Проблема конфигурации
- Что такое ConfigMap
- Использование ConfigMap в Pod
- ConfigMap для конфигурационных файлов
- Что такое Secret
- Использование Secret в Pod
- Типы Secret
- Обновление конфигурации
- Практический пример: полная конфигурация приложения
- Безопасность Secret
- External Secrets Operator
- Лучшие практики
- Заключение
Конфиги
При работе с приложениями в Kubernetes возникает важная задача: как правильно передавать конфигурацию? Хардкодить значения в код неправильно, пересобирать образы для каждой среды (development, staging, production) неудобно. ConfigMap и Secret решают эту задачу, позволяя отделить конфигурацию от кода.
Проблема конфигурации
Представьте веб-приложение, которому нужны параметры подключения к базе данных, URL внешних API, настройки кеширования и feature flags. Эти параметры различаются между окружениями: в development используется локальная база данных, в production — отказоустойчивый кластер.
Традиционный подход — создавать разные Docker-образы для каждой среды. Но это приводит к проблемам: нельзя быть уверенным, что код в production идентичен протестированному в staging; изменение одного параметра требует полной пересборки образа; секретные данные попадают в слои образа и могут утечь.
Kubernetes предлагает лучшее решение: хранить конфигурацию отдельно от образа и монтировать её в контейнеры во время запуска. ConfigMap для обычных настроек, Secret для чувствительных данных.
Что такое ConfigMap
ConfigMap — это объект Kubernetes, который хранит конфигурационные данные в виде пар ключ-значение. Вы создаёте ConfigMap с нужными параметрами и подключаете его к Pod через переменные окружения или файлы.
Создадим простой ConfigMap с настройками приложения:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database_host: "postgres.default.svc.cluster.local"
database_port: "5432"
cache_enabled: "true"
log_level: "info"
ConfigMap содержит четыре параметра. Все значения хранятся как строки, даже если это числа или булевы значения. Приложение должно преобразовывать их в нужный тип при чтении.
Создайте ConfigMap несколькими способами:
# Из YAML-манифеста
kubectl apply -f app-config.yaml
# Из литеральных значений
kubectl create configmap app-config \
--from-literal=database_host=postgres.default.svc.cluster.local \
--from-literal=database_port=5432
# Из файла
echo "info" > log_level.txt
kubectl create configmap app-config --from-file=log_level.txt
# Из директории
kubectl create configmap app-config --from-file=config-dir/
Посмотрите на созданный ConfigMap:
# Список ConfigMap
kubectl get configmap
# Содержимое ConfigMap
kubectl describe configmap app-config
# Вывести в YAML
kubectl get configmap app-config -o yaml
Использование ConfigMap в Pod
Существует три способа использования ConfigMap в Pod: как переменные окружения, как файлы в томе или как аргументы командной строки.
Переменные окружения
Самый простой способ — монтировать значения из ConfigMap как переменные окружения:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: example/app:1.0
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: database_host
- name: DATABASE_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: database_port
Здесь переменная DATABASE_HOST получает значение из ключа database_host в ConfigMap app-config. Если нужно импортировать все ключи сразу, используйте envFrom:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: example/app:1.0
envFrom:
- configMapRef:
name: app-config
Все ключи из ConfigMap станут переменными окружения. Ключ database_host превратится в переменную database_host.
Файлы в томе
Для сложной конфигурации удобнее монтировать ConfigMap как файлы. Каждый ключ становится файлом, значение — содержимым файла:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: example/app:1.0
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config
В контейнере появится директория /etc/config с файлами: database_host, database_port, cache_enabled, log_level. Каждый файл содержит соответствующее значение.
Можно выбрать конкретные ключи и задать имена файлов:
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: database_host
path: db-host.txt
- key: database_port
path: db-port.txt
Теперь в директории /etc/config будут файлы db-host.txt и db-port.txt.
ConfigMap для конфигурационных файлов
ConfigMap отлично подходит для хранения полных конфигурационных файлов. Например, конфигурация Nginx:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend-service:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static {
root /usr/share/nginx/html;
}
}
Символ | позволяет хранить многострочный текст. Смонтируйте этот ConfigMap в Pod с Nginx:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.21
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-config
configMap:
name: nginx-config
Nginx прочитает конфигурацию из /etc/nginx/conf.d/nginx.conf при запуске.
Что такое Secret
Secret работает аналогично ConfigMap, но предназначен для чувствительных данных: паролей, ключей API, сертификатов. Kubernetes хранит Secret в зашифрованном виде (если настроено шифрование etcd) и ограничивает доступ к ним через RBAC.
Создадим Secret с параметрами подключения к базе данных:
apiVersion: v1
kind: Secret
metadata:
name: database-secret
type: Opaque
data:
username: cG9zdGdyZXM=
password: c3VwZXJzZWNyZXQ=
Значения в Secret должны быть закодированы в base64. Это не шифрование, а только кодирование для хранения бинарных данных:
# Закодировать значение
echo -n "postgres" | base64
# Результат: cG9zdGdyZXM=
echo -n "supersecret" | base64
# Результат: c3VwZXJzZWNyZXQ=
Создавайте Secret из командной строки для безопасности:
# Из литеральных значений
kubectl create secret generic database-secret \
--from-literal=username=postgres \
--from-literal=password=supersecret
# Из файлов
echo -n "postgres" > username.txt
echo -n "supersecret" > password.txt
kubectl create secret generic database-secret \
--from-file=username.txt \
--from-file=password.txt
# Для TLS-сертификатов
kubectl create secret tls tls-secret \
--cert=path/to/cert.pem \
--key=path/to/key.pem
Посмотрите на Secret:
# Список Secret
kubectl get secret
# Подробная информация (без отображения значений)
kubectl describe secret database-secret
# Вывести значения (декодированные)
kubectl get secret database-secret -o jsonpath='{.data.password}' | base64 --decode
Использование Secret в Pod
Secret используется так же, как ConfigMap: через переменные окружения или файлы.
Переменные окружения
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: example/app:1.0
env:
- name: DATABASE_USERNAME
valueFrom:
secretKeyRef:
name: database-secret
key: username
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: database-secret
key: password
Приложение получит переменные DATABASE_USERNAME и DATABASE_PASSWORD с расшифрованными значениями.
Файлы в томе
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: example/app:1.0
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: database-secret
В директории /etc/secrets появятся файлы username и password с расшифрованными значениями. Используйте readOnly: true для дополнительной безопасности.
Типы Secret
Kubernetes поддерживает несколько типов Secret для разных сценариев:
Opaque
Opaque — базовый тип для произвольных данных. Используется по умолчанию:
apiVersion: v1
kind: Secret
metadata:
name: generic-secret
type: Opaque
data:
api_key: YXBpa2V5MTIzNDU2
kubernetes.io/tls
Для хранения TLS-сертификатов и ключей:
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-key>
kubernetes.io/dockerconfigjson
Для аутентификации в приватных Docker-реестрах:
kubectl create secret docker-registry registry-secret \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=password \
--docker-email=user@example.com
Используйте этот Secret в Pod для загрузки приватных образов:
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
imagePullSecrets:
- name: registry-secret
containers:
- name: app
image: registry.example.com/app:1.0
kubernetes.io/basic-auth
Для хранения имени пользователя и пароля:
apiVersion: v1
kind: Secret
metadata:
name: basic-auth-secret
type: kubernetes.io/basic-auth
stringData:
username: admin
password: password123
Используйте stringData вместо data для автоматического кодирования в base64.
Обновление конфигурации
Когда вы обновляете ConfigMap или Secret, возникает вопрос: как обновить работающие Pod? Kubernetes не перезапускает Pod автоматически при изменении ConfigMap/Secret.
Если конфигурация смонтирована как том, изменения появятся в контейнере через некоторое время (обычно 1-2 минуты). Но приложение должно само отслеживать изменения файлов и перечитывать конфигурацию.
Если конфигурация передана через переменные окружения, изменения не применятся до перезапуска Pod. Переменные окружения устанавливаются при создании контейнера и не меняются.
Для принудительного обновления Pod измените Deployment, добавив аннотацию:
kubectl patch deployment app-deployment \
-p '{"spec":{"template":{"metadata":{"annotations":{"configmap-version":"2"}}}}}'
Изменение аннотации заставит Deployment выполнить rolling update и пересоздать все Pod с новой конфигурацией.
Альтернатива — использовать checksum ConfigMap в аннотациях:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
template:
metadata:
annotations:
configmap-checksum:
{
{
include (print $.Template.BasePath "/configmap.yaml") . | sha256sum,
},
}
spec:
containers:
- name: app
image: example/app:1.0
Этот подход используется в Helm — при изменении ConfigMap checksum изменится, и Deployment обновит Pod.
Практический пример: полная конфигурация приложения
Создадим полноценное приложение с ConfigMap для настроек и Secret для паролей:
# ConfigMap с настройками
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
config.json: |
{
"server": {
"port": 8080,
"host": "0.0.0.0"
},
"database": {
"host": "postgres.default.svc.cluster.local",
"port": 5432,
"name": "appdb"
},
"cache": {
"enabled": true,
"ttl": 3600
},
"features": {
"newUI": true,
"analytics": false
}
}
---
# Secret с чувствительными данными
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
stringData:
database-username: appuser
database-password: supersecret123
api-key: sk-1234567890abcdef
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: example/app:1.0
ports:
- containerPort: 8080
env:
- name: DATABASE_USERNAME
valueFrom:
secretKeyRef:
name: app-secret
key: database-username
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: database-password
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secret
key: api-key
volumeMounts:
- name: config-volume
mountPath: /etc/app
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
Приложение получит конфигурацию из файла /etc/app/config.json и чувствительные данные из переменных окружения. Такое разделение — хорошая практика: статические настройки в файлах, динамические секреты в переменных.
Безопасность Secret
Secret хранится в etcd в base64, что не является шифрованием. Для настоящей безопасности включите шифрование etcd на уровне кластера. Это можно сделать через конфигурацию API-сервера:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
Используйте RBAC для ограничения доступа к Secret. По умолчанию только администраторы и Pod, использующие Secret, имеют к ним доступ:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
Для максимальной безопасности используйте внешние системы управления секретами: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault. Kubernetes может интегрироваться с ними через CSI-драйверы или операторы.
External Secrets Operator
External Secrets Operator синхронизирует Secret из внешних хранилищ в Kubernetes:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
auth:
tokenSecretRef:
name: vault-token
key: token
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secret
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: app-secret
data:
- secretKey: database-password
remoteRef:
key: database
property: password
External Secrets Operator автоматически создаст и обновит Secret app-secret из Vault.
Лучшие практики
При работе с ConfigMap и Secret следуйте правилам безопасности и удобства. Никогда не коммитьте Secret в Git. Используйте шаблоны с плейсхолдерами или инструменты шифрования (sealed-secrets, sops).
Разделяйте конфигурацию по окружениям. Создавайте отдельные ConfigMap для development, staging и production. Используйте namespace для изоляции.
Документируйте структуру ConfigMap. Добавляйте комментарии и примеры значений, чтобы другие разработчики понимали назначение каждого параметра.
Используйте валидацию конфигурации в приложении. Проверяйте наличие обязательных параметров при запуске и выводите понятные ошибки.
Ротируйте Secret регулярно. Меняйте пароли и ключи API с определённой периодичностью и автоматизируйте этот процесс.
Ограничивайте доступ к Secret через RBAC. Предоставляйте минимальные необходимые права сервисным аккаунтам и пользователям.
Заключение
ConfigMap и Secret — это фундаментальные инструменты для управления конфигурацией в Kubernetes. ConfigMap для обычных настроек, Secret для чувствительных данных. Оба объекта можно монтировать как переменные окружения или файлы.
Отделение конфигурации от кода делает приложения портируемыми между окружениями. Один и тот же образ работает в development, staging и production с разными настройками. Изменение конфигурации не требует пересборки образа.
Безопасность Secret требует дополнительных мер: шифрование etcd, RBAC, внешние системы управления секретами. Для production-окружений рекомендуется использовать специализированные решения вроде Vault или External Secrets Operator.
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.