Kubernetes
Теория: Отладка и мониторинг
Представьте, три часа ночи. Алерт: «Сервис недоступен». Вы открываете терминал и понимаете, что не знаете, с чего начать. Pod вроде Running, но что-то не работает.
Как думать при диагностике
Хаотичная отладка — потеря времени. «Попробую посмотреть логи... нет, сначала describe... или events?» Так можно крутиться часами.
Вместо этого двигайтесь от общего к частному. Сначала поймите, что происходит в кластере в целом. Потом найдите проблемный компонент. Потом разберитесь, почему он сломался. И только потом лезьте внутрь.
На практике это выглядит так: вы начинаете с событий — они показывают, что произошло недавно. Затем смотрите на Pod'ы — кто из них в проблемном состоянии. Потом describe — почему этот Pod в таком состоянии. Логи расскажут, что думает само приложение. И если всё ещё непонятно — заходите внутрь контейнера и исследуете.
Давайте пройдём этот путь на реальных примерах.
События: что произошло в кластере
События — это «чёрный ящик» Kubernetes. Сюда записывается всё важное: почему Pod не запустился, почему был убит, почему не смог подключить volume. Начинайте диагностику отсюда.
Флаг --sort-by сортирует вывод по указанному полю. В данном случае — по времени создания события. Без него события выводятся в произвольном порядке, и вы не поймёте, что произошло раньше, а что позже.
Вывод покажет хронологию: что происходило в кластере за последний час (события живут около часа, потом удаляются). Ищите строки с Warning — это обычно проблемы.
Если нужны события конкретного Pod:
Флаг --field-selector фильтрует по полям объекта. Здесь мы говорим: «покажи только события, где поле involvedObject.name равно my-pod». Это как WHERE в SQL.
Что означают типичные события
- FailedScheduling — планировщик не смог разместить Pod на узле. Обычно это значит, что на всех узлах не хватает ресурсов (CPU или памяти), или Pod требует узел с определёнными характеристиками (nodeSelector, affinity), а таких узлов нет. Смотрите describe pod — там будет подробнее, чего именно не хватило.
- Failed to pull image — Kubernetes не смог скачать образ контейнера. Либо вы опечатались в имени образа или теге, либо registry недоступен, либо нужна авторизация (imagePullSecrets). Проверьте имя образа в describe pod и попробуйте сделать docker pull вручную.
- OOMKilled — контейнер убит из-за превышения лимита памяти. Приложение попыталось выделить больше памяти, чем разрешено в limits. Либо увеличивайте лимит, либо ищите утечку памяти в приложении.
- Liveness probe failed — проверка живучести не прошла. Kubernetes считает, что приложение зависло, и перезапускает контейнер. Проверьте, что endpoint для probe существует и отвечает вовремя. Возможно, нужно увеличить timeout или initialDelaySeconds.
- FailedAttachVolume / FailedMount — не удалось подключить volume. Причин много: PVC не привязан к PV, storage недоступен, volume уже примонтирован к другому Pod (для ReadWriteOnce). Смотрите describe pvc и события PV.
Состояние Pod: что означают статусы
После событий смотрим на Pod'ы:
Вы увидите список Pod и колонку STATUS. Но статус — это только верхушка айсберга. Давайте разберём, что каждый из них означает и что делать.
- Pending — Pod создан, но не запущен. Он ждёт чего-то: свободных ресурсов на узле, подходящего узла по nodeSelector, привязки PVC к PV. Это нормальное состояние в первые секунды, но если Pod висит в Pending минуту и больше — проблема. Смотрите события и describe pod.
- ContainerCreating — Pod назначен на узел, идёт подготовка: скачивается образ, монтируются volumes, выполняются init-контейнеры. Тоже нормально в первые секунды, но если застряло — проверяйте события (обычно проблема с образом или volume).
Running — контейнеры запущены. Но внимание: это не значит, что приложение работает правильно! Pod может быть - Running, но не Ready — это значит, что readiness probe не проходит. Смотрите колонку READY: если там
0/1вместо1/1, приложение не готово принимать трафик. - CrashLoopBackOff — контейнер падает сразу после запуска. Kubernetes пытается перезапустить, но контейнер снова падает. После нескольких попыток Kubernetes начинает ждать всё дольше между перезапусками (backoff). Это серьёзная проблема — приложение не может стартовать. Смотрите логи предыдущего контейнера:
Флаг --previous показывает логи предыдущего экземпляра контейнера — того, который упал. Без этого флага вы увидите логи текущего контейнера, который, возможно, ещё не успел ничего написать перед падением.
ImagePullBackOff — не удаётся скачать образ. Kubernetes пробует снова и снова с увеличивающимися интервалами. Проверьте имя образа и тег в describe pod, убедитесь, что registry доступен.
OOMKilled — контейнер убит из-за превышения лимита памяти. Вы увидите это в describe pod в секции Last State. Увеличьте лимит или разберитесь, почему приложение ест столько памяти.
Evicted — Pod вытеснен с узла. Это происходит, когда на узле заканчиваются ресурсы (диск, память) и kubelet начинает «выселять» Pod'ы, чтобы спасти узел. Evicted Pod'ы нужно удалять вручную, они не восстанавливаются автоматически.
Для более подробной информации добавьте флаг -o wide:
Это покажет дополнительные колонки: на каком узле запущен Pod, его IP-адрес, сколько раз перезапускался. Если RESTARTS больше нуля — контейнер падал и перезапускался, это повод разобраться.
Describe: глубокий анализ
Когда вы нашли проблемный Pod, пора понять, что с ним не так:
Вывод большой, но смотреть нужно на конкретные секции.
Conditions — состояние Pod как набор условий:
Если PodScheduled = False, Pod не смог разместиться на узле (см. события). Если Ready = False при Running статусе — readiness probe не проходит. Conditions — это быстрый способ понять, на каком этапе проблема.
Container State — состояние каждого контейнера:
Exit Code — код выхода приложения. 0 означает успешное завершение, любое другое значение — ошибку. Exit Code 137 обычно означает OOMKilled (128 + 9, где 9 — сигнал SIGKILL). Exit Code 1 — общая ошибка приложения, смотрите логи.
Events — события, связанные с этим Pod. Та же информация, что в kubectl get events, но отфильтрованная по конкретному Pod. Смотрите сюда, чтобы понять хронологию проблемы.
Describe работает и для других объектов:
Для Service особенно важна секция Endpoints — это IP-адреса Pod'ов, на которые Service направляет трафик. Если Endpoints пустой, Service не нашёл Pod'ы (проблема с selector).
Логи
События и describe показывают, что происходит с точки зрения Kubernetes. Логи показывают, что происходит с точки зрения приложения.
Если в Pod несколько контейнеров, укажите какой:
Для отслеживания логов в реальном времени (как tail -f):
Флаг -f (follow) держит соединение открытым и показывает новые строки по мере их появления. Нажмите Ctrl+C, чтобы выйти.
Часто нужно посмотреть логи не с начала, а за определённый период:
Флаг --since показывает логи за указанный период: 1h — час, 30m — 30 минут, 2h30m — два с половиной часа. Это удобнее, чем пролистывать тысячи строк.
Можно ограничить количество строк:
Флаг --tail показывает только последние N строк. Полезно, когда логов много и вам нужен только «хвост».
Для поиска ошибок комбинируйте с grep:
Логи нескольких Pod
Если у вас Deployment с несколькими репликами, можно посмотреть логи всех Pod сразу:
Флаг -l (selector) выбирает Pod'ы по метке. Флаг --prefix добавляет имя Pod перед каждой строкой, чтобы вы понимали, откуда она.
Учтите, что это агрегирование может быть шумным: если у вас 10 реплик, логи смешаются. Для серьёзного анализа логов используйте централизованные системы (об этом в конце урока).
Логи упавшего контейнера
Если контейнер в CrashLoopBackOff, обычный kubectl logs может показать пустоту — контейнер падает быстрее, чем пишет логи. Используйте:
Это покажет логи предыдущего экземпляра контейнера — того, который уже упал. Часто именно там находится сообщение об ошибке.
Исследование изнутри: exec и debug
Иногда нужно зайти внутрь контейнера: проверить файлы, переменные окружения, сетевую связность.
Флаг -i (interactive) держит stdin открытым. Флаг -t (tty) создаёт терминал. Вместе они дают интерактивную сессию. После -- идёт команда, которую нужно выполнить в контейнере.
Если в контейнере нет bash (alpine-образы, минималистичные образы), попробуйте sh:
Можно выполнить одну команду без интерактивной сессии:
Что проверять внутри контейнера
Переменные окружения — здесь могут быть неправильные значения из ConfigMap или Secret:
Сетевые соединения — слушает ли приложение на нужном порту:
Важно: приложение должно слушать на 0.0.0.0:порт, а не на 127.0.0.1:порт. Если слушает на localhost, другие Pod'ы не смогут подключиться.
DNS — работает ли резолвинг имён:
Связность с другими сервисами:
Отладка контейнеров без shell
Некоторые образы (distroless, scratch) не содержат ни bash, ни sh, ни других утилит. Kubernetes позволяет подключить отладочный контейнер:
Эта команда создаёт временный (ephemeral) контейнер с образом busybox в том же Pod и подключает его к namespace процессов указанного контейнера. Вы сможете видеть процессы основного контейнера и исследовать файловую систему.
Если команда не работает, проверьте версию Kubernetes и настройки кластера — ephemeral containers должны быть включены (в современных версиях включены по умолчанию).
Для сетевой отладки удобен образ nicolaka/netshoot — в нём есть curl, dig, tcpdump, netstat и другие инструменты:
Флаг --rm удалит Pod после выхода. Флаг --restart=Never создаёт просто Pod, а не Deployment.
Сетевые проблемы
Сетевые проблемы — самые коварные. «Не работает» может означать что угодно: DNS не резолвит, Service не находит Pod'ы, NetworkPolicy блокирует трафик.
Проверка Service и Endpoints
Когда сервис недоступен, первым делом проверьте, есть ли у Service endpoints:
Endpoints — это список IP-адресов Pod'ов, на которые Service направляет трафик. Если список пустой, Service не нашёл ни одного Pod'а.
Почему так может быть? Service ищет Pod'ы по selector. Если selector не совпадает с labels Pod'ов — endpoints будут пустыми:
Флаг -o jsonpath позволяет вытащить конкретное поле из объекта. Здесь мы получаем selector сервиса. Сравните его с labels Pod'ов — они должны совпадать.
Ещё одна причина пустых endpoints — Pod'ы не в состоянии Ready. Service направляет трафик только на Ready Pod'ы. Проверьте:
Если в колонке READY стоит 0/1, Pod не готов, и Service его игнорирует.
Проверка DNS
Если endpoints есть, но сервис всё равно недоступен, проверьте DNS. Запустите отладочный Pod:
Внутри проверьте резолвинг:
Если DNS не работает, проверьте CoreDNS:
NetworkPolicy
Если DNS работает, endpoints есть, но соединение не устанавливается — возможно, трафик блокирует NetworkPolicy.
По умолчанию, без NetworkPolicy, весь трафик в Kubernetes разрешён. Но если в namespace есть NetworkPolicy типа default-deny, трафик блокируется, пока вы явно не разрешите его.
Проверьте, есть ли NetworkPolicy:
Если политики есть, убедитесь, что они разрешают нужный трафик (по портам, по labels источника).
Проблемы с ресурсами
Kubernetes может не запустить Pod, если не хватает ресурсов, или убить контейнер, если он превысил лимит памяти.
Проверка использования ресурсов
Для просмотра текущего потребления нужен metrics-server:
Если команда выдаёт ошибку «Metrics API not available», metrics-server не установлен. Это компонент, который собирает метрики с kubelet. В managed-кластерах (EKS, GKE, AKS) он обычно уже есть, в самостоятельных установках нужно ставить отдельно.
Команда kubectl top pods --sort-by=memory отсортирует Pod'ы по потреблению памяти — удобно для поиска «прожорливых».
Почему Pod не размещается
Если Pod в Pending из-за ресурсов, посмотрите, сколько ресурсов доступно на узлах:
Вы увидите что-то вроде:
Если Requests близки к 100%, новые Pod'ы не смогут разместиться — все ресурсы уже «забронированы» существующими Pod'ами.
OOMKilled на практике
Создайте Pod, который превысит лимит памяти:
Примените и подождите 10–20 секунд:
kubectl describe pod memory-hog | grep -A 5 "Last State"
Вы увидите OOMKilled — контейнер запросил 150M памяти при лимите 100Mi и был убит.
Полная диагностика
Давайте соберём всё вместе. Создайте «сломанный» deployment:
Здесь две проблемы: readiness probe обращается к несуществующему endpoint /ready, и selector в Service не совпадает с labels Pod'ов.
Примените и начните диагностику:
Шаг 1 — события:
Вы увидите Readiness probe failed.
Шаг 2 — состояние Pod'ов:
Pod'ы Running, но READY: 0/1.
Шаг 3 — describe:
Ready: False. В Events — Readiness probe failed with statuscode: 404.
Шаг 4 — проверка Service:
Пусто. Даже если бы probe проходил, Service не нашёл бы Pod'ы из-за неправильного selector.
Теперь вы знаете обе проблемы и можете исправить.


