Learning Platform
Глоссарий Troubleshooting
Урок 16.01 · 20 мин
Средний
kubectl logscontainer runtimeCRIloggingkubeletlog rotation

Логи: kubectl logs deep

kubectl logs — это первая команда, которую вызываешь, когда что-то сломалось. Снаружи она выглядит просто: kubectl logs my-pod. На деле — это thin client поверх длинной цепочки: apiserver → kubelet → container runtime → файл на ноде → SPDY/WebSocket stream обратно. Понимать эту цепочку важно, потому что её слабые звенья определяют, какие логи ты увидишь, а какие — потеряны навсегда.


journalctl: единый журнал systemd

Что делает kubectl logs под капотом

Когда ты вызываешь kubectl logs <pod>, происходит следующее:

  • kubectl делает GET /api/v1/namespaces/<ns>/pods/<name>/log — это subresource Pod-а, не отдельный объект.
  • apiserver проксирует запрос на kubelet ноды, где запущен Pod (endpoint /containerLogs/<ns>/<pod>/<container> через kubelet API).
  • kubelet не разговаривает напрямую с приложением. Он читает файлы с диска ноды, которые туда положил container runtime.
  • На path-е /var/log/pods/<ns>_<pod>_<uid>/<container>/0.log лежат stdout+stderr контейнера в CRI log format (<timestamp> <stream> <P|F> <message>).
  • kubelet парсит эти файлы, фильтрует по --since, --tail и стримит обратно.
Путь kubectl logs от клиента до файла на ноде
kubectlЛокальная утилита. GET /api/v1/namespaces/<ns>/pods/<name>/log. Это subresource Pod-а — отдельной CRD для логов нет.
kube-apiserverПринимает запрос, проверяет RBAC (verb=get, resource=pods/log). Находит, на какой ноде запущен Pod (spec.nodeName), прокидывает запрос на kubelet через node lookup.
kubeletЗапрос приходит на endpoint /containerLogs/<ns>/<pod>/<container>. kubelet НЕ обращается к container runtime для чтения логов — он сам читает файлы CRI log format с диска.
/var/log/podsСтруктура: /var/log/pods/<ns>_<pod>_<uid>/<container>/0.log. После ротации появляется 1.log, 2.log. CRI log format: один JSON-like line per stdout/stderr line.
container runtimecontainerd / CRI-O пишет stdout+stderr контейнера в файл. Дальше kubelet читает этот файл — runtime больше не участвует в чтении логов.
приложениеПриложение пишет в stdout/stderr. Это перехватывается runtime через pipe. Если приложение пишет напрямую в файл внутри контейнера — kubectl logs этого не покажет.
WARNING

Если ваше приложение пишет логи в файл внутри контейнера (например, /var/log/app.log) — kubectl logs их не увидит. CRI собирает только stdout и stderr. Это первая ошибка миграции legacy-приложений в K8s: переводите логи на stdout либо настройте sidecar для tail-ирования файла.


Ключевые флаги

# Базовое
kubectl logs my-pod

# Multi-container Pod
kubectl logs my-pod -c app          # конкретный container
kubectl logs my-pod -c init-db      # init-container логи (только после init)
kubectl logs my-pod --all-containers  # все containers вместе
kubectl logs my-pod/app             # сокращение для -c app

# Время
kubectl logs my-pod --since=10m
kubectl logs my-pod --since-time=2026-05-13T10:00:00Z
kubectl logs my-pod --tail=100
kubectl logs my-pod --timestamps

# Stream и предыдущий instance
kubectl logs -f my-pod              # follow (как tail -f)
kubectl logs my-pod --previous      # логи предыдущего container instance
kubectl logs my-pod -p              # сокращение --previous

# По selector — у всех matching Pods
kubectl logs -l app=web --tail=50 --prefix=true
  • -c <container> нужен всегда в multi-container Pod, иначе kubectl откажется работать без default-а.
  • --all-containers=true объединяет вывод. Без --prefix непонятно, какой контейнер пишет — ставьте --prefix=true.
  • -l <selector> стримит из всех Pods, чьи labels matches. Удобно для Deployment: kubectl logs -l app=web -f --max-log-requests=10.

--previous: единственный шанс увидеть, почему контейнер упал

Когда контейнер крашится и kubelet рестартует его, старый файл логов перезаписывается новым. К этому моменту kubelet сохраняет предыдущий — но только один. Это значит:

  • Контейнер упал → старые логи доступны через kubectl logs -p.
  • Контейнер упал, рестартовал, упал второй раз → старые-старые логи потеряны навсегда. -p покажет только последний крашнутый instance, не оригинальный.
DANGER

Если Pod в CrashLoopBackOff и ты ничего не делаешь — kubelet продолжает рестартовать, и каждый рестарт затирает логи предыдущего краша. Сразу делай kubectl logs <pod> -p > crash.log, иначе оригинальная причина крэша уйдёт в небытие.


Лимиты ротации и почему нужен centralized logging

Container runtime ротирует логи. У containerd дефолты:

  • Размер файла — 10 MB.
  • Количество файлов на контейнер — 5.
  • Итого: максимум 50 MB на контейнер на ноде.

После этого старые логи удаляются runtime-ом. kubectl logs --tail=10000 ограничен только тем, что осталось в неротированных файлах. Для production это категорически недостаточно — нужен centralized logging:

  • Fluent Bit / Fluentd DaemonSet на каждой ноде читает /var/log/pods/* и отправляет в backend.
  • Backend: Loki (cheap, label-based, integration с Grafana), Elasticsearch / OpenSearch (full-text search), Cloud-native (CloudWatch, Stackdriver).
  • Альтернативный шаблон — sidecar logger: контейнер fluent-bit в Pod-е читает stdout другого контейнера через shared emptyDir.
TIP

Для CKAD достаточно знать kubectl logs со всеми флагами и понимать, что --previous работает только на одно поколение. Production-grade logging (Loki, Elastic) — больше CKA / SRE scope, но появляется в реальных задачах постоянно.


Init-контейнеры и их логи

Init-containers выполняются до запуска основных containers Pod-а. Их логи доступны как обычно — через -c:

kubectl logs my-pod -c db-migration
kubectl logs my-pod -c wait-for-redis

Если init упал — Pod в Init:CrashLoopBackOff. kubectl logs my-pod -c <init-name> --previous покажет, почему init упал в прошлый раз. Без -c kubectl откажется — init-containers не считаются default-ом для log-команды.


Killer-моменты

  • --previous работает только один раз. После второго краша оригинальная причина потеряна.
  • Файл логов на ноде/var/log/pods/<ns>_<pod>_<uid>/<container>/0.log. На kind / minikube node-логи доступны через docker exec / kind get nodes.
  • kubelet, не runtime, читает логи. Если runtime восстановится, а kubelet нет — kubectl logs сломан, хотя контейнеры работают.
  • stdout/stderr — единственный source of truth для kubectl logs. Файлы внутри контейнера не видны.
  • Селектор -l — must-have для отладки Deployment: kubectl logs -l app=web --tail=20 --prefix=true | grep ERROR.

Проверка знанийKnowledge check
Pod в CrashLoopBackOff: контейнер падает каждые 30 секунд. Ты запускаешь kubectl logs <pod> --previous через 10 минут после первого падения. Что увидишь?
ОтветAnswer
Увидишь логи предыдущего instance — но это уже НЕ оригинальный крах. За 10 минут произошло несколько рестартов, и каждый затирал previous буфер. Ты видишь только последний предыдущий instance, не первый. Правильный workflow: при первом обнаружении CrashLoopBackOff сразу сохранять логи (kubectl logs <pod> -p > crash.log) и копать дальше. Долгосрочно — centralized logging (Loki / Elastic), который собирает stdout всех контейнеров через Fluent Bit DaemonSet.
Проверка знанийKnowledge check
Приложение пишет логи в /var/log/app.log внутри контейнера. kubectl logs ничего не показывает. Почему и как чинить?
ОтветAnswer
kubectl logs читает stdout и stderr контейнера через CRI log format на ноде. Файлы внутри контейнера CRI не трогает. Способы починить: (1) Перенастроить app писать в stdout/stderr (самое правильное — cloud-native way); (2) Sidecar-контейнер в том же Pod с shared emptyDir, который делает tail -F /shared/app.log и пишет результат в свой stdout — теперь kubectl logs <pod> -c log-tailer показывает; (3) Fluent Bit DaemonSet, читающий путь напрямую с ноды.
Проверка знанийKnowledge check
Что делает kubectl logs my-pod --all-containers и почему --prefix=true почти всегда обязателен с этим флагом?
ОтветAnswer
--all-containers объединяет stdout/stderr ВСЕХ containers (включая init и sidecars) в один поток. Без --prefix=true строки идут вперемешку без указания источника — невозможно понять, какой контейнер написал какую строку. С --prefix=true каждая строка начинается с [pod/<name>/<container>], как у kubectl logs -l ... --prefix=true для нескольких Pods.
Проверка знанийKnowledge check
kubelet вышел из строя на ноде, но containerd жив и контейнеры работают. Что произойдёт с kubectl logs?
ОтветAnswer
kubectl logs сломается. Несмотря на то, что containerd продолжает писать stdout в /var/log/pods, именно kubelet принимает HTTP-запрос от apiserver и читает эти файлы. Без kubelet нет endpoint /containerLogs — apiserver не может проксировать. Но при этом файлы продолжают писаться, и если зайти на ноду напрямую (SSH или kubectl debug node) — логи можно прочитать. Это часто используется для disaster recovery, когда плоскость управления частично отказала.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. kubectl logs my-pod --previous работает после первого крэша контейнера. Pod рестартовал второй раз. Какие логи покажет --previous теперь?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5