Задача
Приложение читает настройки из ConfigMap. Конфигурацию обновили:
kubectl edit configmap app-configВ манифесте пода конфигурация подключена двумя способами:
spec:
containers:
- name: app
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: log_level
volumeMounts:
- name: config
mountPath: /etc/app
volumes:
- name: config
configMap:
name: app-configВопрос: что произойдёт с LOG_LEVEL и с файлами в /etc/app после обновления ConfigMap?
Что кажется очевидным
«Kubernetes обновит и то, и другое — может быть, с небольшой задержкой».
Верно только наполовину, и разница принципиальная.
Как это работает на самом деле
Три способа подключить ConfigMap ведут себя по-разному:
| Способ | Обновляется? | Когда |
|---|---|---|
Переменная окружения (env, envFrom) | ❌ Никогда | Только при пересоздании пода |
Том (volumeMounts) | ✅ Да | С задержкой до ~1–2 минут |
Том с subPath | ❌ Никогда | Только при пересоздании пода |
Переменные окружения передаются процессу при его запуске — это свойство самого Linux, а не Kubernetes. Изменить окружение работающего процесса извне нельзя. Значение LOG_LEVEL останется прежним до пересоздания пода.
Тома работают иначе: kubelet периодически синхронизирует содержимое смонтированного ConfigMap. Обновление доходит с задержкой, складывающейся из периода синхронизации kubelet и времени жизни кэша. На практике — обычно до минуты-двух.
subPath — коварная ловушка. Он монтирует конкретный файл, а не каталог, и такой монтаж не обновляется никогда. Это регулярный источник недоумения: «у коллеги обновляется, у меня нет» — разница именно в subPath.
Даже когда файл обновился
Есть второй уровень проблемы: обновление файла не означает, что приложение перечитало конфигурацию. Большинство приложений читают конфиг один раз при старте. Чтобы изменения вступили в силу, приложение должно либо следить за файлом (inotify), либо перечитывать по сигналу (например, SIGHUP).
Атомарность
Полезная деталь реализации: Kubernetes монтирует ConfigMap через симлинки на скрытый каталог с версией. При обновлении создаётся новый каталог и симлинк атомарно переключается — приложение никогда не увидит наполовину обновлённый набор файлов.
Ответ
LOG_LEVEL не изменится никогда — переменные окружения фиксируются при старте процесса. Файлы в /etc/app обновятся в течение примерно минуты (если не использован subPath), но приложение продолжит работать по старой конфигурации, пока не перечитает файлы.
Что с этим делать
Способ 1: перезапуск при изменении (рекомендуемый)
Самый предсказуемый подход — считать конфигурацию частью версии приложения и перевыкатывать под при её изменении. Классический приём — хеш конфигурации в аннотациях пода:
spec:
template:
metadata:
annotations:
checksum/config: '{{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}'Изменился ConfigMap → изменился хеш → изменился шаблон пода → Deployment сам запускает rolling update. Работает в Helm; в Kustomize аналогичный эффект даёт configMapGenerator с суффиксом хеша в имени.
Ручной вариант того же:
kubectl rollout restart deployment/apiПлюс подхода: изменение конфигурации проходит через ту же процедуру, что и выкатка кода, — с rolling update, readiness-проверками и возможностью отката.
Способ 2: горячая перезагрузка
Если перезапуск нежелателен (долгий прогрев, stateful-нагрузка), приложение должно уметь перечитывать конфиг:
- следить за файлом через
inotify; - перечитывать по
SIGHUP; - периодически опрашивать файл.
Для сервисов, которые этого не умеют, существуют вспомогательные инструменты вроде reloader, которые перезапускают Deployment при изменении связанного ConfigMap/Secret.
Практические правила
- Не используйте
subPathдля того, что должно обновляться. Если нужен один файл в каталоге с другим содержимым — монтируйте каталог целиком и используйте симлинки, либо смиритесь с перезапуском. - Секреты ведут себя так же. Обновление Secret через том доходит с задержкой; через
env— не доходит вовсе. Для ротации учётных данных это критично: подумайте заранее, как приложение узнает о новом пароле. - Помните о размере. ConfigMap и Secret хранятся в etcd и ограничены примерно 1.5 МиБ (см. задачу 9). Крупные файлы туда класть нельзя.
immutable: trueдля конфигураций, которые не меняются: это снимает нагрузку с API server (kubelet перестаёт следить за объектом) и защищает от случайной правки. Изменить такой объект можно только пересозданием — что хорошо сочетается с подходом «конфиг = версия».- Проверяйте, что реально лежит в контейнере:BASH
kubectl exec deploy/api -- cat /etc/app/config.yaml kubectl exec deploy/api -- env | grep LOG_LEVEL
Дальше: Задача 14. Куда на самом деле идёт запрос к Service.