Перейти к содержанию
Бэкенд и системы

13 вопросов по теме «DevOps: Kubernetes» на собеседовании

В этом материале — 13 вопросов из русской колоды RecallDeck по теме «DevOps: Kubernetes». Сначала сформулируйте короткий ответ сами, затем откройте подробный разбор и проверьте примеры, ограничения и отказные случаи.

11 мин чтения13 подробных ответовПроверено 24 августа 2026
Главная мысль

Стройте ответ вокруг владения состоянием, лимитов, отказа и восстановления. Определение становится инженерным ответом только в конкретном production-сценарии.

Вопросы и ответы

13 подробных ответов

01

Что происходит после `kubectl apply -f deployment.yaml`? Опишите всю цепочку до работающего пода.

Короткий ответ: kubectl валидирует манифест и отправляет его в API server: authn → RBAC → admission-вебхуки → запись в etcd. На этом синхронная часть заканчивается — дальше контроллеры, scheduler и kubelet асинхронно замечают изменения через watch и приводят кластер к желаемому состоянию. Никто ничего не «пушит».

Подробно:

  1. API server — аутентификация, авторизация (RBAC), admission (mutating → validating вебхуки), объект сохранён в etcd.
  2. Deployment-контроллер — видит новый Deployment (watch) и создаёт ReplicaSet.
  3. ReplicaSet-контроллер — создаёт Pod-объекты, пока без ноды.
  4. Scheduler — находит поды с пустым nodeName, фильтрует и скорит ноды, делает bind.
  5. kubelet — на выбранной ноде видит «свой» под: тянет образы, запускает контейнеры через CRI, сеть поднимает CNI-плагин.
  6. Readiness — проба прошла → под попадает в EndpointSlice и получает трафик.
kubectl ──► API server (authn → RBAC → admission) ──► etcd
              ▲ watch      ▲ watch       ▲ watch      ▲ watch
  deploy-ctrl → RS    rs-ctrl → Pods   scheduler → bind   kubelet → CRI/CNI

⚠️ Частая ошибка: «API server шедулит под и запускает контейнеры». Он только хранит и раздаёт состояние — всю работу делают независимые контроллеры по модели watch/reconcile.

02

Разберите архитектуру Kubernetes: apiserver, etcd, scheduler, controller-manager, kubelet, kube-proxy — за что отвечает каждый?

Короткий ответ: apiserver — единая точка входа и единственный, кто ходит в etcd; etcd — хранилище состояния; scheduler — выбирает ноду; controller-manager — циклы reconcile; kubelet — запускает контейнеры на ноде; kube-proxy — сетевые правила для Service.

Подробно:

Компонент Роль в одну строку
kube-apiserver REST-API кластера: authn/authz, admission, валидация; единственный клиент etcd
etcd консистентное KV-хранилище всего состояния кластера (Raft)
kube-scheduler подбирает ноду для пода (фильтры + скоринг), пишет bind
kube-controller-manager десятки контроллеров — Deployment, ReplicaSet, Node, Job… — reconcile желаемого состояния
kubelet агент на ноде: запускает поды через CRI, гоняет пробы, репортит статус
kube-proxy программирует iptables/IPVS, чтобы работали Service ClusterIP

Первые четыре — control plane; kubelet и kube-proxy живут на каждой ноде. Ключевая идея: все общаются только с apiserver через watch — компоненты не знают друг о друге и не ходят в etcd напрямую. Поэтому control plane переживает отказ любого компонента, кроме etcd.

⚠️ Частая ошибка: говорить, что scheduler или controller-manager «пишут в etcd». Пишут они в apiserver — он один держит соединение с etcd.

03

Почему etcd нужно нечётное число членов и что произойдёт с кластером при потере кворума?

Короткий ответ: etcd работает на Raft: запись подтверждается большинством — (n/2)+1. Чётный член не добавляет отказоустойчивости: и 3, и 4 ноды переживают ровно один отказ. При потере кворума etcd перестаёт принимать записи → API «замирает»: kubectl apply падает, контроллеры и шедулинг стоят. Уже запущенные поды продолжают работать.

Подробно:

Членов Кворум Переживает отказов
3 2 1
4 3 1 (чётный член ничего не дал)
5 3 2
  1. Кворум потерян — записи невозможны; apiserver ещё может отдавать чтения, но не принимает изменений: ни выкаток, ни скейлинга, ни новых подов.
  2. Data plane живёт — kubelet держит контейнеры, kube-proxy — правила; кластер «заморожен», но трафик идёт.
  3. Медленный etcd опаснее упавшего — растёт латентность каждого API-запроса, watch отстают, контроллеры реагируют с лагом, leader election флапает.

⚠️ Частая ошибка: «упал etcd — упало всё». Нет: падает management plane; рабочая нагрузка продолжает обслуживать трафик, пока не понадобится что-то изменить.

04

Под завис в Pending: как найти причину и какие причины самые частые?

Короткий ответ: Pending значит «scheduler не нашёл ноду». Первый шаг всегда один: kubectl describe pod — в Events scheduler сам объясняет, что его не устроило. Дальше причина почти всегда одна из пяти: ресурсы, taints, affinity, PVC, квота.

Подробно:

kubectl describe pod app-7d9f   # Events: "0/12 nodes are available: ..."
kubectl get nodes               # ноды живы и Ready?
kubectl describe node n1        # Allocatable vs Allocated: влезают ли requests
kubectl get pvc                 # PVC тоже Pending?
kubectl describe quota -n team  # упёрлись в ResourceQuota?
  1. Не хватает ресурсов — суммарные requests не влезают ни на одну ноду («Insufficient cpu/memory»).
  2. Taints без tolerations — «node(s) had untolerated taint».
  3. nodeSelector / affinity — ни одна нода не подходит под метки.
  4. PVC не забиндился — под ждёт том: нет StorageClass, не совпала зона.
  5. ResourceQuota — нюанс: под вообще не создаётся, ошибка видна в Events у ReplicaSet, а не как Pending.

⚠️ Частая ошибка: начинать с логов. У Pending-пода нет логов — он ещё нигде не запущен; вся информация в Events.

05

Под в CrashLoopBackOff: какова последовательность отладки?

Короткий ответ: CrashLoopBackOff — контейнер запускается, падает, и kubelet рестартит его с растущей задержкой (10 с, удваивается, потолок 5 мин). Порядок: describe (Events и Last State с exit code) → logs --previous (логи именно упавшего контейнера) → интерпретировать exit code.

Подробно:

kubectl describe pod app-7d9f       # Events + Last State: Exit Code
kubectl logs app-7d9f --previous    # логи УПАВШЕГО контейнера — ключевой флаг
kubectl logs app-7d9f -c init-db    # если падает init-контейнер
Exit code Что значит
1 ошибка приложения — читаем свой лог
127 нет такого бинаря — опечатка в command/entrypoint
137 SIGKILL: OOMKilled (смотрим describe) или прибит при eviction

Дальше по убыванию частоты: битый конфиг или секрет (нет env, не смонтировался volume), падающая liveness-проба (в Events будет «Liveness probe failed… will be restarted»), недоступная на старте зависимость или миграции.

⚠️ Частая ошибка: смотреть kubectl logs без --previous и удивляться пустоте: свежерестартнутый контейнер ещё ничего не написал, а логи упавшего живут за флагом.

06

OOMKilled с exit code 137: кто убивает контейнер — Kubernetes или ядро? И при чём здесь QoS-классы?

Короткий ответ: Убивает ядро Linux. Kubernetes лишь записал memory limit в cgroup (memory.max); когда процесс его превышает, срабатывает kernel OOM killer и шлёт SIGKILL (128+9=137). QoS-класс пода задаёт oom_score_adj — кого ядро и kubelet принесут в жертву первым.

Подробно:

QoS Условие oom_score_adj
Guaranteed requests = limits по CPU и памяти −997 (умирает последним)
Burstable requests < limits или заданы частично промежуточный, зависит от request
BestEffort ни requests, ни limits +1000 (умирает первым)

Два разных механизма, которые путают:

  1. cgroup OOM (ядро) — контейнер превысил СВОЙ limit → мгновенный SIGKILL, статус OOMKilled. Kubernetes в этот момент не участвует.
  2. Node-pressure eviction (kubelet) — память кончается на ноде в целом → kubelet сам вытесняет поды, ранжируя по QoS и превышению usage над request; статус Evicted, а не OOMKilled.

⚠️ Частая ошибка: «kubelet следит за памятью и убивает контейнер при превышении limit». Нет: per-container limit энфорсит ядро через cgroup; kubelet вмешивается только при давлении на всю ноду.

07

Requests и limits: чем управляет каждый и что происходит при превышении?

Короткий ответ: Requests — валюта шедулинга: по ним scheduler бин-пакует поды на ноды, и они же задают вес при борьбе за CPU. Limits — потолок: CPU при превышении троттлится, память — SIGKILL (OOMKilled). CPU — «сжимаемый» ресурс, память — нет, отсюда и разное наказание.

Подробно:

CPU Память
request гарантированная доля + вес при contention учитывается только шедулером
limit превышен throttling: под тормозит, но живёт OOM kill: мгновенная смерть
  1. Requests ≠ реальное потребление — scheduler смотрит только на сумму requests: нода может быть «занята» на бумаге и пустой по факту, и наоборот.
  2. Limits без requests — Kubernetes автоматически выставит requests = limits.
  3. Requests без limits — под может burst'ить в свободные ресурсы; для CPU это часто желаемое поведение, для памяти — риск OOM на переподписанной ноде.

⚠️ Частая ошибка: «память при превышении лимита троттлится, как CPU». Память нельзя отнимать у процесса постепенно — только убить: превышение memory limit всегда заканчивается SIGKILL.

08

Как Service на самом деле доставляет трафик до подов и что делает kube-proxy?

Короткий ответ: ClusterIP — виртуальный адрес: на нём никто не слушает, и пакет до него не «доходит». kube-proxy на каждой ноде программирует правила iptables (или IPVS): пакет на ClusterIP DNAT'ится в IP:port одного из ready-подов прямо в netfilter ноды. Сам kube-proxy в пути пакета не участвует.

Подробно:

Pod A ──► 10.96.0.10:80 (ClusterIP: виртуальный, процесса нет)
             │ iptables DNAT (правила написал kube-proxy)
             ▼ случайный ready-эндпоинт
          10.244.1.5:8080 (Pod B)
  1. EndpointSlice — контроллер собирает список подов Service'а, прошедших readiness; kube-proxy watch'ит его и переписывает правила.
  2. Readiness = членство — под провалил пробу → выпал из EndpointSlice → правила больше на него не указывают.
  3. iptables vs IPVS — при тысячах Service'ов линейная цепочка iptables тормозит; IPVS даёт hash-таблицу.
  4. Ingress — это уже L7: отдельный контроллер (nginx, envoy) терминирует HTTP и маршрутизирует по host/path; Service под ним — та же L4-механика.

⚠️ Частая ошибка: думать, что kube-proxy — прокси в датапути, через который текут пакеты. Он лишь конфигуратор правил ядра и трафика не видит.

09

Чем отличаются liveness, readiness и startup пробы — и как неправильная liveness-проба устраивает каскадный отказ?

Короткий ответ: Readiness управляет трафиком (членство в EndpointSlice), liveness — жизнью контейнера (провал = рестарт), startup откладывает первые две, пока приложение стартует. Классическая авария: liveness проверяет зависимость (БД) — база моргнула, и kubelet рестартит весь флот разом.

Подробно:

Проба Провал → Для чего
readiness под выпадает из балансировки «сейчас не могу принимать трафик» — временно и обратимо
liveness kubelet рестартит контейнер «процесс завис безнадёжно» — только про сам процесс
startup рестарт после failureThreshold медленный старт (JVM, прогрев кэша) без раздувания liveness

Анатомия аварии: liveness дёргает /health, который ходит в БД → база недоступна 30 секунд → liveness провалилась у всех реплик → kubelet рестартит все контейнеры → сервис лёг целиком, а массовые рестарты добивают едва ожившую базу.

Правило: liveness — только о самом процессе (deadlock, зависший event loop); зависимости — территория readiness, и то с осторожностью.

⚠️ Частая ошибка: один endpoint на liveness и readiness, проверяющий всё подряд. Один blip зависимости превращается в рестарт флота вместо паузы трафика.

10

Deployment, StatefulSet или DaemonSet: как выбрать и чем обосновать?

Короткий ответ: Deployment — взаимозаменяемые stateless-реплики с rolling update. StatefulSet — когда репликам нужна стабильная идентичность: имя (pod-0, pod-1), свой PVC на реплику, упорядоченная выкатка, стабильный DNS через headless Service. DaemonSet — ровно один под на каждой подходящей ноде: агенты логов, метрик, CNI.

Подробно:

Deployment StatefulSet DaemonSet
Идентичность нет, поды взаимозаменяемы стабильная: web-0, web-1 + DNS привязка к ноде
Хранилище общее или нет PVC-шаблон: том на реплику hostPath или нет
Порядок параллельно упорядоченные rollout и scale по нодам
Кейсы API, воркеры Kafka, etcd, реплики БД node-exporter, fluent-bit

Headless Service (clusterIP: None) даёт каждой реплике StatefulSet предсказуемое DNS-имя вида web-0.web.ns.svc — так брокеры и реплики находят друг друга.

⚠️ Частая ошибка: «есть база — значит StatefulSet». Если база managed (RDS, Cloud SQL) — приложению хватит Deployment; а серьёзные БД внутри кластера обычно живут под оператором, который сам решает, чем управлять.

11

Как HPA решает, сколько реплик нужно, и почему без resource requests он не работает?

Короткий ответ: По формуле: desiredReplicas = ceil(текущая метрика / целевая × текущие реплики). Для CPU «utilization» считается как процент от requests — нет requests, нет знаменателя, и HPA не может посчитать вообще ничего.

Подробно:

desired = ceil( current / target × currentReplicas )
пример: CPU 80% от request, цель 50%, реплик 4
        ceil(80/50 × 4) = ceil(6.4) = 7
  1. Источник метрик — metrics-server (resource-метрики) либо custom/external metrics API (RPS, глубина очереди).
  2. % от requests — «CPU 80%» значит «под ест 80% своего request», а не ноды; заниженные requests заставляют HPA скейлить раньше времени, завышенные — не скейлить никогда.
  3. Стабилизация — окно по умолчанию 300 с на scale-down (вверх — сразу): защита от флаппинга на шумной метрике.
  4. Границы — ниже minReplicas не уйдёт; при недоступных метриках масштабирование замирает, а не «скейлится в ноль».

⚠️ Частая ошибка: повесить HPA на поды без requests и ждать чуда — в статусе будет FailedGetResourceMetric, и число реплик не сдвинется.

12

Во время rolling update часть запросов ловит 5xx. Почему и как сделать выкатку по-настоящему zero-downtime?

Короткий ответ: Из-за гонки при остановке пода: SIGTERM контейнеру и удаление пода из EndpointSlice происходят параллельно, а iptables на нодах и внешние LB обновляются с лагом — какое-то время трафик летит в уже умирающее приложение. Лечится связкой: preStop-пауза + грамотный SIGTERM + запас в grace period + PDB.

Подробно:

t=0     под помечен на удаление
        ├─► SIGTERM приложению        (мгновенно)
        └─► удаление из EndpointSlice (асинхронно: kube-proxy, LB — лаг!)
t=0…3с  старые правила ещё шлют запросы → connection refused / 5xx
фикс:   preStop sleep 5 → сначала дренится трафик, потом умирает процесс
  1. preStop hooksleep 5: даёт правилам и LB время убрать под из ротации до SIGTERM.
  2. SIGTERM в приложении — перестать принимать новые соединения, дорешать текущие, выйти.
  3. terminationGracePeriodSeconds — больше worst-case дренажа + обработки (по умолчанию 30 с, дальше SIGKILL).
  4. PodDisruptionBudget — на drain'ах и апгрейдах нод (cordon + drain уважает PDB) гарантирует минимум живых реплик.

⚠️ Частая ошибка: починить только приложение (SIGTERM-хендлер) и пропустить гонку с эндпоинтами: без preStop-паузы запросы продолжат прилетать в под, который уже начал останавливаться.

13

Команде нужен доступ только к её неймспейсу: какие RBAC-объекты вы создадите и как аутентифицируются поды?

Короткий ответ: Role (правила внутри неймспейса) + RoleBinding на группу или пользователей команды. Для переиспользования — один ClusterRole с типовым набором прав и по RoleBinding в каждом неймспейсе: binding неймспейсный, значит и права неймспейсные. Поды аутентифицируются токенами ServiceAccount.

Подробно:

  1. Role vs ClusterRole — Role живёт в неймспейсе; ClusterRole глобален, но через RoleBinding его права «сужаются» до одного неймспейса — паттерн для одинаковых прав у десятка команд.
  2. Субъекты — биндить лучше на группу из IdP (group: team-a), а не на людей поимённо.
  3. ServiceAccount — под получает projected-токен своего SA; если приложению нужен доступ к API, SA биндится теми же Role/RoleBinding.
  4. Проверка — не гадать, а спросить кластер:
kubectl auth can-i list pods -n team-a --as u@corp --as-group team-a   # yes
kubectl auth can-i list pods -n team-b --as u@corp --as-group team-a   # no
kubectl auth can-i --list -n team-a --as u@corp    # весь набор прав разом

⚠️ Частая ошибка: выдать cluster-admin «временно, чтобы не разбираться». RBAC аддитивен — отнять лишнее потом нельзя, только отзывать биндинги; начинайте с least privilege.

Источники

Источники и редакционная политика

Материалы RecallDeck сопоставлены с официальной документацией и открытыми публикациями компаний, когда первичный источник доступен. Мы не связаны с упомянутыми работодателями, не публикуем конфиденциальные задания и не продаём места в подборках. Формат найма может меняться — уточняйте его у рекрутера.

От чтения к воспроизведению

Отрепетируйте полный цикл интервью.

RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.

Начать подготовку

Продолжить подготовку

Библиотека собеседований RecallDeck

Подробные русские ответы, разборы этапов найма и планы подготовки для российского IT-рынка.

RSS