← 18: Scheduling Constraints | 20: Authn/Authz/Admission →
Что такое kube-apiserver
kube-apiserver — центральный узел control plane. Каждое взаимодействие с кластером проходит через него: kubectl, controllers, scheduler, kubelet общаются только через API server. Pipeline обработки запросов включает policy enforcement, throttling и persistence.
API server отвечает за persistence (сохранение желаемого состояния в etcd), но не за convergence (приведение кластера в это состояние). Всё, что происходит после 200 OK — ответственность controllers, scheduler и kubelet.
[source: kubernetes.io/docs/concepts/overview/components/#kube-apiserver]
Три пути через API server
Три типа запросов с разной длиной pipeline и разными failure modes.
Успешный GET не доказывает, что write path работает. Успешный write не доказывает, что система сконвергировала к желаемому состоянию.
Полный write path
Client (kubectl, controller, kubelet)
│
▼
TLS termination
│
▼
Authentication ──── 401 Unauthorized
│
▼
Authorization ───── 403 Forbidden
│
▼
API Priority & Fairness ── 429 Too Many Requests
│
▼
Mutating Admission Webhooks ── reject
│
▼
Object Schema Validation ───── reject
│
▼
Validating Admission Webhooks ── reject
│
▼
Persist to etcd (через Raft consensus)
│
▼
Response 200/201 to client
Каждая стрелка — точка, где запрос может не дойти до storage. Запрос проходит все стадии синхронно: клиент получает ответ только после записи в etcd.
[source: kubernetes.io/docs/reference/access-authn-authz/controlling-access/]
Read path и watch cache
Read path короче — admission webhooks не вызываются:
Client
│
▼
TLS → Authentication → Authorization → APF
│
▼
Handler читает из in-memory cache или etcd
│
▼
Response
GET и LIST используют семантику resourceVersion. В современных версиях Kubernetes consistent reads могут обслуживаться из watch cache, если кэш подтверждён как свежий относительно etcd; при неподходящей версии etcd или недоступной свежести apiserver fallback-ится к etcd.
# Consistent read: самое свежее состояние на момент обработки запроса
kubectl get pods -n default --resource-version=""
Watch path
Watch — это long-lived HTTP streaming connection. Informers в controllers, scheduler и kubelet используют watch для получения изменений в реальном времени.
Client (informer)
│
▼
TLS → Authentication → Authorization → APF
│
▼
API server открывает watch stream
│
▼
При каждом изменении объекта в etcd:
etcd watch → API server → serialize event → отправить клиенту
Если watch разрывается, informer переподключается и делает re-list для синхронизации. Это level-triggered design: при переподключении informer видит полное текущее состояние и не теряет события.
[source: kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes]
API Priority and Fairness (APF)
APF (GA с Kubernetes 1.29) стоит между authorization и обработкой запроса. Задача — защитить API server от перегрузки и обеспечить fair access между разными клиентами и типами запросов.
[source: kubernetes.io/docs/concepts/cluster-administration/flow-control/]
Как работает
Входящий запрос
│
▼
FlowSchema (классификация)
│ Матчит по: user, group, namespace, resource, verb
│
▼
PriorityLevelConfiguration (очередь)
│ Определяет: concurrency shares, queue depth, hand size
│
├── capacity есть → обработать запрос
│
└── capacity нет → поставить в очередь или reject 429
Пример FlowSchema
apiVersion: flowcontrol.apiserver.k8s.io/v1
kind: FlowSchema
metadata:
name: system-leader-election
spec:
priorityLevelConfiguration:
name: leader-election # высокий приоритет
matchingPrecedence: 100
rules:
- subjects:
- kind: ServiceAccount
serviceAccount:
name: "*"
namespace: kube-system
resourceRules:
- verbs: ["get", "update"]
apiGroups: ["coordination.k8s.io"]
resources: ["leases"]
Встроенные уровни приоритета
APF создаёт неочевидную ситуацию: API server работает, healthz отвечает 200, но пользовательские запросы получают 429 Too Many Requests. Мониторинг одного healthz endpoint недостаточен.
Что реально происходит при kubectl apply
Разберём kubectl apply -f deployment.yaml по шагам:
1. TLS handshake
2. Authentication: kubeconfig → client cert → username=developer, groups=[dev-team]
3. Authorization: RBAC → verb=create, resource=deployments, namespace=app → Allow
4. APF: classify → workload-low → capacity есть → пропустить
5. Mutating Admission:
- LimitRanger: добавляет default resource limits
- Istio webhook: inject sidecar container
- Custom webhook: добавляет labels
6. Schema Validation: проверяет spec после mutations
7. Validating Admission:
- PodSecurity: проверяет security context
- ResourceQuota: проверяет, что namespace quota не превышена
8. Persist: запись в etcd через Raft → quorum ack → commit
9. Response: 201 Created
Deployment создан в etcd. Но Pod-ы ещё не существуют — это задача controller-manager. Контейнеры не запущены — это задача scheduler и kubelet.
Почему 200 OK не означает "workload работает"
kubectl apply -f deployment.yaml
# → 200 OK ← API server сохранил объект
# Дальше (всё асинхронно):
# → Deployment controller создаёт ReplicaSet
# → ReplicaSet controller создаёт Pod objects
# → Scheduler выбирает node и делает bind
# → kubelet скачивает image и запускает контейнеры
# Между "200 OK" и "контейнер запущен" может пройти
# от секунд до минут, или workload может вообще не запуститься.
Сигналы мониторинга
Комбинация, которую легко пропустить:
apiserver_request_total{code="200", verb="GET"} → растёт (reads OK)
apiserver_request_total{code="500", verb="POST"} → растёт (writes fail)
apiserver_request_total{code="429"} → растёт (throttling)
API "работает", но кластер неуправляем. GET отвечает, writes ломаются. Без разделения метрик по verb и code — этого не увидеть.
Полная сводная таблица: что на каком шаге
DELETE проходит через admission, но schema validation обычно не применяется (нет тела объекта). Admission webhooks могут заблокировать удаление — типичный use case: защита critical объектов от случайного удаления.
Когда использовать
Прямое взаимодействие с API server актуально когда:
- Raw API запрос, который
kubectlне поддерживает:kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes - Отладка APF throttling: FlowSchema/PriorityLevel объекты в кластере
- Webhook блокирует запросы: включить audit logging на API server
- Кастомный клиент: client-go informer pattern, не polling
Когда APF важен:
- Много операторов/контроллеров — конкурируют за capacity
- CI/CD с массовыми kubectl apply — могут выжечь workload-low
[source: kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/]
Типичные ошибки
1. Считать healthz=200 признаком здорового кластера
healthz проверяет только жизнеспособность процесса, не работоспособность write path. Кластер может быть read-only (etcd потерял quorum) при healthz=200.
2. Игнорировать 429 в метриках
APF rejection — это не "кто-то делает слишком много запросов". Это сигнал, что API server перегружен. Обычная причина — слишком много controllers или операторов без rate limiting.
3. Не учитывать async nature после apply
kubectl apply + 200 OK ≠ workload running. Нужен kubectl rollout status или проверка через conditions, а не просто выход команды apply.
4. Webhook с failurePolicy: Fail без high availability
Один инстанс webhook + failurePolicy: Fail = потенциальный deadlock кластера при рестарте webhook pod. Всегда минимум 2 реплики с podAntiAffinity.
5. Неправильное понимание read consistency
resourceVersion задаёт требования к свежести чтения. Для операций, где важно самое свежее состояние на момент запроса, явно запрашивай consistent read и учитывай, что apiserver может обслужить его из подтверждённо свежего watch cache, а не обязательно напрямую из etcd.
Альтернативы
ValidatingAdmissionPolicy (CEL) вместо validating webhooks
С Kubernetes 1.30 (GA) — политики валидации без внешних HTTP сервисов. Работают in-process в API server, нет network dependency, нет availability risk. Ограничение: только validation, только CEL expressions.
Aggregated API Server
Для кастомных API (не CRD) — отдельный API server, зарегистрированный через APIService. Запросы проксируются через kube-apiserver. Используется metrics-server, custom metrics adapters.