Пропал ConfigMap: подъём сервиса после катастрофы
CreateContainerConfigError, восстановление конфига и почему Pod-ы не чинятся сами.
Кто-то выполнил kubectl delete cm не в том контексте. Через минуту
сервис перестал отвечать, Pod-ы висят в CreateContainerConfigError, а
приложение даже не пыталось стартовать. Это инцидент про recovery:
восстановить конфиг и вернуть трафик, разбор виноватых потом.
Что значит CreateContainerConfigError
Это статус, когда kubelet физически не может создать контейнер из-за
проблем с конфигурацией. Самый частый сценарий: Pod ссылается на
ConfigMap или Secret через envFrom, env.valueFrom.configMapKeyRef
или volumes.configMap, а объект отсутствует.
В описании Pod это видно так:
kubectl describe pod api-xxx
В секции Events:
Warning Failed 10s (x5 over 30s) kubelet Error: configmap "api-config" not found
Принципиально, что Pod не падает в CrashLoopBackOff: он даже не стартовал. CrashLoop это «контейнер запустился и упал», CreateContainerConfigError это «контейнер не создался вообще». Разница важна: логов приложения не будет, смотреть надо события.
Проверка, что объекта правда нет:
kubectl get cm
Как восстановить ConfigMap
Источник значений бывает двух видов. Если конфиг простой и значения известны из документации сервиса, его собирают руками:
kubectl create configmap api-config \
--from-literal=API_PORT=8080 \
--from-literal=API_HOST=0.0.0.0 \
--from-literal=LOG_LEVEL=info
Если бэкап снимали заранее, достаточно применить его:
kubectl apply -f backup/api-config.yaml
Результат один. Объект появится, но Pod-ы он сам не починит.
Почему Pod-ы не поднимаются сами
kubelet читает ConfigMap в трёх ситуациях:
- При создании контейнера: читает объект один раз и формирует env-vars или монтирует volume. Нет ConfigMap - нет контейнера.
- При обновлении смонтированного volume: kubelet обновляет файлы
через свой sync period (по умолчанию 60 секунд). Работает для
volume-mount, но не для
envFrom. - При перезапуске Pod: новый старт читает ConfigMap заново.
Pod-ы, застрявшие в CreateContainerConfigError, в Running так и не
вышли. После появления ConfigMap kubelet попробует создать контейнер на
следующем sync-цикле, и Pod поднимется. Это может занять до минуты.
Ускоряют удалением Pod: ReplicaSet тут же создаст новые, и они увидят ConfigMap при старте.
kubectl delete pods -l app=api
# или
kubectl rollout restart deploy api
Терять нечего: Pod-ы и так в нерабочем состоянии.
envFrom vs volumeMount
| Способ ссылки | Что после восстановления ConfigMap |
|---|---|
envFrom | значения вшиты при старте, нужен рестарт Pod |
volume + volumeMounts | kubelet обновит файлы примерно за минуту |
envFrom:
- configMapRef:
name: api-config
Все ключи ConfigMap вшиваются в env при старте. После старта изменения конфига до работающего контейнера не доедут, нужен рестарт Pod.
volumes:
- name: config
configMap:
name: api-config
volumeMounts:
- name: config
mountPath: /etc/api
Здесь ConfigMap монтируется как файлы. kubelet обновит их автоматически примерно через минуту после изменения объекта. Контейнер должен сам уметь перечитывать конфиг: через SIGHUP, watch на файл, что угодно.
Для recovery первый вариант неприятнее: даже после восстановления ConfigMap приложение продолжает жить со старым env, пока Pod не перезапустят.
Как не наступать снова
Критичные ConfigMap регулярно выгружают:
kubectl get cm api-config -o yaml > backup/api-config.yaml
Дамп чище получается через kubectl-neat или yq, которые снимают
служебные поля:
kubectl get cm api-config -o yaml \
| yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp, .metadata.managedFields)' \
> backup/api-config.yaml
На проде эту роль обычно закрывает GitOps: ConfigMap лежат в
репозитории, кластер тянет их через ArgoCD или Flux. Тогда восстановление
это re-sync, а не ручной apply по памяти. Плюс права на delete в
проде стоит выдавать отдельно от прав на apply.
Полезные команды
kubectl get pods
kubectl describe pod <pod-name>
kubectl get cm
kubectl rollout status deploy api --timeout=120s
kubectl get events --sort-by=.lastTimestamp | tail
kubectl explain pod.spec.containers.envFrom
kubectl explain configmap.data
Домашка
Теорию закрепляет задание на своём кластере: 1 шаг с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start k8s-configmap-disaster # развернуть стартовое состояние
./run.sh verify k8s-configmap-disaster # проверить текущий шаг
Коротко
CreateContainerConfigErrorозначает, что контейнер не создавался; логов приложения не будет, читай Events.- Самая частая причина - отсутствующий ConfigMap или Secret из
envFrom,configMapKeyRefилиvolumes.configMap. - Восстановление объекта не поднимает застрявшие Pod мгновенно: либо ждать sync-цикл kubelet, либо пересоздать Pod.
envFromтребует рестарта Pod, смонтированный volume обновляется сам.- Бэкап критичных ConfigMap или GitOps-репозиторий превращает такую катастрофу в одну команду.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс