ConfigMap, Secret, Namespace: что еще есть в кластере

Тройка с прошлого шага, Pod, Deployment, Service, поднимает приложение и дает к нему достучаться. Но рабочему приложению этого мало. Ему нужны настройки: адреса, режимы, флаги. Нужны секреты: пароль к базе, токен к внешнему сервису. А когда в одном кластере живут десятки приложений от разных команд, нужен порядок, иначе все смешается в кучу. Эти три задачи закрывают еще три объекта Kubernetes, и разберем мы их в том же порядке, в каком они возникают у живого приложения: сначала настройки, потом секреты, потом наведение порядка.

ConfigMap выносит настройки из образа

Любому приложению нужна конфигурация: уровень логирования, адрес базы, включенные функции. Зашивать ее прямо в образ плохая идея, и вы уже знаете почему. В модуле про Docker мы условились, что один и тот же образ едет с машины разработчика на тест и на прод без пересборки. Но настройки на этих окружениях разные. Если конфиг внутри образа, под каждое окружение пришлось бы собирать свой образ, и весь смысл единого артефакта рассыпается.

ConfigMap решает это: настройки хранятся в кластере отдельно от образа, парами ключ-значение.

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  LOG_LEVEL: "info"
  WELCOME_TEXT: "Привет из ConfigMap"

Дальше эти значения подставляются в под, чаще всего как переменные окружения. В манифесте Deployment с прошлого шага это выглядит так:

    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          envFrom:
            - configMapRef:
                name: web-config

Контейнер стартует, и внутри него уже есть переменные LOG_LEVEL и WELCOME_TEXT. Выигрыш тот самый: образ один, а ConfigMap под каждое окружение свой, на тесте один уровень логирования, на проде другой, и пересобирать ничего не нужно. Это та же идея «настройки снаружи, под окружение», что переменные в playbook из модуля про инфраструктуру как код, только теперь она живет в кластере.

Secret для чувствительных данных

Пароли и токены в ConfigMap класть нельзя, для них есть Secret. Устроен он почти так же, пара ключ-значение, и в под подставляется тем же способом:

apiVersion: v1
kind: Secret
metadata:
  name: web-secret
type: Opaque
stringData:
  DB_PASSWORD: "s3cr3t"
          envFrom:
            - secretRef:
                name: web-secret

Зачем отдельный объект, если структура та же? Затем, чтобы кластер мог обращаться с секретами иначе: ограничивать, кому их видно, и не светить их значения где попало. Kubernetes выделяет Secret в отдельный тип именно для этого.

Но здесь важна честность, иначе название обманет. Значения в Secret по умолчанию не зашифрованы, а лишь закодированы в base64. А base64 это не шифрование, а обратимое кодирование: любой, кто получил доступ к Secret, раскодирует его одной командой. Настоящую защиту дают две вещи: ограничение доступа (кто вообще в кластере имеет право читать Secret) и шифрование самой базы кластера. Помните etcd из шага про архитектуру, где лежит вообще все? Secret лежит там же, и шифрование etcd «на диске» включают отдельно, чтобы пароли не хранились открытым текстом.

Отсюда же следует правило из модуля про инфраструктуру как код: манифест Secret с настоящим паролем не коммитят в Git как есть. Секреты в кластер подают защищенно, через внешние хранилища вроде Vault или специальные инструменты, а в репозитории если что и лежит, то в зашифрованном виде. Тема глубокая, на вводном уровне достаточно усвоить, что Secret хранит секреты, но «secret» в его названии про назначение, а не про автоматическую защиту.

Namespace делит кластер

Третья задача это порядок. В реальном кластере крутятся не три объекта, а сотни: несколько команд, несколько окружений, десятки приложений. Если все свалить в общую кучу, начнется хаос: имена сталкиваются (две команды захотели Service с именем web), ничего не разграничить, не понять, где чье.

Namespace делит кластер на логические отсеки, как дом на квартиры. Объекты раскладывают по namespace, по командам, по окружениям, как удобно. И ключевое: имена должны быть уникальны только внутри своего namespace. Service web в namespace dev и Service web в namespace prod мирно сосуществуют, потому что живут в разных квартирах.

По умолчанию объекты попадают в namespace с именем default, а служебные компоненты кластера живут в отдельном kube-system, том самом, где работают системные поды. Указать namespace можно прямо в команде:

kubectl get pods -n dev          # поды в namespace dev
kubectl get pods --all-namespaces  # во всех сразу

Namespace задевает и сеть с прошлого шага. Внутри одного namespace приложение по-прежнему ходит к Service просто по имени, http://web. А чтобы дотянуться до сервиса в соседнем namespace, к имени добавляют его: http://web.prod. На namespace же навешивают и более взрослые вещи, ограничение прав по командам и квоты на ресурсы, но это уже территория буткемпа.

Как это складывается

Соберем картину. Deployment держит нужное число подов, Service дает к ним постоянный адрес, ConfigMap отдает им настройки, Secret отдает пароли, и все это лежит внутри своего namespace.

Связаны объекты по-прежнему не ссылками, а по именам и лейблам: Deployment тянет ConfigMap и Secret по их именам, Service находит поды по стикеру. Шесть объектов, Pod, Deployment, Service, ConfigMap, Secret, Namespace, это уже рабочий комплект, которым описывают настоящее приложение в кластере.

Причем здесь DevOps

Ровно эту шестерку девопс и пишет в манифестах изо дня в день. ConfigMap выносит настройки наружу, чтобы один образ работал в любом окружении. Secret хранит пароли отдельно от кода и образа. Namespace разводит команды и окружения по отсекам, чтобы dev не мешал prod и чтобы права можно было выдавать по квартирам. Все это, как и тройка с прошлого шага, лежит YAML-манифестами в Git и едет в кластер через пайплайн, тот самый стек, что прошли, который к этому моменту сошелся в одно целое.

Словарик

ТерминЧто означает
ConfigMapНастройки приложения (ключ-значение), хранятся в кластере отдельно от образа
SecretТо же для секретов: паролей, токенов, ключей
base64Обратимое кодирование; Secret по умолчанию закодирован, но не зашифрован
NamespaceЛогический отсек кластера; имена уникальны только внутри него
defaultNamespace, куда объекты попадают по умолчанию
kube-systemNamespace со служебными компонентами кластера
envFromСпособ подставить ConfigMap или Secret в под как переменные окружения