ConfigMap не найден
Разбор CreateContainerConfigError: где Pod ссылается на внешние объекты и почему после создания ConfigMap перезапуск не нужен.
Образ скачался, нода нашлась, а контейнер всё равно не стартует:
NAME READY STATUS RESTARTS AGE
webapp 0/1 CreateContainerConfigError 0 45s
Статус длинный и на вид пугающий, но означает простую вещь: Pod ссылается на объект, которого в кластере нет. Обычно это ConfigMap - объект с конфигурацией в виде пар ключ-значение.
CreateContainerConfigError и соседние ошибки
Статус похож на ImagePullBackOff, но ломается совсем другой шаг.
ImagePullBackOff про сам образ. CreateContainerConfigError про то,
что spec ссылается на несуществующие ConfigMap, Secret или PVC.
| Статус | На каком шаге | Кто виноват |
|---|---|---|
ImagePullBackOff | pull образа | runtime или registry |
CreateContainerConfigError | подготовка контейнера | не найден ConfigMap, Secret или PVC |
CreateContainerError | создание контейнера | сломался runtime, например ошибка в command |
RunContainerError | запуск процесса | образ битый, ENTRYPOINT падает |
Все четыре сидят в состоянии Waiting, отличается только reason. Слово
Config в reason это подсказка: искать надо ссылки на внешние объекты
в spec.
Куда Pod может ссылаться
В spec контейнера есть несколько мест, где ссылка идёт по имени:
spec:
containers:
- name: app
image: ...
env:
- name: DB_URL
valueFrom:
configMapKeyRef:
name: db-config # ← ссылка на ConfigMap
key: url
- name: API_TOKEN
valueFrom:
secretKeyRef:
name: api-secret # ← ссылка на Secret
key: token
envFrom:
- configMapRef:
name: app-config # ← ссылка на ConfigMap
- secretRef:
name: db-creds # ← ссылка на Secret
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
configMap: # ← ссылка на ConfigMap
name: static-files
- name: secrets
secret: # ← ссылка на Secret
secretName: tls-cert
- name: storage
persistentVolumeClaim: # ← ссылка на PVC
claimName: app-data
Любая из этих ссылок роняет старт контейнера, если объекта нет.
Что показывает диагностика
kubectl describe pod webapp
В секции State:
State: Waiting
Reason: CreateContainerConfigError
В Events:
Events:
Type Reason Message
Normal Pulled Successfully pulled image
Warning Failed Error: configmap "app-config" not found
Ровно одна строка с именем объекта, и этого достаточно для починки. Если ссылок несколько, в сообщении будет имя того объекта, который kubelet не смог найти первым. Контейнер не стартует, пока все ссылки не зарезолвятся.
Опечатка в имени даёт ровно ту же ошибку, что и полное отсутствие объекта.
Первым делом имя из Events сверяют со списком kubectl get configmap.
Ещё две команды, которые сокращают разбор:
# На что ссылается Pod
kubectl get pod webapp -o jsonpath='{.spec.containers[0].envFrom}'
# Что реально есть в namespace
kubectl get configmap
optional, чтобы не падать
Ссылку можно отметить как необязательную:
envFrom:
- configMapRef:
name: app-config
optional: true
Тогда при отсутствии ConfigMap контейнер стартует без этих переменных. Подходит для конфигурации, которой может и не быть.
Значение по умолчанию другое: optional: false. Pod будет ждать
объект, и это правильное поведение для обязательных настроек. Молча
стартовать без пароля к базе хуже, чем не стартовать вовсе.
Когда контейнер увидит новый ConfigMap
Зависит от способа ссылки:
| Способ | Когда меняется |
|---|---|
env / envFrom | при запуске контейнера, дальше значения зафиксированы |
volume с configMap | kubelet периодически синхронизирует, примерно раз в минуту |
монтирование через subPath | не обновляется без пересоздания Pod |
Отсюда неочевидное следствие. При envFrom переменные фиксируются на
старте, но у Pod в статусе CreateContainerConfigError контейнер ещё
ни разу не стартовал. kubelet продолжает попытки, и как только ConfigMap
появится, следующая попытка пройдёт. Пересоздавать Pod не нужно.
Другое дело, если Pod давно работает, а ConfigMap изменили. Тогда переменные окружения останутся старыми, и нужен рестарт:
kubectl rollout restart deploy/webapp
Как чинят
Недостающий ConfigMap создают императивно:
# Один ключ
kubectl create configmap app-config --from-literal=MODE=production
# Несколько ключей
kubectl create configmap app-config \
--from-literal=MODE=production \
--from-literal=LOG_LEVEL=info
# Из файла: имя файла станет ключом
kubectl create configmap app-config --from-file=config.json
Через несколько секунд Pod сам перейдёт в Running.
Как не допустить
ConfigMap и Secret применяют раньше, чем Pod, который на них
ссылается. В Kustomize и Helm это делается порядком ресурсов, в
обычном kubectl apply -f ./manifests/ порядок алфавитный по имени
файла, поэтому конфигурацию кладут в файл, который идёт раньше.
Для необязательной конфигурации ставят optional: true явно. Для
обязательной оставляют значение по умолчанию, чтобы Pod не поднялся с
пустыми настройками.
Домашка
Теорию закрепляет задание на своём кластере: 1 шаг с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start debug-config # развернуть стартовое состояние
./run.sh verify debug-config # проверить текущий шаг
Коротко
CreateContainerConfigErrorозначает, что spec ссылается на отсутствующий ConfigMap, Secret или PVC.- Имя недостающего объекта kubelet пишет прямым текстом в Events.
- Ссылки живут в
env,envFrom,volumesиpersistentVolumeClaim. optional: trueразрешает старт без объекта, по умолчанию Pod ждёт.- После создания ConfigMap застрявший Pod поднимается сам, а вот
работающий Pod новых значений из
envFromне увидит без рестарта.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс