kai.kzLab

Вопросы на собеседовании по Kubernetes: 15 ситуаций с разбором ответов

Команда KAI Lab · · 11 мин чтения

На собеседовании по Kubernetes почти не спрашивают «что такое под». Спрашивают, почему он в CrashLoopBackOff, почему висит в Pending, почему Service не отдаёт трафик и как обновить узел без простоя. Интервьюер смотрит, какими командами вы ищете причину и понимаете ли, что Kubernetes делает под капотом.

Ниже 15 таких ситуаций: статусы подов, выкатки, сервисы, пробы, ресурсы и обслуживание узлов. У каждой — что хочет услышать интервьюер и ответ под спойлером. Факты сверены с документацией Kubernetes. Вопросы по другим темам — в подборке из 41 ситуации DevOps, по контейнерам — в 15 вопросах по Docker.

Поды: статусы и ошибки

1. Под в CrashLoopBackOff. Твои действия?

Что хочет услышать интервьюер: логи предыдущего запуска, describe, код выхода и понимание механизма backoff.

Ответ

CrashLoopBackOff — не причина, а состояние: контейнер падает, и kubelet перезапускает его с растущей паузой. По умолчанию 10, 20, 40 секунд и так далее до 5 минут; если контейнер проработал 10 минут без сбоев, отсчёт начинается заново. Порядок действий:

kubectl logs app-7d9f8 --previous      # логи упавшего экземпляра, а не нового
kubectl describe pod app-7d9f8         # Last State, Exit Code, Reason, события
kubectl get events --sort-by=.lastTimestamp

По коду и событиям видно направление. Код 1 и стектрейс в логах — ошибка приложения или конфигурации. Reason OOMKilled и код 137 — лимит памяти. События о проваленной liveness-пробе — пробу убивает медленный старт. Подробный разбор — в статье CrashLoopBackOff.

2. Под висит в Pending уже десять минут.

Что хочет услышать интервьюер: что значит фаза Pending, событие FailedScheduling и его причины.

Ответ

По документации Pending — под принят кластером, но контейнеры ещё не готовы: он ждёт планировщика или скачивает образ. kubectl describe pod покажет в событиях FailedScheduling с объяснением, почему ни один узел не подошёл. Частые причины:

  • requests пода не помещаются ни на один узел. Планировщик смотрит на сумму requests, а не на фактическую загрузку, поэтому под не встанет даже на «пустой» по графикам узел;
  • на узлах taint, а у пода нет подходящей toleration;
  • не совпали nodeSelector или affinity;
  • PersistentVolumeClaim не привязан к тому.

kubectl describe node покажет, сколько ресурсов уже распределено. Подробнее — под в Pending.

3. Под в ImagePullBackOff. Что проверишь?

Что хочет услышать интервьюер: три типовые причины — неверное имя или тег, доступ к registry, лимиты — и где это видно.

Ответ

В kubectl describe pod в событиях будет точная ошибка скачивания. «not found» — опечатка в имени или теге, образ не опубликован. «unauthorized» — приватный registry без imagePullSecrets или секрет в другом namespace. toomanyrequests — упёрлись в лимит скачиваний Docker Hub, см. toomanyrequests в Docker Hub. Таймауты — узел не видит registry по сети или DNS. Пока образ не скачан, под остаётся в фазе Pending, а kubelet повторяет попытки с растущей паузой. Подробнее — ImagePullBackOff.

4. Под в CreateContainerConfigError.

Что хочет услышать интервьюер: контейнер не создаётся, потому что нет ConfigMap, Secret или ключа, на которые он ссылается.

Ответ

Образ скачан, но kubelet не может собрать конфигурацию контейнера. Почти всегда под ссылается через env, envFrom или том на ConfigMap или Secret, которого нет, или на ключ, которого нет внутри. Событие в kubectl describe pod называет конкретный объект. Проверяю kubectl get secret,configmap -n <namespace>: эти объекты живут в namespace, и секрет из соседнего пространства не подойдёт. Если ссылка необязательна, её помечают optional: true. Подробнее — CreateContainerConfigError.

Деплойменты и выкатки

5. Выкатка новой версии зависла: новые поды не становятся Ready. Что происходит и что делать?

Что хочет услышать интервьюер: как ведёт себя RollingUpdate, дедлайн выкатки и откат.

Ответ

При стратегии RollingUpdate с настройками по умолчанию (maxUnavailable и maxSurge по 25%) Deployment не гасит больше четверти старых подов, пока новые не готовы. Поэтому сервис продолжает работать на старой версии, а kubectl rollout status deploy/app висит. Причину ищу в новых подах: kubectl describe pod покажет проваленную readiness-пробу, ImagePullBackOff или падение при старте. Через progressDeadlineSeconds (по умолчанию 600 секунд) у Deployment появится условие ProgressDeadlineExceeded, но сам он не откатится: это сигнал для людей и автоматики. Откат — kubectl rollout undo deploy/app.

6. Как выкатывать новую версию без простоя?

Что хочет услышать интервьюер: readiness, параметры RollingUpdate, корректное завершение и PodDisruptionBudget.

Ответ

Нужно несколько вещей вместе:

  • минимум две реплики;
  • readiness-проба: трафик идёт только на готовые поды;
  • maxUnavailable: 0 и maxSurge: 1, если хочется не терять ни одной рабочей реплики во время выкатки;
  • корректное завершение: при удалении пода контейнер получает SIGTERM и по умолчанию 30 секунд, чтобы дописать запросы, и только потом SIGKILL. Приложение должно ловить SIGTERM;
  • PodDisruptionBudget, чтобы плановые операции, например drain узла, не убили все реплики разом.

Ещё одна деталь: под удаляется из эндпоинтов не мгновенно, поэтому часто добавляют короткую паузу в preStop, чтобы балансировщик успел перестать слать на него запросы.

7. Что откатывает kubectl rollout undo, а что нет?

Что хочет услышать интервьюер: откат шаблона пода через ReplicaSet и то, что остаётся за его пределами.

Ответ

Каждая ревизия Deployment хранится в старом ReplicaSet, по умолчанию их держится 10 (revisionHistoryLimit). rollout undo возвращает шаблон пода из предыдущей ревизии: образ, переменные, ресурсы. Не откатываются:

  • содержимое ConfigMap и Secret — это отдельные объекты без истории, а переменные окружения из ConfigMap в работающих подах не обновляются без перезапуска;
  • Service, Ingress и другие ресурсы;
  • миграции базы данных.

Поэтому конфигурацию версионируют вместе с приложением: ConfigMap с хэшем в имени, Helm-релизы, GitOps. Тогда откат возвращает и код, и его настройки.

Сервисы и сеть

8. Service создан, а запросы до подов не доходят. Как искать?

Что хочет услышать интервьюер: селектор и метки, EndpointSlice, targetPort, readiness.

Ответ
kubectl get endpointslices -l kubernetes.io/service-name=app
kubectl get pods -l app=app --show-labels
kubectl port-forward pod/app-7d9f8 8080:8080

Пустой EndpointSlice значит, что селектор Service не совпадает с метками подов или поды не Ready: по документации проваленная readiness-проба убирает IP пода из эндпоинтов всех его сервисов. Если адреса есть — проверяю, что targetPort совпадает с портом, который слушает контейнер, и что приложение слушает 0.0.0.0, а не 127.0.0.1. port-forward проверяет под в обход сервиса. Потом — запрос из соседнего пода по имени app.<namespace>.svc.cluster.local. Последний подозреваемый — NetworkPolicy.

9. Как открыть сервис наружу и чем отличаются варианты?

Что хочет услышать интервьюер: типы Service и Ingress, когда что уместно.

Ответ

ClusterIP — тип по умолчанию, адрес доступен только внутри кластера. NodePort открывает порт на каждом узле из диапазона 30000–32767 по умолчанию; годится для стендов или своего внешнего балансировщика. LoadBalancer просит у облака внешний балансировщик — по отдельному на каждый сервис. Для HTTP обычно ставят один Ingress-контроллер или Gateway API за одним балансировщиком и маршрутизируют по хостам и путям, там же завершают TLS. Отвечая, стоит сказать, почему в проде не держат десяток сервисов типа LoadBalancer: это десяток внешних адресов и счетов.

Пробы

10. Приложение две минуты прогревает кэш, а Kubernetes убивает под при старте. Как исправить?

Что хочет услышать интервьюер: startup-проба и арифметика failureThreshold × periodSeconds.

Ответ

Liveness-проба не ждёт готовности приложения, и если она начинает проверять слишком рано, kubelet убивает под, который просто ещё не прогрелся. Правильное решение — startupProbe: пока она не прошла, liveness и readiness не запускаются. Время на старт — failureThreshold × periodSeconds:

startupProbe:
  httpGet:
    path: /healthz
    port: 8080
  failureThreshold: 30
  periodSeconds: 10      # до 300 секунд на запуск
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  periodSeconds: 10

После первого успеха startup-пробы работает liveness и быстро ловит зависания. Огромный initialDelaySeconds у liveness — худший вариант: он замедляет реакцию на реальные зависания всю жизнь пода.

11. База была недоступна 30 секунд, и все поды приложения перезапустились. Что не так с пробами?

Что хочет услышать интервьюер: liveness не должна зависеть от внешних систем; каскадные сбои; разница liveness и readiness.

Ответ

Скорее всего, liveness-проба проверяет базу. Когда база моргнула, проба провалилась у всех подов сразу, и kubelet перезапустил здоровые процессы, которым просто нужно было подождать. Документация Kubernetes предупреждает: liveness должна означать неустранимый сбой, например взаимоблокировку, а неправильная liveness-проба ведёт к каскадным сбоям. Liveness проверяет сам процесс. Зависимости можно учитывать в readiness, но осторожно: если readiness провалится у всех подов, у сервиса не останется эндпоинтов, и частичная деградация превратится в полный отказ.

Ресурсы

12. Под периодически перезапускается с OOMKilled. Что делаешь?

Что хочет услышать интервьюер: лимит памяти, код 137, как отличить утечку от заниженного лимита.

Ответ

В kubectl describe pod: Last State — Terminated, Reason — OOMKilled, Exit Code — 137. Контейнер превысил лимит памяти, и ядро убило процесс. По документации лимиты памяти работают реактивно: контейнер может ненадолго выйти за лимит, но его убьют, когда ядро заметит нехватку памяти. Дальше смотрю потребление: kubectl top pod и графики за несколько дней. Память растёт ровно и не падает — похоже на утечку. Скачет под нагрузкой или упирается в размер кучи — лимит занижен или куча JVM или Node.js настроена больше лимита. Подробнее — OOMKilled и exit code 137.

13. Задержка сервиса растёт, а загрузка CPU на графике всего 60%. Что может быть?

Что хочет услышать интервьюер: троттлинг по CPU limit, разница requests и limits, классы QoS.

Ответ

CPU limit ядро применяет через троттлинг: контейнер не может взять больше процессорного времени, чем разрешено за период планирования. Средняя загрузка за минуту может быть 60%, а внутри коротких окон процесс упирается в лимит и ждёт, отсюда рост задержки. Проверяю метрики cAdvisor container_cpu_cfs_throttled_periods_total и container_cpu_cfs_periods_total: какая доля периодов прошла с троттлингом. Лечится более высоким limit или его отказом для чувствительных к задержке сервисов при правильно выставленных requests. Requests — это то, под что планировщик резервирует место на узле. По requests и limits под получает класс QoS: Guaranteed, Burstable или BestEffort.

14. Поды на одном узле массово уходят в Evicted. Почему и кто будет следующим?

Что хочет услышать интервьюер: давление на ресурсы узла, пороги и порядок выселения.

Ответ

Kubelet выселяет поды, когда на узле заканчивается память, диск или inode. Пороги по умолчанию: свободной памяти меньше 100 МиБ, на корневом разделе узла меньше 10% места, на разделе образов меньше 15%, свободных inode меньше 5%. Порядок выселения по документации такой: сначала поды, которые потребляют больше своих requests, — по приоритету и по тому, насколько превышение велико; последними — поды, которые укладываются в requests. Kubelet не смотрит на класс QoS напрямую, но по нему можно предсказать очерёдность. При давлении на диск виноваты обычно логи и emptyDir без лимитов ephemeral-storage. Выселенные поды остаются в статусе Failed, пока их не удалят. Подробнее — под в статусе Evicted.

Обслуживание узлов

15. Нужно обновить ядро на узле кластера без простоя сервисов. Как?

Что хочет услышать интервьюер: cordon и drain, PodDisruptionBudget, DaemonSet, uncordon.

Ответ
kubectl cordon node-3                                   # новые поды сюда не планируются
kubectl drain node-3 --ignore-daemonsets --delete-emptydir-data
# обслуживание, перезагрузка
kubectl uncordon node-3

drain вытесняет поды с корректным завершением и соблюдает PodDisruptionBudget: если выселение нарушит бюджет, drain будет ждать. Поэтому у важных сервисов должно быть минимум две реплики и PDB. Поды DaemonSet drain не трогает — отсюда --ignore-daemonsets, а данные в emptyDir при выселении пропадут, о чём явно говорит второй флаг. Обновление самого Kubernetes рискованнее обновления ядра: у Reddit в 2023 году переход с 1.23 на 1.24 обернулся 314 минутами недоступности — см. сбой Reddit.

Как готовиться

Поднимите локальный кластер — kind, minikube или k3s — и воспроизведите каждую ситуацию из списка: опечатка в теге образа, requests больше узла, liveness-проба на базу, лимит памяти меньше, чем нужно приложению. Один раз увидеть FailedScheduling и OOMKilled в собственном describe полезнее, чем прочитать о них десять раз.

Проверить себя на рабочих ситуациях можно в бесплатном тесте навыков DevOps: 18 ситуаций, 10–15 минут, без регистрации.

Частые вопросы

Что спрашивают по Kubernetes на собеседовании джуна? Базу эксплуатации: статусы подов и как их диагностировать, Deployment и Service, пробы, requests и limits, kubectl logs, describe и get events. Администрирование control plane и etcd чаще спрашивают у мидлов и тех, кто идёт на платформенную роль.

Нужен ли сертификат CKA, чтобы пройти собеседование? Не обязателен, но экзамен практический и хорошо тренирует работу с kubectl под давлением времени. Интервьюер всё равно будет спрашивать ситуации, поэтому важнее опыт диагностики, чем сам сертификат.

Чем readiness отличается от liveness в одном предложении? Readiness решает, слать ли поду трафик, liveness — перезапускать ли контейнер. Проваленная readiness убирает под из эндпоинтов сервиса, проваленная liveness приводит к перезапуску.

Безопасно ли хранить пароли в Kubernetes Secret? По умолчанию Secret хранится в etcd незашифрованным, а base64 — не шифрование. Нужны шифрование etcd на уровне хранения, RBAC с минимальным доступом к секретам и, по возможности, внешнее хранилище секретов.

Источники

Читать дальше