Kubernetes — обзор и архитектура

Kubernetes (K8s) — система автоматизации деплоя и управления контейнеризированными приложениями на кластере машин. Если Docker решает проблему "как запустить приложение в контейнере", то Kubernetes решает другую — "как управлять тысячами контейнеров на десятках серверов". Проект вырос из внутренних систем Google (Borg/Omega), где обрабатывалось более 2 миллиардов контейнеров в неделю, и стал open source в 2014 году.

Аналогия: операционная система управляет процессами на одной машине, Kubernetes управляет контейнерами на множестве машин. Ты выступаешь в роли капитана — решаешь куда плыть. K8s выступает рулевым — ведёт корабль по курсу и докладывает обстановку.


Зачем нужен Kubernetes

В мире монолитов ручное управление допустимо: одно приложение, один деплой, один сервер. С переходом на микросервисы (десятки и сотни отдельных сервисов) ручной менеджмент становится невозможным.

АспектМонолитМикросервисы
Приложений1100+
Деплоев1100+
Ручное управлениедопустимоневозможно

Kubernetes автоматизирует то, что невозможно делать руками:

ВозможностьОписание
Service discoveryПриложения находят друг друга по имени, без хардкода адресов
Horizontal scalingАвтоматическое масштабирование по нагрузке (HPA)
Load balancingРаспределение трафика между репликами
Self-healingПерезапуск упавших контейнеров, перенос с упавших нод
Leader electionВстроенный механизм выбора лидера для stateful-сервисов
Rolling updatesОбновление приложений без downtime
Config managementConfigMaps и Secrets для внешней конфигурации

Стандартизация облака

Без Kubernetes: AWS API отличается от GCP API и от Azure API. Миграция между облаками означает переписывание всего деплоя. С Kubernetes: kubectl apply -f app.yaml работает одинаково на AWS, GCP, Azure и on-prem. Kubernetes API — единый стандарт деплоя, который устраняет vendor lock-in.


Модель управления кластером

Декларативная модель: desired state vs current state

Главный принцип Kubernetes — декларативность. Ты не говоришь системе как сделать, ты описываешь что хочешь получить.

Императивный подход (без K8s)

"Запусти 3 инстанса на сервере A, 2 на сервере B, настрой балансировщик на порту 80, подключи к нему все пять инстансов..." — ты управляешь каждым шагом.

Декларативный подход (K8s)

"Хочу 5 инстансов моего приложения, порт 8080, 256MB RAM на каждый" — ты описываешь желаемое состояние (desired state), а Kubernetes решает, как его достичь и поддерживать.

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: replicas: 5 # хочу 5 инстансов selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: my-app:1.0 ports: - containerPort: 8080 resources: requests: memory: "256Mi"

Изменил описание — K8s приводит систему к новому состоянию:

  • Добавил реплику (replicas: 6) — K8s запустит ещё один pod
  • Убрал компонент — K8s остановит его
  • Приложение упало — K8s перезапустит
  • Нода вышла из строя — K8s переместит поды на здоровые ноды

Архитектура кластера

Кластер Kubernetes состоит из двух плоскостей: Control Plane (мозг, управление) и Worker Nodes (тело, выполнение). Все компоненты общаются только через API Server — никто не ходит напрямую в etcd или между собой.

text
Kubernetes Cluster +--------------------------------------------+ | Control Plane | | +----------+ +----------+ +----------+ | | | Master 1 | | Master 2 | | Master 3 | | | +----------+ +----------+ +----------+ | |--------------------------------------------+ | Workload Plane | | +--------+ +--------+ +--------+ | | |Worker 1| |Worker 2| |Worker 3| ... | | +--------+ +--------+ +--------+ | +--------------------------------------------+

Control Plane

Control Plane — это мозг кластера. Принимает решения, хранит состояние, реагирует на изменения. В production минимум 3 master-ноды для высокой доступности.

text
+---------------------------------------------+ | Control Plane | | | | +-----------------+ +------------------+ | | | API Server | | Scheduler | | | | единая точка | | выбирает ноду | | | | входа (REST API)| | для каждого pod | | | +--------+--------+ +------------------+ | | | | | +--------v--------+ +------------------+ | | | etcd | | Controllers | | | | distributed KV | | поддерживают | | | |store (состояние)| | desired state | | | +-----------------+ +------------------+ | +----------------------------------------------+

API Server

API Server — единственная точка входа в кластер. Все взаимодействия проходят через него: kubectl, контроллеры, kubelet, внешние системы.

  • RESTful API для всех операций (CRUD над объектами)
  • Stateless — сам ничего не хранит, всё в etcd
  • Валидирует объекты перед сохранением
  • Уведомляет подписчиков (watch) об изменениях объектов
  • Поддерживает аутентификацию, авторизацию, admission control

etcd

etcd — распределённое key-value хранилище, основанное на алгоритме консенсуса Raft. Хранит всё состояние кластера.

  • Все объекты K8s (pods, deployments, services, configmaps, secrets)
  • Только API Server общается с etcd напрямую
  • Потеря etcd = потеря кластера, поэтому бэкапы критичны
  • Для production требуется кластер из нечётного количества нод (3, 5, 7) для Raft quorum

Scheduler

Scheduler наблюдает за новыми подами без назначенной ноды и выбирает для них лучшее место.

  • Watch: поды с пустым полем nodeName
  • Оценивает ноды по доступным ресурсам, affinity/anti-affinity правилам, taints/tolerations
  • Записывает решение (поле nodeName) через API Server
  • Не запускает pod — только назначает ноду; запуском занимается Kubelet

Controller Manager

Controller Manager запускает набор контроллеров, каждый из которых отвечает за свой тип объекта. Каждый контроллер реализует reconciliation loop.

Примеры контроллеров:

КонтроллерЗа что отвечает
ReplicaSet controllerПоддерживает нужное количество реплик pod
Deployment controllerУправляет ReplicaSet при обновлениях
Node controllerСледит за здоровьем нод
Job controllerЗапускает задачи до завершения
EndpointSlice controllerОбновляет endpoints для Service

Worker Nodes

Worker-ноды — это машины, на которых запускаются приложения. Каждая нода содержит три обязательных компонента.

text
+--------------------------------------+ | Worker Node | | | | +----------+ +-----------------+ | | | Kubelet | |Container Runtime| | | | агент |-->| containerd / | | | | на ноде | | CRI-O | | | +----------+ +-----------------+ | | | | +----------------------------------+| | | kube-proxy || | | сетевые правила, load balancing || | +----------------------------------+| | | | +-----+ +-----+ +-----+ | | |Pod 1| |Pod 2| |Pod 3| ... | | +-----+ +-----+ +-----+ | +--------------------------------------+

Kubelet

Kubelet — агент, который запущен на каждой worker-ноде.

  • Наблюдает (watch) за подами, назначенными на его ноду
  • Инструктирует Container Runtime запустить или остановить контейнеры
  • Следит за здоровьем подов (выполняет probes), отправляет статус в API Server
  • Не управляет контейнерами, запущенными вне Kubernetes

Container Runtime

Container Runtime — программа, непосредственно запускающая контейнеры.

  • containerd — стандартный runtime начиная с K8s 1.24+ (Docker runtime удалён)
  • CRI-O — альтернативный легковесный runtime
  • Общается с Kubelet через CRI (Container Runtime Interface)
  • Образ, собранный через Docker, работает на containerd/CRI-O без изменений (OCI стандарт)

kube-proxy

kube-proxy — сетевой компонент на каждой ноде.

  • Реализует абстракцию Service (ClusterIP, NodePort, LoadBalancer)
  • Настраивает iptables или IPVS правила для балансировки трафика
  • Не проксирует трафик сам — настраивает правила в ядре Linux
  • Обновляет правила при изменении endpoints (поды появляются/исчезают)

Reconciliation loop

Reconciliation loop — фундаментальный паттерн Kubernetes. Каждый контроллер работает по одному принципу: бесконечный цикл "прочитай желаемое состояние (spec) — сравни с текущим (status) — исправь разницу".

text
Ты Controller Кластер | | | | пишешь spec | | | (replicas: 3) | | |----------------------->| | | | читает spec | | | создаёт 3 Pod'а | | |----------------------->| | | | | | пишет status | | | (replicas: 3, | | читаешь status | ready: 3) | |<-----------------------| |

Контроллер не выполняет команду один раз — он постоянно следит за состоянием. Если pod упал и осталось 2 реплики вместо 3, контроллер заметит расхождение и создаст новый pod. Это обеспечивает self-healing.


Как запускается приложение

Как K8s запускает приложение

Когда ты выполняешь kubectl apply -f app.yaml, происходит цепочка событий:

  1. kubectl -> API Server — манифест отправляется в API Server, который валидирует объекты и сохраняет их в etcd
  2. Controller замечает новый Deployment, создаёт ReplicaSet; ReplicaSet controller создаёт Pod-объекты
  3. Scheduler замечает Pod-ы без назначенной ноды, выбирает лучшую ноду для каждого, записывает nodeName
  4. Kubelet на назначенной ноде замечает Pod, инструктирует Container Runtime запустить контейнер
  5. kube-proxy замечает готовый Pod, настраивает сетевые правила для Service (load balancing)
  6. Kubelet + Controllers постоянно мониторят: pod упал — перезапуск; нода упала — pod перемещён на здоровую ноду

Каждый компонент выполняет свою задачу и общается только через API Server. Нет центрального оркестратора — есть набор независимых контроллеров, каждый отвечающий за свою часть.


Kubernetes API и манифесты

Всё в Kubernetes — объект в API. Pods, Deployments, Services, Nodes, ConfigMaps — всё управляется через REST API. kubectl — это просто CLI-клиент к API Server.

HTTP-методURLДействие
POST/apis/apps/v1/namespaces/default/deploymentsСоздать
GET/apis/apps/v1/namespaces/default/deployments/xПрочитать
PUT/apis/apps/v1/namespaces/default/deployments/xОбновить
DELETE/apis/apps/v1/namespaces/default/deployments/xУдалить

Структура манифеста

Каждый объект Kubernetes описывается YAML-манифестом из четырёх основных секций:

yaml
apiVersion: apps/v1 # --- Type metadata kind: Deployment # тип объекта и API-группа metadata: # --- Object metadata name: my-app # имя, namespace, labels, annotations namespace: default labels: app: my-app spec: # --- Desired state (ты пишешь) replicas: 3 # что ты ХОЧЕШЬ selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: my-app:1.0 status: # --- Actual state (контроллер пишет) replicas: 3 # что СЕЙЧАС на самом деле conditions: [...]
  • apiVersion + kind — определяют тип объекта
  • metadata — идентификация: имя, namespace, labels, annotations
  • spec — желаемое состояние, которое ты описываешь
  • status — текущее состояние, которое заполняет контроллер

Не все объекты имеют spec/status. Например, Event, ConfigMap, Secret — это статические данные без контроллера, который бы выполнял reconciliation.

apiVersion и kind

Поле apiVersion определяет API-группу и версию:

apiVersionОбъекты
v1Pod, Service, ConfigMap, Node, Secret (core-группа)
apps/v1Deployment, ReplicaSet, StatefulSet, DaemonSet
batch/v1Job, CronJob
networking.k8s.io/v1Ingress, NetworkPolicy

Conditions — состояние объекта

Status объекта часто содержит массив conditions — независимых друг от друга аспектов состояния:

yaml
status: conditions: - type: Ready status: "True" # True / False / Unknown reason: KubeletReady # machine-facing (для автоматики) message: "kubelet is posting ready status" # human-facing lastTransitionTime: "2024-01-15T10:30:00Z"

Conditions ортогональны: каждый описывает независимый аспект. Это лучше, чем одно поле status: healthy/unhealthy, и легко расширяется новыми условиями.

Примеры Node conditions:

ConditionОписание
ReadyНода готова принимать pod-ы
MemoryPressureЗаканчивается RAM
DiskPressureЗаканчивается диск
PIDPressureЗаканчиваются PID-ы

Event-объекты

Event — отдельный объект в API, создаваемый контроллерами при действиях или проблемах. Events удаляются примерно через час, чтобы не нагружать etcd. Бывают двух типов: Normal и Warning.

bash
kubectl get events # все события kubectl get events --field-selector type=Warning # только проблемы kubectl describe pod my-app # события конкретного pod-а

kubectl — основные команды

kubectl — основной инструмент для работы с кластером. Все команды отправляют HTTP-запросы к API Server.

Просмотр объектов

bash
kubectl get pods # список pod-ов в текущем namespace kubectl get pods -A # pod-ы во всех namespace-ах kubectl get pods -o wide # расширенный вывод (IP, нода) kubectl get deployment my-app -o yaml # полный YAML-манифест объекта kubectl get deployment my-app -o json # JSON-формат kubectl describe pod my-app # human-readable + events

Управление объектами

bash
kubectl apply -f manifest.yaml # создать/обновить объект из файла kubectl delete -f manifest.yaml # удалить объект kubectl delete pod my-app # удалить конкретный pod kubectl scale deployment my-app --replicas=5 # изменить количество реплик

Документация по полям

bash
kubectl explain pod # документация по типу Pod kubectl explain pod.spec.containers # drill down в конкретное поле kubectl explain pods --recursive # полное дерево полей

Отладка

bash
kubectl logs my-app # логи pod-а (stdout/stderr) kubectl logs my-app -f # стриминг логов kubectl logs my-app -c sidecar # логи конкретного контейнера kubectl logs my-app --previous # логи предыдущего контейнера (после restart) kubectl exec -it my-app -- bash # shell внутри контейнера kubectl port-forward my-app 8080:8080 # проксирование порта на localhost

Эксплуатационные решения

Add-on компоненты

Не часть ядра Kubernetes, но почти всегда установлены в кластере:

КомпонентНазначение
CoreDNSDNS-сервер кластера для service discovery по имени
CNI pluginСетевой плагин (Calico, Cilium, Flannel)
Ingress controllerВходящий HTTP/HTTPS трафик (Nginx, Traefik)
Metrics ServerМетрики для HPA и kubectl top
DashboardВеб-интерфейс (опционально)

Сколько нод нужно

Control Plane (master)

  • Dev/test: 1 нода — single point of failure, но допустимо для разработки
  • Production: 3+ ноды — etcd требует Raft quorum (формула 2N+1 для отказоустойчивости)

Worker Nodes

  • Зависит от нагрузки приложений
  • Можно добавлять и убирать без downtime
  • В облаке: cluster autoscaler добавляет ноды автоматически при нехватке ресурсов

Когда использовать Kubernetes, а когда нет

Kubernetes нужен когда:

  • 20+ микросервисов, которыми невозможно управлять вручную
  • Требуется автомасштабирование по нагрузке
  • Multi-cloud или hybrid стратегия (нужна портабельность)
  • Zero-downtime deploys обязательны
  • Self-healing критичен для бизнеса

Kubernetes НЕ нужен когда:

  • Монолитное приложение (один процесс, один сервер)
  • Менее 5 микросервисов (overhead Kubernetes превышает пользу)
  • Нет ресурсов на обучение команды (кривая обучения крутая)
  • Нет готовности к начальным затратам (K8s потребляет ресурсы сам по себе)
  • Простой проект, который можно развернуть на одном сервере с Docker Compose

Managed vs Self-managed

Managed Kubernetes (рекомендуется)

Облачный провайдер управляет Control Plane — ты только деплоишь приложения.

ПровайдерСервисОсобенности
GoogleGKEИсторически лучшая интеграция (создатели K8s)
AmazonEKSГлубокая интеграция с AWS-сервисами
AzureAKSБесплатный control plane

Преимущества managed: не нужно обновлять control plane, автоматические бэкапы etcd, встроенный мониторинг, SLA на доступность API Server. Использовать K8s в 10 раз проще, чем управлять им.

Self-managed

Инструменты: kubeadm, Rancher, OpenShift. Ты управляешь всем сам — от обновлений etcd до сертификатов. Это сложно и требует глубокой экспертизы. Подходит, когда есть жёсткие требования по безопасности или данные не могут покидать периметр.

Hybrid

On-prem кластер + облачные worker-ноды при пиках нагрузки. Стабильная нагрузка идёт на свои серверы, пики перетекают в облако.


Пример: Go-приложение в Kubernetes

Рассмотрим полный пример деплоя Go-сервиса. Допустим, у нас есть HTTP-сервер:

go
package main import ( "fmt" "log" "net/http" "os" ) func main() { port := os.Getenv("PORT") if port == "" { port = "8080" } http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintln(w, "ok") }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { hostname, _ := os.Hostname() fmt.Fprintf(w, "Hello from %s\n", hostname) }) log.Printf("Starting server on :%s", port) log.Fatal(http.ListenAndServe(":"+port, nil)) }

Манифест для Kubernetes:

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: hello-go labels: app: hello-go spec: replicas: 3 selector: matchLabels: app: hello-go template: metadata: labels: app: hello-go spec: containers: - name: hello-go image: myregistry/hello-go:1.0 ports: - containerPort: 8080 env: - name: PORT value: "8080" resources: requests: cpu: "100m" memory: "64Mi" limits: cpu: "200m" memory: "128Mi" livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 3 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: hello-go spec: selector: app: hello-go ports: - port: 80 targetPort: 8080 type: ClusterIP
bash
# Применение манифеста kubectl apply -f hello-go.yaml # Проверка статуса kubectl get pods -l app=hello-go kubectl get svc hello-go # Просмотр логов kubectl logs -l app=hello-go --all-containers # Масштабирование kubectl scale deployment hello-go --replicas=5 # Обновление образа kubectl set image deployment/hello-go hello-go=myregistry/hello-go:2.0

Вопросы на собеседовании

Что такое Kubernetes и какую проблему он решает?

Kubernetes — система оркестрации контейнеров, автоматизирующая деплой, масштабирование и управление контейнеризированными приложениями на кластере машин. Решает проблему управления большим количеством микросервисов: service discovery, self-healing, rolling updates, auto-scaling. Без K8s ручное управление 100+ микросервисами на множестве серверов невозможно.

В чём разница между декларативным и императивным подходом в K8s?

Императивный подход: ты говоришь системе как сделать каждый шаг (создай контейнер, настрой балансировщик, подключи сеть). Декларативный: ты описываешь что хочешь (YAML-манифест с desired state), а Kubernetes сам решает, как достичь этого состояния и постоянно поддерживает его через reconciliation loop. При изменении манифеста K8s вычисляет разницу и приводит систему к новому состоянию.

Из каких компонентов состоит Control Plane?

Четыре ключевых компонента: (1) API Server — единая точка входа, RESTful API, stateless; (2) etcd — распределённое key-value хранилище всего состояния кластера (Raft consensus); (3) Scheduler — назначает pod-ы на ноды по ресурсам, affinity, taints; (4) Controller Manager — набор контроллеров (ReplicaSet, Deployment, Node, Job), каждый из которых реализует reconciliation loop для своего типа объекта. Все компоненты общаются только через API Server.

Что такое reconciliation loop?

Бесконечный цикл: контроллер читает desired state (spec) из API, сравнивает с actual state (status) в кластере, исправляет разницу. Например, ReplicaSet controller видит replicas: 3 в spec, но обнаруживает только 2 работающих pod-а — создаёт ещё один. Это фундаментальный паттерн, обеспечивающий self-healing: система постоянно стремится к желаемому состоянию.

Какова роль Kubelet и чем он отличается от Scheduler?

Scheduler выбирает, на какую ноду разместить pod (записывает nodeName в объект Pod через API Server), но сам не запускает ничего. Kubelet — агент на каждой worker-ноде, который наблюдает за pod-ами, назначенными на его ноду, и инструктирует Container Runtime (containerd) запустить контейнеры. Kubelet также выполняет health-проверки (probes) и отправляет статус обратно в API Server.

Когда Kubernetes не нужен?

Когда overhead превышает пользу: монолитное приложение на одном сервере; менее 5 сервисов (Docker Compose достаточно); нет ресурсов на обучение команды (кривая обучения K8s крутая); нет требований к автоскейлингу, self-healing или zero-downtime deploys. K8s потребляет ресурсы сам по себе (control plane, etcd, system pods), что не оправдано для маленьких проектов.

В чём отличие Managed Kubernetes (EKS, GKE, AKS) от Self-managed?

В managed-варианте облачный провайдер управляет Control Plane: обновления, бэкапы etcd, мониторинг, сертификаты, HA. Ты управляешь только worker-нодами и деплоишь приложения. В self-managed (kubeadm, Rancher) ты отвечаешь за всё: от установки etcd до ротации сертификатов. Managed рекомендуется в большинстве случаев — использовать K8s в 10 раз проще, чем управлять им.


Задачи

Задача 1. Анализ манифеста

Уровень: Средняя

Что проверяет: понимание структуры манифеста и связи между объектами

Условие: Дан манифест. Определи: сколько pod-ов будет запущено, на каком порту приложение будет доступно внутри кластера через Service, и что произойдёт, если один pod упадёт.

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: api-server spec: replicas: 4 selector: matchLabels: app: api version: v2 template: metadata: labels: app: api version: v2 spec: containers: - name: api image: myregistry/api:2.0 ports: - containerPort: 3000 --- apiVersion: v1 kind: Service metadata: name: api-service spec: selector: app: api ports: - port: 80 targetPort: 3000

Ответ:

Будет запущено 4 pod-а (replicas: 4). Внутри кластера приложение доступно по адресу api-service:80 (Service слушает на порту 80 и перенаправляет трафик на containerPort 3000). Если один pod упадёт, ReplicaSet controller обнаружит, что текущее количество реплик (3) отличается от желаемого (4), и создаст новый pod. Service автоматически перенаправит трафик только на здоровые pod-ы. Обрати внимание: selector Service использует только label app: api, поэтому он будет маршрутизировать трафик ко всем pod-ам с этим label-ом, включая pod-ы других версий, если они существуют.

Задача 2. Порядок запуска

Уровень: Средняя

Что проверяет: понимание взаимодействия компонентов кластера

Условие: Опиши пошагово, что происходит от момента выполнения kubectl apply -f deployment.yaml до момента, когда приложение готово обслуживать трафик. Укажи, какой компонент кластера выполняет каждый шаг и через что компоненты общаются.

Ответ:

  1. kubectl отправляет HTTP POST с манифестом на API Server
  2. API Server валидирует манифест, сохраняет объект Deployment в etcd
  3. Deployment controller (через watch на API Server) замечает новый Deployment, создаёт объект ReplicaSet через API Server
  4. ReplicaSet controller замечает новый ReplicaSet, создаёт N объектов Pod (по числу replicas) через API Server. Pod-ы сохраняются с пустым nodeName
  5. Scheduler (через watch на API Server) замечает pod-ы без ноды, оценивает ресурсы и ограничения, записывает nodeName для каждого pod-а через API Server
  6. Kubelet на соответствующей ноде (через watch на API Server) замечает pod, назначенный на его ноду, отправляет команду Container Runtime (containerd) через CRI — скачать образ и запустить контейнер
  7. Kubelet выполняет readiness probe; когда probe проходит, обновляет статус pod-а как Ready через API Server
  8. EndpointSlice controller добавляет IP pod-а в endpoints Service
  9. kube-proxy на каждой ноде обновляет iptables/IPVS правила
  10. Трафик, приходящий на Service, балансируется на все Ready pod-ы

Все компоненты общаются исключительно через API Server — прямого взаимодействия между ними нет.

Задача 3. Диагностика проблемы

Уровень: Средняя

Что проверяет: практические навыки отладки

Условие: После kubectl apply -f app.yaml pod остаётся в статусе Pending более 5 минут. Какие шаги ты предпримешь для диагностики? Назови минимум 3 возможные причины.

Ответ:

Шаги диагностики:

bash
kubectl describe pod <name> # смотрим Events и Conditions kubectl get events --sort-by='.lastTimestamp' # все события kubectl get nodes # проверяем состояние нод kubectl describe nodes # смотрим ресурсы и taints

Возможные причины Pending: (1) недостаточно ресурсов на нодах — requests pod-а (CPU/memory) превышают доступное на всех нодах; (2) pod имеет nodeSelector или nodeAffinity, и ни одна нода не соответствует требованиям; (3) все подходящие ноды имеют taint, а у pod-а нет соответствующей toleration; (4) PersistentVolumeClaim не может быть привязан к PersistentVolume (нет доступных PV нужного размера/класса); (5) кластер достиг лимита pod-ов на ноду (по умолчанию 110).


Интерактивная практика

Quiz+10 XP

Где в Kubernetes хранится желаемое состояние кластера, с которым сверяются контроллеры?

  • В kubelet на каждой ноде
  • В container runtime
  • В API Server и его хранилище etcd
  • В локальном kubeconfig разработчика
Predict+15 XP

Что выведет этот код?

go
package main import "fmt" func podPhase(scheduled bool, containersReady bool) string { if !scheduled { return "Pending" } if containersReady { return "Running" } return "Running" } func main() { fmt.Println(podPhase(false, false)) fmt.Println(podPhase(true, true)) }
Задача+20 XP

Реализуй PendingReason: выбери первый диагностический фокус для pod-а в Pending.