Урок по треку Docker и Kubernetes 7 мин

ConfigMap и Secret за 7 минут

Конфигурация через env и файлы, Secret и его ограничения, обновление конфига и внешние хранилища секретов.

Один и тот же образ должен работать в 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.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.