Работа с Kubernetes может серьезно озадачивать, особенно когда дело доходит до отладки и устранения сбоев. Основная сложность кроется в недостатке подробных сообщений об ошибках и сложности самой системы. Все еще больше усложняется огромным числом частей, движущихся в потоке оркестровки контейнеров, который представляется всего несколькими состояниями. Вы, к примеру, увидите, что есть не менее шести веских причин, по которым Pod
может застрять в состоянии ContainerCreating
или CrashLoppBackOff
.
На протяжении уже более трех лет постоянного использования Kubernetes мы то и дело сталкивались с большим количеством проблем. Все эти проблемы в равной степени были трудно уловимы и сложны, хотя большую их часть можно разделить на три основных категории:
- Застревание Pod в состоянии
ContainerCreating
. CrashLoopBackOff
и периодические перезапуски.- Проблемы с сетью.
По ходу статьи вы поймете, что за каждой из перечисленных проблем стоит множество причин, которые мы с вами подробно разберем. Нашей целью будет не снабдить вас списком команд или инструментов, которые могут решить только часть проблем, а повысить уровень понимания этих проблем, чтобы, выработав отчетливую интуицию, вы могли более уверенно решать возникающие сложности при работе с этой платформой.
Как правило, при возникновении проблем чаще всего нужно начинать с изучения журнала событий (kubectl get events -n <NAMESPACE>
). В нем фиксируются детали каждого шага оркестровки, которые и являются ключом к диагностике неполадок.
1. Застревание Pod в состоянии ContainerCreating
Для понимания ContainerCreating
, как и любого другого состояния Пода, главное точно знать его место в общем конвейере оркестровки. Эта информация помогает определить и исключить другие компоненты стека. Например, ContainerCreating
подразумевает, что kube-scheduler
выделил контейнеру рабочий узел и дал демону Docker
команду запустить рабочую нагрузку. Но обратите внимание, что сеть на данной стадии еще не подготовлена — т.е. у рабочей нагрузки нет IP-адреса.
Давайте рассмотрим некоторые наиболее распространенные причины, почему Pod
может застрять на стадии ContainerCreating
.
1.1 Сбой при обнаружении IP-адреса
KubeControlPlane
запрашивает IP от CNI (например, Calico) и, если обнаружить IP не удается, ControlPane
будет просто сидеть и ждать этот IP бесконечно, не делая записей в журнал и не выводя ошибок, при этом kubectl
в терминале тоже ничего не проясняет. Регистрируется же данная проблема только в журналах kubelet
/system
.
Причины:
- Недостаток IP-адресов в
IPPool
, настраиваемом вCNI
. - Ошибки связи между
kubelet
иCNI
. - Ошибки конфигурации в
CNI
.
1.2 Сбой монтирования Configmaps
Configmaps
содержат файлы, которые монтируются на (виртуальную) файловую систему контейнера в среде выполнения. Эта стадия оркестровки логируется в журналах событий.
Причины:
- Заполнение
/var/lib/docker
на узле, что препятствует правильной работе демонаDocker
. - Ошибки/опечатки в имени
Configmap
при обращении во время развертывания Пода.
1.3 Сбой получения PV
Контейнеры, полагающиеся на информацию о состоянии, например базы данных или платформы сообщений, иногда тоже застревают на стадии ContainerCreating
, если не могут смонтировать свой PersistentVolume
через PersistentVolumeClaim
, о чем можно узнать из журналов событий. Более же подробная информация содержится в системных журналах.
Причины:
- Ошибка связи между плагином
CSI
и облачным провайдером (Vsphere, AWS EBS и т.д.). - При перемещении рабочих нагрузок между узлами кластера
ControlPane
для перемещения дисков выполняет процедурыattach
иdetach
. Этот процесс может иногда затягиваться из-за таймаутов во время монтирования и размонтирования.
2. CrashLoopBackOff и периодические перезапуски
CrashLoopBackOff
в первую очередь обозначает сбои в коде контейнера или самого ПО. Это состояние возникает, когда команда entrypoint
в контейнере ошибочно завершается после запуска. У этого сбоя может быть очень много причин, но чаще всего объясняется он следующими.
2.1 Ошибка в скрипте запуска или InitContainers
Помимо ошибок ПО в самом контейнере, вызывать сбой при запуске и вести к CrashLoopBackOff
могут и такие факторы, как внешние сбои при внедрении переменных среды, настройки монтирования, секреты или обращение к другим объектам K8s.
2.2 Превышение лимитов памяти
Если контейнер при запуске или позже в процессе работы превышает лимиты памяти, установленные для Pod
, то Kubernetes сигнализирует о прерывании выполняющегося процесса. Если это происходит часто, то вы скорее всего увидите постепенный рост числа перезапусков Pod
.
2.3 Недостаток пространства на диске или в хранилище
Контейнеры узла могут выдавать сбой из-за недостатка места хранилища в двух местах:
PersistentVolume
вPod
, что может повлиять на процессы контейнера.- В
/var/lib/docker
(OverlayFS
) рабочего узла, где запланированPod
.
2.4 Liveliness probes
Механизм liveliness probe (датчик жизненности) позволяет ControlPane
автоматически перезапускать контейнеры после сбоев, обеспечивая отказоустойчивость. Хотя эти датчики в то же время могут нарушать стабильность, если будут настроены либо интенсивно, либо просто ошибочно без учета отложенных запусков восстановления.
- Интенсивно настроенный
Liveliness
датчик принудит Kubernetes часто выполнять проверки. Но если контейнер, например, находится в середине критического события GC (в случае Java), тогда проверка его жизненности (liveliness) скорее всего провалится и приведет к перезапуску контейнера. - Измененный/отложенный запуск восстановления. Иногда запуск контейнера может затягиваться из-за попыток исправить вызванные сбоем ошибки. Такое часто наблюдается в приложениях с сохранением состояния, которые могут запускать процедуры восстановления для устранения таких проблем, как повреждение файловой системы. Поэтому, если датчик жизненности будет настроен недостаточно гибко, чтобы это допустить, контейнер может застрять в бесконечном цикле перезапусков.
3. Проблемы с сетью
Определяемая ПО сеть Kubernetes достаточно сложна и при отладке проблем подключения может вызывать особые затруднения. Например, сбой одного компонента, особенно в сетевом стеке, может вызвать сбои связи во всем кластере. Это приводит к тому, что вы начинаете искать в разных местах, поэтому правильным подходом будет начать со стратегической оценки наиболее важных точек и сужать круг поиска постепенно.
Когда дело доходит до отладки внутренних сетевых проблем K8s, стоит проработать следующие основные направления.
3.1 Kube-proxy и таблицы IP
Главная назначение kube-proxy
— облегчать взаимодействие между Service IP
и его конечными точками в бэкенд (Pod IP
). Поэтому, если ваш Pod
находится на узле, имеющем сложности с подключением к IP сервиса, и возникают ошибки истечения времени подключения или отказа подключения, проблему зачастую может решить простой перезапуск kube-proxy
, потому что, как и в случае с любой другой рабочей нагрузкой, возможно, контейнер kube-proxy
на этом узле дал сбой.
Ядро IP Table
является основным компонентом маршрутизации трафика между сервисами и Подами в бэкенд, включая сообщение через node-ports
. kube-proxy
задействует таблицу IP-адресов для установки правил, облегчающих балансировку нагрузки и маскировку, в связи с чем эти таблицы нужно смотреть в первую очередь, если ваши Pod
могут связываться с IP других Pod
, но не с Service
.
3.2 Kube-DNS
В случае сбоев при разрешении имен начинать поиск неисправности нужно во внутренней настройке DNS кластера K8s (по умолчанию DNS-сервис — это kubedns
) и убедиться, что этот сервис досягаем через его IP-адрес Cluster
или Pod
.
Далее нужно убедиться, что resolv.conf
в Подах и рабочих узлах содержит необходимые DNS домены и сервер имен для поиска.
3.3 Conntrack
Проблемы в Conntrack
могут вести к вылетам подключений и несогласованности сетевого траффика. Коротко говоря, Conntrack
используется ядром Linux для поддержания состояний подключения и логических потоков в системе. Это значит, что если ваше приложение, например, раскрывает внешний IP, и к нему выполняется миллион подключений, то состояние этих подключений и логические потоки отслеживаются в таблице ядра conntrack
. При этом вполне разумно, что такие таблицы предусматривают жесткие лимиты, и если эти лимиты превышаются, то сеть приходит в беспорядок.
В RHEL лимиты подключений для conntrack
можно проверить так:
$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_count = 167012
net.netfilter.nf_conntrack_max = 262144
3.5 CNI и таблица маршрутизации
Когда daemonset
CNI (например, calico
) терпит сбой на любом из узлов, то он нарушает маршрутизацию и сетевое подключение в кластере K8s, часто локально для подверженного сбою узла/узлов.
Как и в случае с kube-proxy
, проблему может решить либо перезапуск контейнера calico
на этом узле, либо контроллер calico
. Аналогично всем прочим рабочим нагрузкам кластера Поды CNI тоже очень уязвимы для сбоев.
Заключение
Kubernetes — это сложная платформа, отладка которой вызывает массу сложностей. В этой статье мы видели, как всего одно застывшее состояние, например ContainerCreating
, может быть вызвано разными причинами, начиная с не обнаружения IP и заканчивая проблемами монтирования дисков. Обычно это обусловлено огромным числом движущихся частей платформы и их глобальными взаимосвязями.
Мы рассмотрели три категории проблем и проанализировали многие связанные с ними неполадки:
- Застревание Pod в состоянии
ContainerCreating
. CrashLoopBackOff
и периодические перезапуски.- Проблемы с сетью.
Это помогло нам разобраться во внутренних процессах и лучше представить, где стоит искать решение возникающих проблем, которые в мире Kubernetes нередкое явление.
Читайте также:
- Kubernetes: сэкономьте до 50% с вытесняемыми объектами
- Kubernetes: безопасное управление секретами с GitOps
- Практичные Canary-релизы в Kubernetes с Argo Rollouts
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Koal Venkatesh Ganesan: Troubleshooting in Kubernetes: A Strategic Guide