← До програми курсу

Модуль 6 / 19

ConfigMap, Secret, Downward API та доставка конфігурації

KUBERNETES

Зміст
01

1. Цілі та результат модуля

Після модуля ви зможете відокремлювати конфігурацію від образу, обирати ConfigMap або Secret, доставляти значення через env і volumes, використовувати Downward API та діагностувати помилку відсутнього об’єкта чи ключа.

  • ConfigMap зберігає несекретну конфігурацію
  • Secret обмежує призначення й доступ, але base64 не є шифруванням
  • env фіксується під час запуску контейнера
  • projected volume зазвичай оновлюється із затримкою
  • subPath mount не отримує автоматичних оновлень
02

2. Теорія: ConfigMap і межі конфігурації

ConfigMap містить пари data string/string і binaryData bytes у base64. Він підходить для flags, файлів конфігурації та невеликих довідників, але не для секретів і великих артефактів. Загальний розмір об’єкта обмежений API Server приблизно одним MiB.

Конфігурація — частина release contract. Застосунок має перевіряти обов’язкові поля під час старту, повідомляти зрозумілу помилку й документувати precedence між defaults, файлом, env та аргументами.

03

3. Теорія: Secret без хибного відчуття безпеки

Secret має тип і поля data або stringData. API перетворює stringData на base64-представлення data. Будь-який суб’єкт із get Secret може декодувати вміст, тому base64 — серіалізація, а не захист.

Захист потребує мінімального RBAC, encryption at rest для etcd, безпечного backup, обмеження доступу до Node та виключення secret із Git, logs, shell history і diagnostic bundles. Secret через env доступний процесу й потенційно dumps; volume дає точніше обмежити файли та permissions.

  • Opaque — довільні пари
  • kubernetes.io/tls — tls.crt і tls.key
  • kubernetes.io/dockerconfigjson — registry credentials
  • ServiceAccount token Secrets не слід створювати вручну для звичайних короткоживучих токенів
04

4. Теорія: env, envFrom і volumes

env задає окремі значення та явні імена, envFrom імпортує всі keys і потребує дисципліни іменування. Значення читаються під час створення контейнера й не змінюються в уже запущеному process environment.

ConfigMap/Secret volume проєктує keys як файли. Kubelet періодично оновлює projection атомарною зміною посилань, тому оновлення не миттєве, а застосунок має вміти перечитати файл. Mount через subPath фіксує конкретний файл і автоматичного оновлення не отримує.

Оновлення ConfigMap саме по собі не створює Deployment revision. Поширений delivery pattern — versioned immutable ConfigMap або checksum annotation у Pod template, що викликає контрольований rollout.

05

5. Теорія: Downward API

Downward API передає контейнеру відомості про власний Pod без звернення до Kubernetes API: name, namespace, uid, labels, annotations, nodeName та окремі resource requests/limits. Доступні fieldRef, resourceFieldRef і downwardAPI volume.

Env-поля залишаються snapshot на старті. Labels та annotations у downwardAPI volume можуть оновлюватися. Не використовуйте Downward API як заміну service discovery або спосіб видати застосунку широкі API credentials.

06

6. Теорія: immutable configuration і rollout

ConfigMap і Secret можна позначити immutable: true. Це захищає від випадкової mutation та зменшує спостереження kubelet, але значення й прапорець immutable вже не можна змінити — потрібен об’єкт із новим ім’ям та оновлення consumers.

Versioned names роблять rollback явним і дозволяють старим та новим Pods одночасно читати сумісну конфігурацію. Mutable object зручніший у лабораторії, але в production потребує визначеної стратегії reload та аудиту.

07

7. Методичні вказівки

  • Не додавайте секрети до manifest, Git, annotation або ConfigMap
  • Використовуйте server dry-run до створення consumers
  • Робіть обов’язкові keys явними й перевіряйте startup validation
  • Обирайте env лише для значень, що змінюються через restart
  • Не очікуйте миттєвого volume refresh
  • Уникайте subPath для динамічної конфігурації
  • Задавайте defaultMode і запускайте process без root, де можливо
  • Для production пов’язуйте зміну конфігурації зі спостережуваним rollout
08

8. Лабораторна робота: ConfigMap і Secret

Створіть несекретну конфігурацію та Secret через stringData. Значення навчальні; справжні credentials не можна додавати до репозиторію або командного рядка.

terminal
mkdir -p /tmp/k8s-m06
cat > /tmp/k8s-m06/config.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: k8s-m06
data:
  LOG_LEVEL: info
  APP_MODE: training
  APP_CONFIG: |
    server:
      port: 8080
    features:
      diagnostics: true
---
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
  namespace: k8s-m06
type: Opaque
stringData:
  API_USER: course-user
  API_PASSWORD: correct-horse-battery-staple
EOF
kubectl wait --for=condition=Ready node --all --timeout=120s
kubectl create namespace k8s-m06
kubectl apply --dry-run=server -f /tmp/k8s-m06/config.yaml
kubectl apply --server-side --field-manager=course-lab -f /tmp/k8s-m06/config.yaml
09

9. Лабораторна робота: env, volumes і Downward API

Pod отримує ті самі ConfigMap дані одночасно через env, повний volume і subPath. Downward API публікує identity та metadata самого Pod.

terminal
cat > /tmp/k8s-m06/pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: inspector
  namespace: k8s-m06
  labels:
    app.kubernetes.io/name: config-lab
    course.goretskiy.pro/environment: training
  annotations:
    course.goretskiy.pro/owner: platform-team
spec:
  containers:
    - name: inspector
      image: busybox:1.37
      command: ["sh", "-c", "sleep 3600"]
      envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: app-secret
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
      volumeMounts:
        - name: config
          mountPath: /etc/app-config
          readOnly: true
        - name: config
          mountPath: /snapshot-app-mode
          subPath: APP_MODE
          readOnly: true
        - name: secret
          mountPath: /etc/app-secret
          readOnly: true
        - name: podinfo
          mountPath: /etc/podinfo
          readOnly: true
  volumes:
    - name: config
      configMap:
        name: app-config
        items:
          - {key: LOG_LEVEL, path: LOG_LEVEL}
          - {key: APP_MODE, path: APP_MODE}
          - {key: APP_CONFIG, path: app.yaml}
    - name: secret
      secret:
        secretName: app-secret
        defaultMode: 0400
    - name: podinfo
      downwardAPI:
        items:
          - path: labels
            fieldRef:
              fieldPath: metadata.labels
          - path: annotations
            fieldRef:
              fieldPath: metadata.annotations
EOF
kubectl apply --dry-run=server -f /tmp/k8s-m06/pod.yaml
kubectl apply -f /tmp/k8s-m06/pod.yaml
kubectl -n k8s-m06 wait --for=condition=Ready pod/inspector --timeout=120s
terminal
kubectl -n k8s-m06 exec inspector -- sh -c 'printf "env: %s %s %s %s\n" "$LOG_LEVEL" "$APP_MODE" "$POD_NAMESPACE" "$NODE_NAME"'
kubectl -n k8s-m06 exec inspector -- cat /etc/app-config/app.yaml
kubectl -n k8s-m06 exec inspector -- cat /etc/podinfo/labels
kubectl -n k8s-m06 exec inspector -- stat -c '%a %n' /etc/app-secret/API_PASSWORD
kubectl -n k8s-m06 get secret app-secret -o jsonpath='{.data.API_USER}' | base64 -d; printf '\n'
kubectl -n k8s-m06 get secret app-secret -o jsonpath='{.data.API_PASSWORD}' | base64 -d; printf '\n'
10

10. Лабораторна робота: поведінка під час оновлення

Змініть ConfigMap і дочекайтеся фактичного refresh повного volume. Результат має показати volume=debug, але env=info та subPath=training: два останні значення потребують повторного створення контейнера/Pod.

terminal
kubectl -n k8s-m06 patch configmap app-config --type=merge \
  -p '{"data":{"LOG_LEVEL":"debug","APP_MODE":"updated"}}'

for attempt in $(seq 1 60); do
  value=$(kubectl -n k8s-m06 exec inspector -- cat /etc/app-config/LOG_LEVEL)
  [ "$value" = debug ] && break
  sleep 2
done
kubectl -n k8s-m06 exec inspector -- sh -c 'printf "volume=%s env=%s subPath=%s\n" "$(cat /etc/app-config/LOG_LEVEL)" "$LOG_LEVEL" "$(cat /snapshot-app-mode)"'
11

11. Діагностика: відсутній Secret key

Pod з обов’язковим посиланням на відсутній key залишається у CreateContainerConfigError. Container logs ще немає: причину показують Waiting.message та Events. Додавання key дозволяє kubelet продовжити запуск без повторного створення Pod.

terminal
cat > /tmp/k8s-m06/broken.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: broken-config
  namespace: k8s-m06
spec:
  containers:
    - name: app
      image: busybox:1.37
      command: ["sh", "-c", "sleep 3600"]
      env:
        - name: REQUIRED_VALUE
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: MISSING_VALUE
EOF
kubectl apply -f /tmp/k8s-m06/broken.yaml
kubectl -n k8s-m06 get pod broken-config
kubectl -n k8s-m06 describe pod broken-config
kubectl -n k8s-m06 get events --field-selector involvedObject.name=broken-config --sort-by=.metadata.creationTimestamp

kubectl -n k8s-m06 patch secret app-secret --type=merge \
  -p '{"stringData":{"MISSING_VALUE":"recovered"}}'
kubectl -n k8s-m06 wait --for=condition=Ready pod/broken-config --timeout=120s
kubectl -n k8s-m06 exec broken-config -- printenv REQUIRED_VALUE

Порядок: ім’я та namespace джерела → наявність key → optional flag → Events/Waiting.message → RBAC і admission → спосіб доставки → rollout/reload споживача.

12

12. Контроль, критерії засвоєння та cleanup

  • Чому Secret data не зашифровано base64?
  • Коли використовувати env, а коли volume?
  • Чи оновиться env запущеного контейнера?
  • Чому subPath зберігає старе значення?
  • Як конфігурація створює Deployment revision?
  • Які поля надає Downward API?
  • Що змінює immutable: true?
  • Чому у CreateContainerConfigError зазвичай немає app logs?
  • Які шари захищають Secret?
terminal
kubectl delete namespace k8s-m06 --wait=true
rm -rf /tmp/k8s-m06
kubectl get namespace k8s-m06 2>&1 || true

Модуль засвоєно, якщо ви прогнозуєте результат оновлення для env/full volume/subPath, не розкриваєте Secret і відновлюєте Pod за evidence без випадкового видалення ресурсів.