Урок 05 · Kubernetes на практике

ConfigMap и Secret

Что-то не запустилось или осталось непонятным? Задайте вопрос в комментариях к разбору этого урока в канале — разберёмся вместе.

Образ hello-fastapi из урока 1 — один и тот же файл, неизменный после сборки. Но приветствие на проде и на тесте вы наверняка захотите разное, а ключ от внешнего API уж точно не один и тот же для всех окружений. Пересобирать образ под каждое окружение — плохая идея: вы теряете смысл слоёв и кеша сборки, о которых говорили в уроке 1. Правильный путь — вынести настройки наружу образа и подложить их уже в кластере. Для обычных настроек это ConfigMap, для чувствительных — Secret.

ConfigMap — обычные настройки

ConfigMap — это просто набор пар ключ-значение, который живёт в кластере отдельно от образа. Значения из него можно подложить в Pod несколькими способами; в этом уроке — самый простой: как переменные окружения. Приложение внутри контейнера ничего не знает про Kubernetes — оно просто читает os.environ, как читало бы их локально.

Secret — то же самое, но для чувствительного

Secret устроен буквально так же, как ConfigMap, — тот же набор ключ-значение, тот же способ подключения к Pod'у. Разница не в механике, а в договорённости: Secret — это место, куда Kubernetes и инструменты вокруг него (RBAC, аудит, сканеры) относятся строже, потому что там ожидаются пароли, токены, ключи API.

Важная оговорка По умолчанию Secret не шифруется — значения просто кодируются в base64, а это тривиально обратимо, не защита. Реальное шифрование Secret'ов в etcd — отдельная настройка кластера, вне рамок этого курса. Считайте Secret не сейфом, а просто «местом с табличкой осторожно», а не физическим замком.
Схема: ConfigMap и Secret вместе попадают в Pod как переменные окружения НЕ СЕКРЕТНО ConfigMap GREETING ЧУВСТВИТЕЛЬНО Secret API_KEY envFrom DEPLOYMENT manifest просто ссылается на оба по имени создаёт POD os.environ GREETING=... API_KEY=••••• приложение просто читает переменные
Для приложения внутри Pod'а ConfigMap и Secret неотличимы — оба превращаются в обычные переменные окружения.
Источник Официальная документация — Kubernetes Docs: ConfigMaps и Kubernetes Docs: Secrets

Практика: подключите настройки к сервису

Понадобится приложение из урока 1 и Deployment из урока 3.

  1. Откройте main.py из урока 1 и добавьте чтение переменных окружения плюс эндпоинт, который их показывает:
    import os
    
    GREETING = os.environ.get("GREETING", "Привет")
    API_KEY = os.environ.get("API_KEY")
    
    @app.get("/config")
    def config():
        return {"greeting": GREETING, "api_key_is_set": API_KEY is not None}

    Обратите внимание: эндпоинт подтверждает, что ключ задан, но не возвращает его значение целиком — секреты не стоит светить даже в собственном debug-эндпоинте.

  2. Пересоберите образ под новым тегом и загрузите его в minikube — так же, как в уроке 3:
    docker build -t hello-fastapi:0.2 .
    minikube image load hello-fastapi:0.2
  3. Создайте configmap.yaml:
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: hello-fastapi-config
    data:
      GREETING: "Привет из ConfigMap"
  4. Создайте secret.yaml. Поле stringData позволяет писать значение обычным текстом — Kubernetes сам закодирует его в base64 при сохранении, вручную кодировать не нужно:
    apiVersion: v1
    kind: Secret
    metadata:
      name: hello-fastapi-secret
    type: Opaque
    stringData:
      API_KEY: "demo-key-12345"

    Это учебное значение для примера — в реальном проекте такой файл с настоящим ключом никогда не попадает в git, ровно по той же причине, по которой в github_pages/ этого курса нет ничего чувствительного.

  5. В deployment.yaml из урока 3 обновите тег образа на 0.2 и добавьте блок envFrom внутрь описания контейнера:
    containers:
      - name: hello-fastapi
        image: hello-fastapi:0.2
        ports:
          - containerPort: 8000
        envFrom:
          - configMapRef:
              name: hello-fastapi-config
          - secretRef:
              name: hello-fastapi-secret
  6. Примените все три манифеста и дождитесь, пока Deployment обновится:
    kubectl apply -f configmap.yaml
    kubectl apply -f secret.yaml
    kubectl apply -f deployment.yaml
    kubectl rollout status deployment/hello-fastapi
  7. Откройте сервис так же, как в уроке 4, и проверьте новый эндпоинт:
    minikube service hello-fastapi --url
    Добавьте к полученному адресу /config — увидите {"greeting": "Привет из ConfigMap", "api_key_is_set": true}.
Проверьте оговорку про base64 Посмотрите на Secret изнутри:
kubectl get secret hello-fastapi-secret -o yaml
В поле data.API_KEY — не осмысленная строка, а base64. Раскодируйте её и убедитесь, что это тот же самый текст, который вы писали в stringData, без какого-либо шифрования:
echo "<значение из data.API_KEY>" | base64 -d
Лайфхак В реальных проектах Secret с боевыми значениями почти никогда не создают вручную через YAML в git — их либо кладут в кластер через отдельный секретный менеджер (Vault, облачный аналог), либо генерируют в CI прямо перед деплоем. Файл secret.yaml из этого урока — учебное исключение: значение в нём фиктивное, и его можно свободно коммитить.

Проверьте себя

В чём главное отличие Secret от ConfigMap на уровне самого Kubernetes (без дополнительной настройки кластера)?

Напишите команду, которая одной строкой применит сразу три манифеста — configmap.yaml, secret.yaml и deployment.yaml:

Напишите команду, которая покажет, завершилось ли обновление Deployment'а hello-fastapi: