← Back to notes

kube-apiserver: путь запроса
и внутреннее устройство


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.


18: Scheduling Constraints | 20: Authn/Authz/Admission

kube-apiserver: путь запроса и внутреннее устройство | Aleksandr Suprun