Aggregated API servers
CRD — основной способ расширить Kubernetes API, но не единственный. Есть второй, более тяжёлый и редкий, механизм — aggregated API server. Идея: к kube-apiserver «подключается» отдельный полноценный API server, который обрабатывает запросы к определённой API group. С точки зрения клиента всё прозрачно — он по-прежнему дёргает kubectl get через kube-apiserver, но за scene запрос проксируется на отдельный сервер. Каноничный пример — metrics-server (тот, что отвечает за kubectl top).
REST поверх HTTP: resources, uniform interface, statelessness
Что такое aggregated API server
Aggregated API server (AAS) — это самостоятельный HTTP-сервер, который:
- Реализует Kubernetes API contract: те же verbs (get, list, watch, create, update, patch, delete), тот же AdmissionReview, тот же OpenAPI discovery.
- Регистрируется в kube-apiserver через объект
APIServiceв группеapiregistration.k8s.io/v1. - Получает прокси-запросы от kube-apiserver: когда приходит запрос на API group, зарегистрированную через APIService, kube-apiserver делает HTTP-запрос на endpoint AAS и проксирует ответ.
AAS сам отвечает за storage, validation, business logic. Он может хранить данные в etcd (своём отдельном или shared с kube-apiserver), в SQL database, в внешнем service, в памяти, где угодно. Это полная свобода — и полная ответственность.
APIService: регистрация AAS
APIService — объект, который регистрирует aggregated API server в kube-apiserver:
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1beta1.metrics.k8s.io # обязательно <version>.<group>
spec:
group: metrics.k8s.io # какую API group обслуживает
version: v1beta1 # какую version
service:
name: metrics-server # Service в кластере → AAS Pods
namespace: kube-system
port: 443 # HTTPS endpoint AAS
insecureSkipTLSVerify: true # для self-signed cert AAS (production: caBundle)
caBundle: <base64 CA cert> # CA, подписавший cert AAS
groupPriorityMinimum: 100 # для merging conflicting groups (e.g. shadowing built-in)
versionPriority: 100 # ordering versions в API discovery
После создания этого объекта kube-apiserver запоминает: «всё, что приходит на /apis/metrics.k8s.io/v1beta1/..., проксировать на https://metrics-server.kube-system.svc:443».
Внутри Kubernetes есть специальный kube-aggregator компонент, встроенный в kube-apiserver, который читает APIService objects и поддерживает таблицу маршрутизации. Он же проверяет health AAS (через GET на endpoint), и помечает APIService.status.conditions[].type=Available. Если AAS недоступен — Available становится False, и kubectl получит 503.
CRD vs Aggregated API: сравнение
| Аспект | CRD | Aggregated API |
|---|---|---|
| Storage | etcd кластера (через kube-apiserver) | Любой: etcd, SQL DB, in-memory, external service |
| Validation | OpenAPI v3 schema (declarative) + CEL для cross-field | Custom Go код, полная свобода |
| Subresources | Basic: /status, /scale (declarative) | Любые custom: /metrics, /proxy, /portforward, что угодно |
| Authentication | через kube-apiserver | через kube-apiserver (proxy) |
| Authorization | через RBAC kube-apiserver | через RBAC kube-apiserver (или собственный) |
| Implementation | YAML manifest, без Go-кода | Реальный Go-сервер на k8s.io/apiserver framework |
| Deployment | kubectl apply CRD object | Deployment + Service + APIService + RBAC |
| Latency | Низкая (in-tree apiserver) | Higher (network hop kube-apiserver → AAS) |
| Сложность | Низкая (declarative) | Высокая (нужно понимать apiserver internals) |
Главное различие: CRD — это «расширение хранилища etcd через метаданные»; AAS — это «отдельный сервер, говорящий на K8s API protocol».
Примеры aggregated API server-ов
| AAS | API group | Что предоставляет |
|---|---|---|
| metrics-server | metrics.k8s.io/v1beta1 | PodMetrics, NodeMetrics — current CPU/memory usage. Используется kubectl top, HPA, VPA. In-memory storage. |
| custom-metrics-apiserver | custom.metrics.k8s.io/v1beta1 | Custom metrics для HPA (например, qps_per_pod из Prometheus). Adapter, проксирующий в Prometheus. |
| external-metrics-apiserver | external.metrics.k8s.io/v1beta1 | External metrics (CloudWatch, Datadog) для HPA. |
| service-catalog (deprecated) | servicecatalog.k8s.io/v1beta1 | Open Service Broker API integration. Сейчас deprecated; альтернатива — Crossplane (через CRD). |
| Knative Eventing (частично) | eventing.knative.dev/v1 | В основном CRD, но некоторые subresources — AAS. |
| virt-api (KubeVirt) | subresources.kubevirt.io/v1 | VM lifecycle subresources: /vnc, /console, /pause, /restart. CRD для main objects, AAS для action subresources. |
Канонический use-case AAS — metrics-server: не нужно хранить данные в etcd (они эфемерны), нужно собирать с многих kubelets, нужна низкая latency. CRD не подходит — etcd не дизайнено для high-throughput метрик.
Почему AAS — редкость
В практике 99% extension в Kubernetes — это CRD + Operator. AAS используется в очень узком наборе случаев:
- Данные с собственным жизненным циклом, отличным от объекта. metrics-server: метрики обновляются каждые 15s, etcd не подходит для таких write rates.
- Storage backend не etcd. Например, ML model registry с custom DB (объекты огромные, нужна versioning, etcd плохо подходит).
- Subresources с custom logic. KubeVirt /vnc — это не просто read-write объекта, это live WebSocket session к виртуальной машине.
- Performance critical. CRD идёт через generic etcd path; AAS может использовать оптимизированный backend.
В большинстве cases CRD + controller покрывают потребность. AAS требует на порядок больше work: реализовать REST API правильно, поддерживать watch streams, реализовать pagination, делать defaulting через webhook, делать version conversion, integrate с RBAC. Это полноценный сервер, а не yaml-объект.
Эвристика выбора: «нужен ли тебе собственный storage backend?» Если да — AAS. Если нет — CRD. В 99% случаев etcd работает отлично, и нужно «просто хранить декларативное состояние объекта» — это CRD-кейс.
Поток развёртывания metrics-server
Чтобы поставить metrics-server, нужен пакет ресурсов:
# 1. ServiceAccount + RBAC для metrics-server
apiVersion: v1
kind: ServiceAccount
metadata: {name: metrics-server, namespace: kube-system}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata: {name: system:metrics-server}
rules:
- apiGroups: [""]
resources: [nodes/metrics, pods]
verbs: [get, list, watch]
- apiGroups: [""]
resources: [nodes/stats, nodes/proxy]
verbs: [get]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata: {name: system:metrics-server}
subjects: [{kind: ServiceAccount, name: metrics-server, namespace: kube-system}]
roleRef: {kind: ClusterRole, name: system:metrics-server, apiGroup: rbac.authorization.k8s.io}
---
# 2. Deployment с metrics-server образом
apiVersion: apps/v1
kind: Deployment
metadata: {name: metrics-server, namespace: kube-system}
spec:
selector: {matchLabels: {k8s-app: metrics-server}}
template:
metadata: {labels: {k8s-app: metrics-server}}
spec:
serviceAccountName: metrics-server
containers:
- name: metrics-server
image: registry.k8s.io/metrics-server/metrics-server:v0.7.2
args:
- --secure-port=10250
- --cert-dir=/tmp
- --kubelet-preferred-address-types=InternalIP
- --kubelet-insecure-tls # для kind/minikube; production: проверенный CA
ports:
- containerPort: 10250
name: https
---
# 3. Service exposes Pods на 443
apiVersion: v1
kind: Service
metadata: {name: metrics-server, namespace: kube-system}
spec:
selector: {k8s-app: metrics-server}
ports:
- port: 443
targetPort: 10250
---
# 4. APIService — регистрирует AAS в kube-apiserver
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata: {name: v1beta1.metrics.k8s.io}
spec:
group: metrics.k8s.io
version: v1beta1
service: {name: metrics-server, namespace: kube-system, port: 443}
insecureSkipTLSVerify: true # production: caBundle
groupPriorityMinimum: 100
versionPriority: 100
После apply — kubectl top начинает работать через минуту-две (как только metrics-server начнёт scraping и накопит данные).
CKAD scope
CKAD не требует уметь писать AAS. Минимум:
- Знать, что есть такой механизм расширения.
- Понимать, что
metrics-server— это AAS, иkubectl topработает через него. - Различать CRD vs AAS концептуально.
- Уметь troubleshoot:
kubectl get apiservice→ ищиAvailable: False.
# Список всех APIServices в кластере
kubectl get apiservice
# Built-in APIServices (всегда Local: True) и aggregated (Local: False, Service: ...)
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml
# Если status.conditions показывает Available=False — AAS Pod упал или RBAC сломан
kubectl describe apiservice v1beta1.metrics.k8s.io
Killer-моменты
- AAS — это полноценный API server рядом с kube-apiserver. Не плагин, не YAML — Go-программа на framework apiserver-runtime.
- APIService — это «route entry» в kube-apiserver. Объект говорит: «всё на этом path проксировать на этот Service».
- AAS управляет своим storage. Это его килer feature и одновременно его сложность. CRD получает etcd «бесплатно», AAS должен выбрать и реализовать.
- metrics-server — единственный AAS, который встречается в каждом production-кластере. Без него HPA +
kubectl topне работают. - Если поставлен APIService и он недоступен (Pod упал), kubectl get на ту group вернёт 503 / timeout. Это легко диагностируется через
kubectl get apiservice.