Зарегистрируйтесь, чтобы продолжить обучение

Конфиги Kubernetes

Конфиги

При работе с приложениями в 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.

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff