#Kubernetes21.10.2026

Как устроен Kubernetes API server

Разбор пути запроса от kubectl до etcd по материалу learnkube.com

Как устроен Kubernetes API server

Kubernetes API server — единственная точка входа в кластер: через него проходят запросы kubectl, scheduler, kubelet, CI/CD-пайплайны и все контроллеры. Прежде чем YAML попадёт в etcd, запрос проходит аутентификацию, авторизацию, admission control и схемную валидацию. Подробный разбор этого пути в январе 2026 года опубликовал learnkube.com — это первая часть серии из шести материалов о работе control plane Kubernetes.

Краткий ответ

Kubernetes API server — это HTTP-сервер, через который проходит каждое изменение в кластере. Запрос сначала проверяют аутентификация и RBAC-авторизация, затем mutating и validating admission-контроллеры дополняют и проверяют ресурс, после чего API server конвертирует его во внутреннее представление и записывает в etcd. Остальные компоненты кластера узнают об изменении через Watch API; напрямую к etcd обращается только сам API server.

Путь запроса: от HTTP-вызова до admission-контроллеров

Когда вы выполняете kubectl apply -f my.yaml, запрос уходит на обычный REST-эндпоинт. API server — это OpenAPI-совместимый HTTP-сервер, и его можно дёргать напрямую: kubectl proxy открывает доступ, а curl localhost:8001/api/v1/pods вернёт обычный JSON. Публикуемая OpenAPI-схема описывает каждый ресурс, поле и операцию — на ней держатся kubectl explain, автодополнение в редакторах и генерация документации; её можно забрать прямо из кластера командой kubectl get --raw /openapi/v3. Именно эта схема объясняет, зачем вообще нужен kubectl, если можно обойтись curl: перед отправкой kubectl скачивает OpenAPI-схему кластера и валидирует YAML локально, ловя опечатки в именах полей и несовпадения типов до того, как запрос уйдёт по сети.

Но локальная проверка не защищает от того, что произойдёт с ресурсом внутри самого API server. Там запрос последовательно проходит аутентификацию, авторизацию, mutating и validating admission-контроллеры, схемную валидацию и только затем слой хранения.

Аутентификация отвечает на вопрос «кто вы»: клиентские сертификаты, bearer-токены, OpenID Connect и другие методы собраны в цепочку аутентификаторов, которая пробует их по очереди. Если ни один не подошёл — 401 Unauthorized. Авторизация отвечает на вопрос «что вам можно». Самый распространённый механизм — RBAC: вы описываете Role с перечнем разрешённых действий и связываете её с пользователем или сервис-аккаунтом через RoleBinding. Это уровень косвенности, который экономит работу на масштабе — при изменении прав достаточно обновить одну Role, а не двадцать индивидуальных наборов разрешений. RBAC — не единственный вариант: API server также поддерживает webhook-авторизацию, ABAC и Node authorizer, который разрешает kubelet обращаться только к ресурсам собственной ноды. Если любая проверка проваливается, API server возвращает 401 или 403, и запрос не идёт дальше.

Mutation, схемная валидация и правила кластера

После авторизации в игру вступает Mutation Admission Controller — модуль, который дополняет присланный YAML. Если не указан imagePullPolicy, он проставит Always; для Pod подставит сервис-аккаунт по умолчанию и добавит том с токеном доступа. Поэтому ресурс, который возвращает kubectl get pod nginx -o yaml, почти никогда не совпадает с тем, что было отправлено: появляются terminationGracePeriodSeconds, dnsPolicy, смонтированный ServiceAccount-токен и другие поля.

Следом идёт быстрая схемная проверка — не для исходного YAML, его уже проверил kubectl, а для результата мутаций. Если мутирующий контроллер или вебхук случайно испортит значение поля, например превратит replicas: 3 в replicas: "three", схемная валидация становится последним барьером перед etcd.

Validation Admission Controller проверяет не структуру, а смысл запроса: существует ли namespace, не превышена ли квота, соблюдены ли организационные политики. Оба типа контроллеров расширяются через вебхуки: API server шлёт HTTP POST с деталями ресурса на внешний сервис, а тот возвращает allow или deny. Так работают Istio, который через мутирующий вебхук автоматически внедряет сайдкар в каждый под, и Kyverno, который вместо кода вебхук-сервера использует декларативные YAML-политики. У подхода есть цена: вебхук — это под в кластере, и при failurePolicy: Fail (безопасном значении по умолчанию) его падение блокирует приём всех ресурсов, которые он должен был проверить.

С версии 1.30 Kubernetes предлагает более лёгкую альтернативу — ValidatingAdmissionPolicy: правила на CEL (Common Expression Language) выполняются прямо внутри процесса API server, без отдельного пода, сертификатов и сетевого вызова. CEL намеренно ограничен — без циклов и неограниченной рекурсии, поэтому каждое выражение гарантированно завершается за миллисекунды. С версии 1.32 в альфа-статусе появился аналог для мутаций — MutatingAdmissionPolicy. Вебхуки остаются нужны для сложных случаев вроде обращения к внешним системам или кросс-ресурсных проверок, которые CEL выразить не может.

Запись в etcd: внутреннее представление вместо версии

Если ресурс прошёл все проверки, он сохраняется в etcd. API server десериализует тело HTTP-запроса и конвертирует версионированный ресурс (например, apps/v1 Deployment) во внутреннее, версионно-нейтральное представление. Слой хранения сериализует этот объект — обычно в Protobuf, а не в JSON, потому что компактнее, — и пишет в etcd по ключу вида /registry/<тип>/<namespace>/<имя>. После записи хранилище перечитывает объект, чтобы заполнить серверные поля вроде metadata.uid и metadata.resourceVersion. Как отмечает learnkube.com, API server — единственный компонент, который напрямую обращается к etcd: kubectl, scheduler, kubelet и контроллеры работают только через него, а etcd остаётся единым источником истины с одним «привратником».

Ресурс не хранится в той версии API, в которой его создали. Если бы Kubernetes держал в кластере вперемешку Deployment’ы в разных версиях API, каждый список пришлось бы конвертировать на лету. Вместо этого каждый ресурс хранится в едином внутреннем представлении (storage version), а при запросе конкретной версии API server конвертирует его на лету. Поэтому Kubernetes может выводить из употребления устаревшие версии API, не теряя данные: меняется только логика конвертации.

Server-Side Apply: кто последним изменил поле

API server не просто хранит ресурс — он запоминает, кто какое поле задал последним. Это называется field ownership, и на нём строится Server-Side Apply. У каждого поля есть «менеджер»: ваш kubectl apply — один, Horizontal Pod Autoscaler — другой, CI/CD-пайплайн — третий.

Типичная ситуация: вы разворачиваете ресурс с replicas: 3, позже HPA масштабирует его до replicas: 10. Если снова применить тот же YAML, где всё ещё написано replicas: 3, без отслеживания владения полями kubectl молча вернул бы три реплики. С Server-Side Apply API server обнаруживает конфликт между двумя менеджерами одного поля и требует явного решения, кто побеждает.

Этот механизм заменил прежний подход с аннотацией kubectl.kubernetes.io/last-applied-configuration — объёмным JSON, который раздувал ресурсы и работал только с kubectl. Отслеживание конфликтов теперь живёт в самом API server и работает для любого клиента: kubectl, Terraform, Helm, собственного оператора. Server-Side Apply стало поведением kubectl apply по умолчанию начиная с Kubernetes 1.22, что подтверждает и официальная документация Kubernetes.

Watch API, расширение через CRD и аудит

После записи в etcd кто-то должен узнать об изменении — например, под ещё нужно запланировать на ноду. За это отвечает Watch API: вместо «дай все поды сейчас» компонент подписывается на «сообщай о каждом изменении». kubectl get pods --watch под капотом открывает долгоживущий HTTP-запрос с параметром ?watch=1, и API server присылает события ADDED, MODIFIED, DELETED. Этим механизмом пользуется каждый компонент: kubelet следит за подами своей ноды, контроллеры — за своими ресурсами, scheduler кладёт под без назначенной ноды во внутреннюю очередь планирования. При разрыве соединения компонент переподключается с последним известным resourceVersion. Комбинацию «список плюс подписка плюс отслеживание версии» называют Shared Informer pattern.

На этом же конвейере строится расширение API. Aggregation layer позволяет зарегистрировать отдельный API-сервер через ресурс APIService — так устроен metrics-server: запрос kubectl top pods проксируется основным API server в соответствующий Service, при этом аутентификацию по-прежнему выполняет основной API server. Более распространённый способ — Custom Resource Definitions: CRD описывает новый тип ресурса, например Certificate из cert-manager, и Kubernetes обращается с ним как со встроенным объектом, проводя через тот же конвейер проверок и запись в etcd.

API server также ведёт встроенный аудит-лог: кто сделал запрос, что именно и чем это закончилось. Настраивается он через audit policy с уровнями None, Metadata, Request и RequestResponse, что позволяет логировать полностью чувствительные ресурсы вроде Secrets и ограничиваться метаданными для рутинного трафика.

Почему это важно

Для платформенных команд этот конвейер — не абстракция, а место, где реально что-то ломается. Упавший мутирующий или валидирующий вебхук с failurePolicy: Fail может остановить приём ресурсов во всём кластере. Конфликт Server-Side Apply без понимания field ownership выглядит как необъяснимый баг CI/CD, когда HPA и ручной kubectl apply борются за одно и то же поле.

CEL-политики выполняются внутри самого процесса API server — без отдельного пода, сетевого вызова и TLS-сертификатов. Для простых проверок вроде обязательных лейблов это, вероятно, снижает число внешних точек отказа в admission-цепочке, хотя прямых данных об этом эффекте источник не приводит. Для сложной логики — обращения к внешним системам, кросс-ресурсных проверок — вебхуки остаются единственным рабочим вариантом.

Что дальше

Если в кластере есть mutating или validating вебхуки, стоит проверить их failurePolicy и готовность обслуживающих подов — это типичная точка отказа при деплоях, особенно в момент обновления самого вебхук-сервиса. Для простых правил вроде обязательных лейблов имеет смысл сравнить ValidatingAdmissionPolicy с уже существующим вебхуком: меньше инфраструктуры и меньше риска уронить кластер вместе с одним подом. И если команда применяет YAML через kubectl apply, не задумываясь о других контроллерах, которые меняют те же ресурсы, стоит заранее разобраться, как Server-Side Apply сигнализирует о конфликтах полей — лучше на тестовом кластере, чем в проде в момент спора HPA и ручного деплоя за одно поле.

FAQ

Что такое admission controller в Kubernetes?

Admission controller — этап обработки запроса в API server, который выполняется после аутентификации и авторизации, но до записи ресурса в etcd. Он либо модифицирует ресурс (mutating), либо проверяет его на соответствие политикам (validating).

Чем mutating admission controller отличается от validating?

Mutating admission controller меняет ресурс: добавляет значения по умолчанию, внедряет сайдкары, проставляет недостающие поля. Validating admission controller ничего не меняет — он проверяет итоговый ресурс на соответствие бизнес-правилам и либо пропускает запрос дальше, либо отклоняет его с ошибкой.

Зачем Kubernetes хранит ресурсы во внутреннем представлении, а не в версии API?

Если бы Kubernetes хранил ресурсы в разных версиях API одновременно, каждый список пришлось бы конвертировать между версиями на лету. Единое внутреннее представление (storage version) позволяет API server конвертировать объект в нужную версию только при запросе, а не при каждой операции с данными, и безопасно выводить из употребления устаревшие версии API.

Чем ValidatingAdmissionPolicy отличается от обычного вебхука?

Вебхук — это внешний сервис, который API server вызывает по сети для каждого подходящего запроса; его падение при failurePolicy: Fail блокирует приём ресурсов. ValidatingAdmissionPolicy выполняет CEL-выражение прямо внутри процесса API server, без сетевого вызова, отдельного пода и TLS-сертификатов, что годится для простых проверок вроде обязательных лейблов.

С какой версии Kubernetes Server-Side Apply стало поведением kubectl apply по умолчанию?

Server-Side Apply стало поведением по умолчанию для kubectl apply начиная с Kubernetes 1.22. Если команда использовала kubectl apply в последние годы, она уже работает с этим механизмом.