kubectl профессионально: get, describe, explain, jsonpath

Рейтинг: 65.7% · 17 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

kubectl профессионально: get, describe, explain, jsonpath

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath (вы здесь)
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps
kubectl - это твой единственный SSH в кластер

Представь дежурство. Прод лежит, в Slack паника, у тебя терминал и боль. Ты не зайдёшь по ssh на ноду чинить руками - в Kubernetes так не делают. У тебя есть ровно один инструмент, через который проходит ВСЁ общение с кластером - kubectl. Это HTTP-клиент к kube-apiserver, не больше и не меньше. Каждая команда kubectl - это REST-запрос к API: GET, POST, PATCH, DELETE по объектам в etcd. Понимаешь это - перестаёшь бояться и начинаешь видеть, что происходит под капотом.

Разница между джуном и сеньором на инциденте часто не в знании архитектуры, а в скорости рук. Джун открывает дашборд, кликает, теряется. Сеньор за 15 секунд набирает три команды kubectl и уже знает, какой под падает, почему и с какого коммита. Этот урок - про то, как из медленного тыканья сделать мгновенную навигацию по кластеру. Разберём kubectl get во всех режимах, kubectl describe с событиями, explain как встроенную документацию, и jsonpath - язык, которым ты выжимаешь из API ровно нужное поле.

Маленькая, но важная вещь сразу. Заведи алиас, иначе сотрёшь клавиатуру:

Код: Выделить всё

alias k=kubectl
# и автодополнение, без него ты слепой:
source <(kubectl completion bash)   # для zsh: kubectl completion zsh
complete -o default -F __start_kubectl k
После этого Tab дополняет имена подов, namespace, поля - это экономит часы за месяц.

Изображение

kubectl get: от списка до точного поля

kubectl get - самая частая команда дня. По умолчанию она показывает урезанную таблицу, которую формирует не клиент, а сам сервер (server-side printing): apiserver отдаёт уже готовые колонки. Поэтому ширины и набор колонок зависят от версии. Базовый вызов:

Код: Выделить всё

$ kubectl get pods
NAME                        READY   STATUS    RESTARTS      AGE
api-7d9f8c5b6-2xk9p         1/1     Running   0             4d2h
api-7d9f8c5b6-jq4lm         0/1     CrashLoopBackOff   7 (2m ago)   18m
worker-5c4d8b9f7-pp7wq      2/2     Running   1 (3h ago)    4d2h
Читай поля как врач кардиограмму. READY 0/1 - контейнер не прошёл readiness-проверку, трафик на него не идёт. RESTARTS 7 (2m ago) - перезапускался семь раз, последний две минуты назад, это сигнал CrashLoop. AGE 18m против 4d2h - под молодой, значит его недавно пересоздали, тут и копай.

Дальше включаем рентген. Флаг -o wide добавляет ноду, IP пода и образ - бесценно, когда подозреваешь конкретную ноду:

Код: Выделить всё

$ kubectl get pods -o wide
NAME                  READY   STATUS    NODE              IP            NOMINATED NODE
api-7d9f8c5b6-2xk9p   1/1     Running   cl-node-fra-03    10.112.4.27   <none>
Ключевые флаги, которые надо вбить в мышечную память:
  • -A (или --all-namespaces) - смотреть по всему кластеру, а не только в текущем namespace. На инциденте почти всегда нужен именно он.
  • -w - watch, поток событий в реальном времени. Открой в соседнем окне kubectl get pods -w и смотри, как под проходит Pending -> ContainerCreating -> Running.
  • --show-labels - показать все метки, чтобы понять, какому Deployment/релизу принадлежит под.
  • -l app=api,tier!=cache - селектор по меткам, фильтрует выборку на стороне сервера.
  • --sort-by=.status.startTime - сортировка по любому полю объекта.

Код: Выделить всё

# все непущенные поды по всему кластеру, отсортированные по числу рестартов:
$ kubectl get pods -A --field-selector=status.phase!=Running \
    --sort-by='.status.containerStatuses[0].restartCount'
Обрати внимание: --field-selector работает по узкому набору серверных полей (status.phase, metadata.name, spec.nodeName и немногим другим), а -l - по меткам. Это разные фильтры, не путай.

Теперь главное оружие kubectl get - форматы вывода. -o yaml и -o json отдают объект целиком, как он лежит в etcd, со status и managedFields. Отсюда родился jsonpath - способ вытащить одно конкретное поле без grep и без глаз на весь YAML:

Код: Выделить всё

# IP всех подов в столбик:
$ kubectl get pods -o jsonpath='{.items[*].status.podIP}'
10.112.4.27 10.112.5.13 10.112.4.41

# образ в каждом поде, по строке на под (range - это цикл):
$ kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[0].image}{"\n"}{end}'
api-7d9f8c5b6-2xk9p	registry.cyberlake.ru/api:1.8.2
worker-5c4d8b9f7-pp7wq	registry.cyberlake.ru/worker:0.4.1
Синтаксис jsonpath: .items[*] - все элементы массива, {range}...{end} - цикл, внутри литералы \t и \n. Это не jq, это собственный диалект kubectl, проще и беднее. Для регулярных отчётов удобнее custom-columns - тот же jsonpath, но в виде таблицы:

Код: Выделить всё

$ kubectl get pods -o custom-columns=\
'NAME:.metadata.name,NODE:.spec.nodeName,RESTARTS:.status.containerStatuses[0].restartCount,IMG:.spec.containers[0].image'
NAME                  NODE             RESTARTS   IMG
api-7d9f8c5b6-2xk9p   cl-node-fra-03   0          registry.cyberlake.ru/api:1.8.2
Связка jsonpath + xargs - твой швейцарский нож. Например, рестартовать все поды деплоя по одному или собрать имена нод с конкретной меткой - всё строится из этих кубиков.

kubectl describe: читаем события, а не только спеку

Если kubectl get отвечает на вопрос "что есть", то kubectl describe отвечает "что с ним происходит". Под капотом describe делает не один запрос: тянет сам объект плюс связанные Events из namespace и склеивает в человекочитаемый текст. Самое ценное внизу - секция Events:

Код: Выделить всё

$ kubectl describe pod api-7d9f8c5b6-jq4lm
...
Events:
  Type     Reason     Age                 From       Message
  ----     ------     ----                ----       -------
  Normal   Scheduled  3m                  scheduler  Successfully assigned default/api-... to cl-node-fra-03
  Normal   Pulled     3m                  kubelet    Container image "api:1.8.2" already present on machine
  Warning  BackOff    30s (x6 over 2m)    kubelet    Back-off restarting failed container api
  Warning  Unhealthy  10s (x4 over 1m)    kubelet    Liveness probe failed: HTTP probe returned 500
Вот где живёт правда. Liveness probe failed: HTTP probe returned 500 - приложение стартует, но health-эндпоинт отдаёт 500, поэтому kubelet убивает контейнер, а BackOff его придерживает. Ни один дашборд не скажет тебе это так быстро. Поле From говорит, КТО породил событие: scheduler не смог найти ноду - проблема в ресурсах/taints; kubelet не может скачать образ - ErrImagePull, проблема в registry или секрете.

Важная ловушка: события в Kubernetes по умолчанию живут около часа, потом etcd их вычищает. Если инцидент был ночью, а ты смотришь утром - событий уже нет. Поэтому на проде Events экспортируют в централизованное хранилище. И ещё: x6 over 2m рядом с Age значит, что событие схлопнуто - оно повторялось 6 раз, kubectl не дублирует строки.

Отдельная команда, которую недооценивают, - kubectl get events. Она даёт хронологию по всему namespace, а не по одному объекту:

Код: Выделить всё

$ kubectl get events --sort-by=.lastTimestamp -A
kubectl explain и jsonpath по схеме ресурса

Никто не помнит наизусть структуру манифеста. И не надо. kubectl explain - это встроенная документация по API прямо из кластера, она всегда соответствует твоей версии Kubernetes, в отличие от статьи из интернета:

Код: Выделить всё

$ kubectl explain pod.spec.containers.resources
KIND:       Pod
FIELD:      resources <ResourceRequirements>
DESCRIPTION:
    Compute Resources required by this container...
FIELDS:
  limits   <map[string]Quantity>
  requests <map[string]Quantity>
Спускайся по дереву через точку: explain deployment.spec.template.spec. Флаг --recursive вываливает всю вложенную структуру разом - удобно, когда лень кликать. Это лучший способ проверить, существует ли поле и в какой версии apiVersion оно живёт, прежде чем выдумывать его в YAML.

Поскольку 2026 год, помни про актуальные ресурсы: для трафика на входе индустрия мигрирует с Ingress на Gateway API (Gateway, HTTPRoute), для безопасности подов вместо мёртвого PodSecurityPolicy работает Pod Security Admission. explain знает обо всех CRD, установленных в кластере, - проверь explain httproute.spec, если в кластере стоит Gateway API.

Изменяем объекты: apply, diff, patch, edit

Менять кластер можно по-разному, и выбор способа отличает аккуратного инженера. Главное правило 2026 - декларативно через kubectl apply -f, а перед применением всегда смотреть diff:

Код: Выделить всё

$ kubectl diff -f deployment.yaml
kubectl diff отправляет манифест на сервер в режиме server dry-run, получает обратно полностью обсчитанный объект (с дефолтами, мутациями от admission webhook, нативными sidecar) и показывает реальную разницу с тем, что в кластере. Это единственный честный предпросмотр - он учитывает то, чего нет в твоём файле.

Связанный флаг - --dry-run, и тут важно различать два значения. --dry-run=client просто печатает объект, который УШЁЛ БЫ на сервер, локально, без отправки. А --dry-run=server реально гоняет запрос через apiserver: проходит валидацию, admission-контроллеры, applies defaults - но не пишет в etcd. Для генерации манифестов используют client, для проверки "пройдёт ли это вообще" - server:

Код: Выделить всё

# сгенерить заготовку Deployment, не создавая его:
$ kubectl create deployment api --image=api:1.8.2 \
    --dry-run=client -o yaml > deployment.yaml
Точечные правки делает kubectl patch, и тут надо понимать три стратегии слияния, иначе однажды снесёшь себе прод. По умолчанию kubectl применяет strategic merge patch - он умный, знает про merge-ключи (например, контейнеры мёржатся по полю name), поэтому патч одного контейнера НЕ затирает остальные. JSON merge patch (RFC 7386) тупее: любой список он заменяет целиком - передал один контейнер, и весь список контейнеров стал из одного. JSON patch (RFC 6902) - это операции add/remove/replace по точному пути, хирургия:

Код: Выделить всё

# strategic (по умолчанию) - меняем число реплик, остальное цело:
$ kubectl patch deployment api -p '{"spec":{"replicas":5}}'

# json patch - точечная операция по индексу:
$ kubectl patch deployment api --type='json' \
  -p '[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"api:1.8.3"}]'
kubectl edit открывает объект в $EDITOR и применяет изменения на сохранении - удобно для разовой ручной правки, но это императивщина: правка не попадёт в git и потеряется при следующем apply из CI. На проде с GitOps (Argo CD, Flux) edit на живом кластере - антипаттерн, твоя правка будет тут же откачена контроллером обратно к состоянию из репозитория.

Боль инцидента: logs, exec, debug, port-forward, top

kubectl logs - первое, что ты смотришь, когда под жив, но врёт. Базовое - logs <pod>, но реальная мощь в флагах:
  • -f - follow, поток в реальном времени, как tail -f.
  • --previous (-p) - логи ПРЕДЫДУЩЕГО упавшего контейнера. При CrashLoopBackOff текущий контейнер только что родился и пуст - правда в логах прошлого воплощения, бери -p.
  • -l app=api - логи сразу со всех подов по метке, не нужно перечислять имена.
  • --all-containers и -c <name> - в поде с sidecar укажи, чей лог хочешь.
  • --since=10m, --tail=100 - ограничить хвост, иначе утонешь.

Код: Выделить всё

$ kubectl logs -l app=api --all-containers --since=5m --prefix
kubectl exec - зайти внутрь работающего контейнера. Классика - интерактивная оболочка:

Код: Выделить всё

$ kubectl exec -it api-7d9f8c5b6-2xk9p -- sh
Запомни -- : всё после него уходит в контейнер как команда, а не парсится kubectl. Но в 2026 половина образов - distroless, в них нет ни sh, ни ps, exec бесполезен. Тут спасает kubectl debug: он подсаживает в живой под эфемерный контейнер (ephemeral container, GA с 1.25) с твоими инструментами, разделяя namespace процессов с целевым контейнером, и при этом ничего в поде не перезапускает:

Код: Выделить всё

$ kubectl debug -it api-7d9f8c5b6-2xk9p \
    --image=nicolaka/netshoot --target=api -- bash
Эфемерный контейнер попадает в аудит-лог и не модифицирует приложение - в отличие от --copy-to, который плодит копию пода и на инциденте только мешает. Минус один: убрать эфемерный контейнер из пода нельзя, он живёт до пересоздания пода.

Дополни арсенал: kubectl port-forward svc/api 8080:80 пробрасывает порт сервиса/пода на localhost - незаменимо, чтобы потыкать внутренний сервис без Ingress. kubectl cp копирует файлы в под и обратно (внутри контейнера должен быть tar). kubectl top pods и kubectl top nodes показывают реальное потребление CPU/памяти - но только если в кластере стоит metrics-server, иначе команда молча вернёт ошибку.

Контексты, namespace и навигация без боли

Один ~/.kube/config держит несколько кластеров. Триада понятий: cluster (адрес apiserver), user (твои креды), context (связка cluster+user+namespace). Команда смотрит на текущий контекст:

Код: Выделить всё

$ kubectl config get-contexts
CURRENT   NAME              CLUSTER         NAMESPACE
*         cl-prod           cl-prod         payments
          cl-staging        cl-staging      default
Главная катастрофа джуна - выполнить kubectl delete, думая, что ты на staging, а ты на prod. Защита - всегда видеть текущий контекст в приглашении (kube-ps1) и переключаться явными инструментами kubectx/kubens (это и есть народные kubectx kubens). kubectx прыгает между кластерами, kubens - между namespace внутри кластера. Для разового запроса не переключай глобально, а добавь --context cl-staging -n default прямо в команду - безопаснее.

Расширяется kubectl плагинами через менеджер krew. kubectl krew install ставит плагины, дальше они вызываются как kubectl <plugin>. Маст-хэв на 2026: ns/ctx (если без kubectx), neat (чистит вывод -o yaml от мусора managedFields и status), tree (показывает дерево владения объектов через ownerReferences), netshoot. И финальный аккорд скорости - метки. kubectl label и kubectl annotate массово навешивают метки/аннотации, что превращает -l в точный фильтр по сотням объектов.

Грабли и антипаттерны из реальной эксплуатации
  • Смотришь logs упавшего пода без --previous и видишь пустоту - ты читаешь свежий, пустой контейнер. Правда в -p.
  • kubectl edit на проде с GitOps - правка живёт минуты, потом Argo откатит. Меняй в git.
  • Спутал --field-selector и -l: первый по серверным полям, второй по меткам. По метке через field-selector не отфильтруешь.
  • JSON merge patch там, где нужен strategic - затёр список контейнеров/портов целиком. Для нативных объектов оставляй стратегию по умолчанию.
  • kubectl top без metrics-server - команда падает не из-за кластера, а из-за отсутствия аддона.
  • Забыл -A на инциденте и полчаса смотрел не тот namespace. На проде по умолчанию ставь -A.
  • Доверяешь статье из интернета про поля манифеста вместо kubectl explain своей версии - и ловишь unknown field при apply.
Мини-лаба: повтори руками

Нужен любой кластер - minikube, kind, k3s или Yandex Managed Kubernetes. Выполни по шагам:
  • Подними деплой: kubectl create deployment lab --image=nginx --replicas=3.
  • Смотри рождение подов в реальном времени: kubectl get pods -l app=lab -w (во втором окне).
  • Достань ноды и IP всех подов одним jsonpath с {range}...{end}.
  • Сломай образ: kubectl set image deployment/lab nginx=nginx:doesnotexist - и найди причину через kubectl describe pod (ищи ErrImagePull/ImagePullBackOff в Events).
  • Сделай kubectl diff -f с исправленным манифестом и убедись, что видишь разницу до применения.
  • Зайди в под через kubectl exec -it ... -- sh, потом то же через kubectl debug с образом netshoot.
  • Прокинь порт: kubectl port-forward deploy/lab 8080:80 и открой localhost:8080.
Контрольные вопросы
  • Чем отличается --dry-run=client от --dry-run=server и в какой задаче нужен каждый?
  • Почему при CrashLoopBackOff надо смотреть kubectl logs с флагом --previous?
  • В чём разница strategic merge patch и JSON merge patch при правке списка контейнеров?
  • Где kubectl describe берёт секцию Events и почему утром их может уже не быть?
Итог

kubectl - это REST-клиент к apiserver, и вся его магия раскладывается на простые кубики: get для обзора и точечной выборки через jsonpath/custom-columns, describe для событий и причин, explain как живую документацию по схеме, diff/patch/apply для безопасных изменений, logs/exec/debug/port-forward для разбора инцидента. Добавь алиас, автодополнение, kubectx/kubens и пару плагинов krew - и навигация по кластеру из мучения превратится в три уверенных нажатия. Эти команды kubectl ты будешь набирать тысячи раз, так что вложенные сегодня полчаса в мышечную память окупятся на первом же ночном дежурстве.
👍5 ❤️1 🔥 😄 🤔3
Аватара пользователя
middlegrind
Сообщения: 1
Зарегистрирован: 20 май 2026, 14:33

Re: kubectl профессионально: get, describe, explain, jsonpath

Сообщение middlegrind »

Год тыкал get pods и не знал про {range}{end} в jsonpath, теперь собрал отчет по нодам одной строкой. Спасибо, реально ускорило
👍1 ❤️1 🔥 😄 🤔
Аватара пользователя
CudaHacker
Сообщения: 1
Зарегистрирован: 12 май 2026, 02:34

Re: kubectl профессионально: get, describe, explain, jsonpath

Сообщение CudaHacker »

Поймал ровно ту граблю с --previous: смотрел пустой лог при CrashLoop и не понимал, где ошибка. А она в прошлом контейнере была все время
👍1 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Декларативная модель: reconciliation, контроллеры, CRD
Следующая глава →
Под глубже: init, sidecar, lifecycle hooks и QoS

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость