Kubernetes — обзор и архитектура
Kubernetes (K8s) — система автоматизации деплоя и управления контейнеризированными приложениями на кластере машин. Если Docker решает проблему "как запустить приложение в контейнере", то Kubernetes решает другую — "как управлять тысячами контейнеров на десятках серверов". Проект вырос из внутренних систем Google (Borg/Omega), где обрабатывалось более 2 миллиардов контейнеров в неделю, и стал open source в 2014 году.
Аналогия: операционная система управляет процессами на одной машине, Kubernetes управляет контейнерами на множестве машин. Ты выступаешь в роли капитана — решаешь куда плыть. K8s выступает рулевым — ведёт корабль по курсу и докладывает обстановку.
Зачем нужен Kubernetes
В мире монолитов ручное управление допустимо: одно приложение, один деплой, один сервер. С переходом на микросервисы (десятки и сотни отдельных сервисов) ручной менеджмент становится невозможным.
| Аспект | Монолит | Микросервисы |
|---|---|---|
| Приложений | 1 | 100+ |
| Деплоев | 1 | 100+ |
| Ручное управление | допустимо | невозможно |
Kubernetes автоматизирует то, что невозможно делать руками:
| Возможность | Описание |
|---|---|
| Service discovery | Приложения находят друг друга по имени, без хардкода адресов |
| Horizontal scaling | Автоматическое масштабирование по нагрузке (HPA) |
| Load balancing | Распределение трафика между репликами |
| Self-healing | Перезапуск упавших контейнеров, перенос с упавших нод |
| Leader election | Встроенный механизм выбора лидера для stateful-сервисов |
| Rolling updates | Обновление приложений без downtime |
| Config management | ConfigMaps и 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 решает, как его достичь и поддерживать.
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 или между собой.
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-ноды для высокой доступности.
+---------------------------------------------+
| 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-ноды — это машины, на которых запускаются приложения. Каждая нода содержит три обязательных компонента.
+--------------------------------------+
| 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) — исправь разницу".
Ты 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, происходит цепочка событий:
- kubectl -> API Server — манифест отправляется в API Server, который валидирует объекты и сохраняет их в etcd
- Controller замечает новый Deployment, создаёт ReplicaSet; ReplicaSet controller создаёт Pod-объекты
- Scheduler замечает Pod-ы без назначенной ноды, выбирает лучшую ноду для каждого, записывает
nodeName - Kubelet на назначенной ноде замечает Pod, инструктирует Container Runtime запустить контейнер
- kube-proxy замечает готовый Pod, настраивает сетевые правила для Service (load balancing)
- 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-манифестом из четырёх основных секций:
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 | Объекты |
|---|---|
| v1 | Pod, Service, ConfigMap, Node, Secret (core-группа) |
| apps/v1 | Deployment, ReplicaSet, StatefulSet, DaemonSet |
| batch/v1 | Job, CronJob |
| networking.k8s.io/v1 | Ingress, NetworkPolicy |
Conditions — состояние объекта
Status объекта часто содержит массив conditions — независимых друг от друга аспектов состояния:
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.
kubectl get events # все события
kubectl get events --field-selector type=Warning # только проблемы
kubectl describe pod my-app # события конкретного pod-а
kubectl — основные команды
kubectl — основной инструмент для работы с кластером. Все команды отправляют HTTP-запросы к API Server.
Просмотр объектов
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
Управление объектами
kubectl apply -f manifest.yaml # создать/обновить объект из файла
kubectl delete -f manifest.yaml # удалить объект
kubectl delete pod my-app # удалить конкретный pod
kubectl scale deployment my-app --replicas=5 # изменить количество реплик
Документация по полям
kubectl explain pod # документация по типу Pod
kubectl explain pod.spec.containers # drill down в конкретное поле
kubectl explain pods --recursive # полное дерево полей
Отладка
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, но почти всегда установлены в кластере:
| Компонент | Назначение |
|---|---|
| CoreDNS | DNS-сервер кластера для 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 — ты только деплоишь приложения.
| Провайдер | Сервис | Особенности |
|---|---|---|
| GKE | Исторически лучшая интеграция (создатели K8s) | |
| Amazon | EKS | Глубокая интеграция с AWS-сервисами |
| Azure | AKS | Бесплатный 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-сервер:
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:
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
# Применение манифеста
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 упадёт.
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 до момента, когда приложение готово обслуживать трафик. Укажи, какой компонент кластера выполняет каждый шаг и через что компоненты общаются.
Ответ:
- kubectl отправляет HTTP POST с манифестом на API Server
- API Server валидирует манифест, сохраняет объект Deployment в etcd
- Deployment controller (через watch на API Server) замечает новый Deployment, создаёт объект ReplicaSet через API Server
- ReplicaSet controller замечает новый ReplicaSet, создаёт N объектов Pod (по числу replicas) через API Server. Pod-ы сохраняются с пустым
nodeName - Scheduler (через watch на API Server) замечает pod-ы без ноды, оценивает ресурсы и ограничения, записывает
nodeNameдля каждого pod-а через API Server - Kubelet на соответствующей ноде (через watch на API Server) замечает pod, назначенный на его ноду, отправляет команду Container Runtime (containerd) через CRI — скачать образ и запустить контейнер
- Kubelet выполняет readiness probe; когда probe проходит, обновляет статус pod-а как Ready через API Server
- EndpointSlice controller добавляет IP pod-а в endpoints Service
- kube-proxy на каждой ноде обновляет iptables/IPVS правила
- Трафик, приходящий на Service, балансируется на все Ready pod-ы
Все компоненты общаются исключительно через API Server — прямого взаимодействия между ними нет.
Задача 3. Диагностика проблемы
Уровень: Средняя
Что проверяет: практические навыки отладки
Условие: После kubectl apply -f app.yaml pod остаётся в статусе Pending более 5 минут. Какие шаги ты предпримешь для диагностики? Назови минимум 3 возможные причины.
Ответ:
Шаги диагностики:
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).
Интерактивная практика
Где в Kubernetes хранится желаемое состояние кластера, с которым сверяются контроллеры?
Что выведет этот код?
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))
}
Реализуй PendingReason: выбери первый диагностический фокус для pod-а в Pending.