Визуальное руководство по диагностике неисправностей в Kubernetes
Прим. перев.: Эта статья входит в состав опубликованных в свободном доступе материалов проекта learnk8s, обучающего работе с Kubernetes компании и индивидуальных администраторов. В ней Daniele Polencic, руководитель проекта, делится наглядной инструкцией о том, какие шаги стоит предпринимать в случае возникновения проблем общего характера у приложений, запущенных в кластере K8s.
TL;DR: вот схема, которая поможет вам отладить deployment в Kubernetes:
Блок-схема для поиска и исправления ошибок в кластере. В оригинале (на английском) она доступна в PDF и как изображение.
При развертывании приложения в Kubernetes обычно необходимо определить три компонента:
Deployment — это некий рецепт по созданию копий приложения, называемых pod’ами;
Service — внутренний балансировщик нагрузки, распределяющий трафик по pod’ам;
Ingress — описание того, как трафик будет попадать из внешнего мира к Service’у.
Вот краткое графическое резюме:
1) В Kubernetes приложения получают трафик из внешнего мира через два слоя балансировщиков нагрузки: внутренний и внешний.
2) Внутренний балансировщик называется Service, внешний – Ingress.
3) Deployment создает pod’ы и следит за ними (они не создаются вручную).
Предположим, вы хотите развернуть простенькое приложение а-ля Hello World. YAML-конфигурация для него будет выглядеть следующим образом:
А как насчет лейбла track: canary в верхней части раздела Deployment? Должен ли он совпадать?
Этот лейбл относится к развертыванию, и не используется сервисом для маршрутизации трафика. Другими словами, его можно удалить или присвоить другое значение.
А что насчет селектора matchLabels?
Он всегда должен совпадать с лейблами Pod’а, поскольку используется Deployment’ом для отслеживания pod’ов.
Предположим, что вы внесли верные правки. Как их проверить?
Проверить лейбл pod’ов можно следующей командой:
kubectl get pods --show-labels
Или, если pod’ы принадлежат нескольким приложениям:
kubectl get pods --selector any-name=my-app --show-labels
Где any-name=my-app — это лейбл any-name: my-app.
Остались сложности?
Можно подключиться к pod’у! Для этого надо использовать команду port-forward в kubectl. Она позволяет подключиться к сервису и проверить соединение.
service/<service name> — имя сервиса; в нашем случае это my-service;
3000 — порт, который требуется открыть на компьютере;
80 — порт, прописанный в поле port сервиса.
Если удалось установить соединение, значит настройки верны.
Если соединение установить не удалось, значит проблема с лейблами или порты не совпадают.
Связь Service’а и Ingress’а
Следующий шаг в обеспечении доступа к приложению связан с настройкой Ingress’а. Ingress должен знать, как отыскать сервис, затем найти pod’ы и направить к ним трафик. Ingress находит нужный сервис по имени и открытому порту.
В описании Ingress и Service должны совпадать два параметра:
servicePort в Ingress должен совпадать с параметром port в Service;
serviceName в Ingress должно совпадать с полем name в Service.
Следующая схема подводит итог по подключению портов:
1) Как вы уже знаете, Service слушает некий port:
2) У Ingress’а есть параметр, называемый servicePort:
3) Этот параметр (servicePort) всегда должен совпадать с port в определении Service:
4) Если в Service задан порт 80, то необходимо, чтобы servicePort также был равен 80:
На практике необходимо обращать внимание на следующие строки:
Теперь каждый раз, когда вы будете посылать запрос на порт 3000 на компьютере, он будет перенаправляться на порт 80 pod’а с контроллером Ingress. Перейдя на http://localhost:3000, вы должны будете увидеть страницу, созданную приложением.
Резюме по портам
Давайте еще раз вспомним о том, какие порты и лейблы должны совпадать:
Селектор в определении Service должен совпадать с лейблом pod’а;
targetPort в определении Service должен совпадать с containerPort контейнера внутри pod’а;
port в определении Service может быть любым. Разные сервисы могут использовать один и тот же порт, поскольку у них разные IP-адреса;
servicePort Ingress’а должен совпадать с port в определении Service;
Название сервиса должно совпадать с полем serviceName в Ingress’е.
Увы, недостаточно знать, как правильно структурировать YAML-конфигурацию.
Что случается, когда что-то идет не так?
Возможно, pod не запускается или он падает.
3 шага для диагностики неисправностей в приложениях в Kubernetes
Прежде чем приступать к отладке deployment’а, необходимо иметь хорошее представление о том, как работает Kubernetes.
Поскольку в каждом выкаченном в K8s приложении имеются три компонента, проводить их отладку следует в определенном порядке, начиная с самого низа.
Сначала надо убедиться, что pod’ы работают, затем…
Проверить, поставляет ли сервис трафик pod’ам, а потом…
Проверить, правильно ли настроен Ingress.
Визуальное представление:
1) Начинать поиск проблем следует с самого низа. Сперва проверьте, что pod’ы имеют статусы Ready и Running:
2) Если pod’ы готовы (Ready), следует выяснить, распределяет ли сервис трафик между pod’ами:
3) Наконец, нужно проанализировать связь сервиса и Ingress’а:
1. Диагностика pod’ов
В большинстве случаев проблема связана с pod’ом. Убедитесь, что pod’ы значатся как Ready и Running. Проверить это можно с помощью команды:
kubectl get pods
NAME READY STATUS RESTARTS AGE
app1 0/1 ImagePullBackOff 0 47h
app2 0/1 Error 0 47h
app3-76f9fcd46b-xbv4k 1/1 Running 1 47h
В выводе команды, приведенном выше, последний pod значится как Running и Ready, однако для двух других это не так.
Как понять, что пошло не так?
Есть четыре полезные команды для диагностики pod’ов:
kubectl logs <имя pod'а> позволяет извлечь логи из контейнеров в pod’е;
kubectl describe pod <имя pod'а> позволяет просмотреть список событий, связанных с pod’ом;
kubectl get pod <имя pod'а> позволяет получить YAML-конфигурацию pod’а, хранящуюся в Kubernetes;
kubectl exec -ti <имя pod'а> bash позволяет запустить интерактивную командную оболочку в одном из контейнеров pod’а
Какую из них выбрать?
Дело в том, что нет универсальной команды. Следует использовать их комбинацию.
Типичные проблемы pod’ов
Существует два основных типа ошибок pod’ов: ошибки во время запуска (startup) и ошибки во время работы (runtime).
Ошибки запуска:
ImagePullBackoff
ImageInspectError
ErrImagePull
ErrImageNeverPull
RegistryUnavailable
InvalidImageName
Runtime-ошибки:
CrashLoopBackOff
RunContainerError
KillContainerError
VerifyNonRootError
RunInitContainerError
CreatePodSandboxError
ConfigPodSandboxError
KillPodSandboxError
SetupNetworkError
TeardownNetworkError
Некоторые ошибки встречаются чаще, чем другие. Вот несколько наиболее распространенных ошибок и способы их устранения.
ImagePullBackOff
Эта ошибка появляется, когда Kubernetes не может получить образ для одного из контейнеров pod’а. Вот три самых распространенных причины этого:
Неверно указано имя образа — например, вы сделали в нем ошибку, или образ не существует;
Указан несуществующий тег для образа;
Образ хранится в закрытом реестре, и у Kubernetes нет полномочий для доступа к нему.
Первые две причины устранить легко — достаточно поправить имя образа и тег. В случае последней необходимо внести учетные данные к закрытому реестру в Secret и добавить ссылки на него в pod’ы. В документации Kubernetes есть пример того, как это можно сделать.
CrashLoopBackOff
Kubenetes выводит ошибку CrashLoopBackOff, если контейнер не может запуститься. Обычно такое случается, когда:
В приложении есть ошибка, которая не позволяет его запустить;
Тест Liveness завершился неудачно слишком много раз.
Необходимо попытаться добраться до логов из контейнера, чтобы выяснить причину его сбоя. Если получить доступ к логам затруднительно, поскольку контейнер слишком быстро перезапускается, можно воспользоваться следующей командой:
kubectl logs <pod-name> --previous
Она выводит сообщения об ошибках из предыдущей реинкарнации контейнера.
RunContainerError
Эта ошибка возникает, когда контейнер не в состоянии запуститься. Она соответствует моменту до запуска приложения. Обычно ее причиной является неправильная настройка, например:
попытка примонтировать несуществующий том, такой как ConfigMap или Secrets;
попытка примонтировать том типа read-only как read-write.
Для анализа подобных ошибок хорошо подходит команда kubectl describe pod <pod-name>.
Pod’ы в состоянии Pending
После создания pod остается в состоянии Pending.
Почему такое происходит?
Вот возможные причины (я исхожу из предположения, что планировщик работает нормально):
В кластере недостаточно ресурсов, таких как вычислительная мощность и память, для запуска pod’а.
В соответствующем пространстве имен установлен объект ResourceQuota и создание pod’а приведет к тому, что пространство имен выйдет за пределы квоты.
Pod привязан к Pending PersistentVolumeClaim.
В этом случае рекомендуется воспользоваться командой kubectl describe и проверить раздел Events:
kubectl describe pod <pod name>
В случае ошибок, связанных с ResourceQuotas, рекомендуется просмотреть логи кластера с помощью команды
kubectl get events --sort-by=.metadata.creationTimestamp
Pod’ы не в состоянии Ready
Если pod значится как Running, но не находится в состоянии Ready, значит проверка его готовности (readiness probe) завершается неудачно.
Когда такое происходит, pod не подключается к сервису, и трафик на него не поступает. Сбой теста readiness вызван проблемами в приложении. В этом случае для поиска ошибки необходимо проанализировать раздел Events в выводе команды kubectl describe.
2. Диагностика сервисов
Если pod’ы значатся как Running и Ready, но ответа от приложения по-прежнему нет, следует проверить настройки сервиса.
Сервисы занимаются маршрутизацией трафика к pod’ам в зависимости от их лейблов. Поэтому первое, что нужно сделать — проверить, сколько pod’ов работают с сервисом. Для этого можно проверить endpoint’ы в сервисе:
kubectl describe service <service-name> | grep Endpoints
Endpoint — это пара значений вида <IP-адрес:порт>, и в выводе должна присутствовать хотя бы одна такая пара (то есть с сервисом работает хотя бы один pod).
Если раздел Endpoins пуст, возможны два варианта:
нет ни одного pod’а с правильным лейблом (подсказка: проверьте, правильно ли выбран namespace);
есть ошибка в лейблах сервиса в селекторе.
Если вы видите список endpoint’ов, но по-прежнему не можете получить доступ к приложению, то вероятным виновником является ошибка в targetPort в описании сервиса.
Как проверить работоспособность сервиса?
Независимо от типа сервиса, можно использовать команду kubectl port-forward для подключения к нему:
Однако вы по-прежнему не можете «достучаться» до приложения.
Это означает, что, скорее всего, неправильно настроен контроллер Ingress. Поскольку контроллер Ingress является сторонним компонентом в кластере, существуют различные методы отладки в зависимости от его типа.
Но прежде чем прибегнуть к помощи специальных инструментов для настройки Ingress’а, можно сделать нечто совсем простое. Ingress использует serviceName и servicePort для подключения к сервису. Необходимо проверить, правильно ли они настроены. Сделать это можно с помощью команды:
kubectl describe ingress <ingress-name>
Если столбец Backend пуст, высока вероятность ошибки в конфигурации. Если бэкэнды на месте, но доступа к приложению по-прежнему нет, то проблема может быть связана с:
настройками доступности Ingress’а из публичного интернета;
настройками доступности кластера из публичного интернета.
Выявить проблемы с инфраструктурой можно, подключившись напрямую к pod’у Ingress’а. Для этого сначала найдите pod Ingress-контроллера (он может находиться в другом пространстве имен):
Теперь все запросы на порт 3000 на компьютере будут перенаправляться на порт 80 pod’а.
Работает ли он теперь?
Если да, то проблема с инфраструктурой. Необходимо выяснить, как именно осуществляется маршрутизация трафика в кластер.
Если нет, то проблема с контроллером Ingress.
Если не получается заставить работать контроллер Ingress, придется провести его отладку.
Существует много разновидностей контроллеров Ingress. Самыми популярными являются Nginx, HAProxy, Traefik и др. (подробнее о существующих решениях см. в нашем обзоре — прим. перев.) Следует воспользоваться руководством по устранению неполадок в документации соответствующего контроллера. Поскольку Ingress Nginx является самым популярным контроллером Ingress, мы включили в статью несколько советов по решению связанных с ним проблем.
Отладка контроллера Ingress Nginx
У проекта Ingress-nginx имеется официальный плагин для kubectl. Команду kubectl ingress-nginx можно использовать для:
kubectl ingress-nginx backend — исследует бэкэнд (по аналогии с kubectl describe ingress <ingress-name>);
kubectl ingress-nginx logs — проверяет логи.
Обратите внимание: в некоторых случаях может потребоваться указать правильное пространство имен для контроллера Ingress с помощью флага --namespace <name>.
Резюме
Диагностика в Kubernetes может оказаться непростой задачей, если не знать, с чего начать. К проблеме всегда следует подходить по принципу «снизу-вверх»: начинайте с pod’ов, а затем переходите к сервису и Ingress’у. Методы отладки, описанные в статье, могут применяться и к другим объектам, таким как: