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 | Логический отсек кластера; имена уникальны только внутри него |
| default | Namespace, куда объекты попадают по умолчанию |
| kube-system | Namespace со служебными компонентами кластера |
| envFrom | Способ подставить ConfigMap или Secret в под как переменные окружения |