sre-k8s-triage — Диагностика k8s от симптома к причине
Цель: дать структурированный путь от симптома к гипотезе, без бесцельного kubectl-серфинга.
Перед началом — если у пользователя есть домашний runbook на этот сценарий, посмотри его (../../rules/sre-runbook-template.md — структура общая).
Шаг 0. Зафиксируй симптом
Спроси:
- ресурс: Pod / Deployment / Service / Ingress
- namespace, имя
- статус:
kubectl -n <ns> get <res> <name>— что показывает - с какого времени, что изменилось
Если симптом размыт («приложение не работает») — попроси конкретику: ошибка пользователя, HTTP-статус, логи.
Шаг 1. Выбери ветку по симптому
| Симптом / Status | Идти в раздел |
|---|---|
Pod: Pending |
A. Шедулинг |
Pod: CrashLoopBackOff |
B. Падает на старте |
Pod: ImagePullBackOff / ErrImagePull |
C. Образ |
Pod: OOMKilled (в kubectl describe Last State) |
D. Память |
Pod: Running, но не отвечает |
E. Readiness / трафик |
| Service: нет endpoints | F. Селектор |
| Ingress: 502/503/504 | G. Маршрутизация |
| Deployment: rollout stuck | H. Деплой |
A. Pod Pending — шедулинг
kubectl -n <ns> describe pod <pod> | tail -20
Смотри секцию Events. Частые причины:
Insufficient cpu/memory→ нет нод с нужными ресурсами. Решение: понизитьrequests, скейл нод, проверитьkubectl top nodes.0/N nodes are available: ... untolerated taint→ под не толерирует taint. Решение: добавитьtolerationsили unaint ноду.pod has unbound immediate PersistentVolumeClaims→ PVC не provisioned. Проверить StorageClass, провижионер.node(s) didn't match Pod's node affinity/selector→ проверитьnodeSelector/affinity.
B. CrashLoopBackOff — падает на старте
kubectl -n <ns> logs <pod> --previous --tail=100
kubectl -n <ns> describe pod <pod> | grep -A5 'Last State'
Частые причины:
- Exit code 1 + ошибка в логах → ошибка приложения (конфиг, миссинг env-vars, БД недоступна).
- Exit code 137 → OOM, см. раздел D.
- Exit code 139 (SIGSEGV) → segfault, скорее всего баг в коде или libc-несовместимость.
- Liveness probe failure → проверь
livenessProbe. Часто стартап дольше, чемinitialDelaySeconds.
C. ImagePullBackOff
kubectl -n <ns> describe pod <pod> | grep -A3 'Events'
pull access denied→ нет imagePullSecret или истёк токен registry.manifest unknown→ тег не существует. Проверьkubectl -n <ns> get pod <pod> -o jsonpath='{.spec.containers[*].image}'.net/http: TLS handshake timeout→ сетевая проблема к registry с ноды.
D. OOMKilled
kubectl -n <ns> describe pod <pod> | grep -A3 'Last State'
kubectl -n <ns> top pod <pod> --containers
- Текущий
resources.limits.memoryниже реального потребления. - Утечка памяти в приложении.
- Митигация: поднять limit, рестарт. Root cause: профилирование heap'а.
E. Pod Running, не отвечает
Проверь, что readiness probe проходит:
kubectl -n <ns> describe pod <pod> | grep -A5 'Conditions'
kubectl -n <ns> get pod <pod> -o jsonpath='{.status.containerStatuses[*].ready}'
Ready: false→ readiness fails, под вServiceне попадает.Ready: true, но 5xx → проверь приложение:kubectl exec -it <pod> -- <healthcheck-cmd>, логи.
F. Service без endpoints
kubectl -n <ns> get svc <name> -o wide
kubectl -n <ns> get endpoints <name>
kubectl -n <ns> get pods -l <selector-from-svc> --show-labels
- Endpoints пустой → селектор Service не матчит ни один Pod (или ни один не
Ready). - Проверь
kubectl -n <ns> describe svc <name>— раздел Selector.
G. Ingress 502/503/504
- 502 → upstream (Pod) не отвечает. Иди в раздел E.
- 503 → нет endpoints или ingress controller не нашёл backend. Раздел F.
- 504 → timeout от upstream. Проверь
kubectl logsingress-controller'а, таймауты в аннотациях Ingress.
H. Deployment rollout stuck
kubectl -n <ns> rollout status deployment/<name>
kubectl -n <ns> describe deployment <name> | tail -30
kubectl -n <ns> get rs -l app=<name> # ReplicaSet'ы
kubectl -n <ns> get pod -l app=<name> # поды
- Новые поды Pending/Crash — иди по соответствующей ветке.
MinimumReplicasAvailable: False→ стратегия rolling не может продвинуться.- Откат:
kubectl -n <ns> rollout undo deployment/<name>.
Шаг 2. Сформулируй гипотезу
После kubectl-копания скажи пользователю одну гипотезу с обоснованием:
Гипотеза: под не стартует из-за миссинга секрета DATABASE_URL.
Основание: в логах "env DATABASE_URL not set", в манифесте deploy секрет
не приcоединён. Восстановление: добавить envFrom secretRef или ручной
patch.
Если данных мало — назови 2-3 гипотезы и спроси, что проверить первым.
Шаг 3. Митигация vs root cause
Спроси, что важнее сейчас:
- митигация — быстро поднять (rollback, scale, ручной patch) → действуй после подтверждения
- root cause — копать глубже → продолжай диагностику
На production — обычно сначала митигация.
Шаг 4. Покажи план, дождись подтверждения
Перед kubectl delete, rollout undo, любым изменяющим действием — покажи команду, спроси, выполнить ли. Read-only команды (get, describe, logs, top) — можно запускать без подтверждения.
Чего не делать
- Не запускай
kubectl deleteна ресурсах прода без явного подтверждения - Не предполагай namespace — спроси
- Не давай 10 команд подряд, не дождавшись вывода предыдущей: это диагностика, а не сценарий
- Не выдавай одну гипотезу за единственную — k8s-симптомы часто многопричинные