API Aggregation: Gắn API Server Thứ Hai
Bài 57 thêm kiểu mới bằng CRD: API server chính phục vụ và lưu nó trong etcd. API aggregation là cách mở rộng khác — không thêm kiểu vào API server chính, mà gắn hẳn một API server thứ hai vào sau nó. API server chính nhận request cho một nhóm API rồi proxy tới server thứ hai đó, và server thứ hai tự quyết lưu trữ thế nào. Cụm ta không cần dựng gì mới để xem: metrics-server cài từ Bài 39 chính là một API aggregated đang chạy.
Aggregation khác CRD ở đâu
CRD: Aggregation:
┌─────────────────────┐ ┌─────────────────────┐
│ kube-apiserver │ │ kube-apiserver │
│ /apis/kkloud.io │ │ /apis/metrics.k8s.io ─┐ proxy
│ ↓ lưu etcd │ │ │ │
│ [etcd] │ └──────────────────────┘ ▼
└─────────────────────┘ ┌──────────────────────┐
API server CHÍNH phục vụ │ metrics-server │
+ lưu trong etcd │ (apiserver thứ hai) │
│ tính live, KHÔNG etcd │
└──────────────────────┘
CRD: kiểu mới do API server chính phục vụ, dữ liệu nằm etcd. Aggregation: một API server riêng phục vụ nhóm API của nó, lưu trữ theo cách riêng — có thể là database khác, hoặc tính tại chỗ không lưu gì. metrics-server chọn cách sau: số liệu CPU/memory tính live từ kubelet, không cất vào etcd.
APIService đăng ký server thứ hai
Một object APIService "nhận" một nhóm API và trỏ về Service phục vụ nó. Xem cái metrics-server đăng ký:
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl get apiservice v1beta1.metrics.k8s.io \
-o jsonpath='group={.spec.group} version={.spec.version} service={.spec.service.namespace}/{.spec.service.name} available={.status.conditions[0].status}'
NAME SERVICE AVAILABLE AGE
v1beta1.metrics.k8s.io kube-system/metrics-server True 6h30m
group=metrics.k8s.io version=v1beta1 service=kube-system/metrics-server available=True
v1beta1.metrics.k8s.io báo cho API server chính: mọi request tới /apis/metrics.k8s.io/v1beta1 hãy proxy về Service kube-system/metrics-server. So với danh sách APIService, các nhóm gốc ghi Local (do chính API server chính phục vụ), còn metrics ghi một Service:
kubectl get apiservice | grep -E "Local|metrics" | head
v1. Local True 9h
v1.apps Local True 9h
v1.authentication.k8s.io Local True 9h
v1beta1.metrics.k8s.io kube-system/metrics-server True 6h30m
Local nghĩa là API server chính tự phục vụ; metrics thì được giao cho một server khác.
Aggregation layer xác thực bằng front-proxy cert
Để API server chính gọi sang server thứ hai một cách tin cậy, nó dùng một bộ cert riêng — front-proxy, ký từ Bài 4. Bài 39 đã đặt các cờ này lên API server (cùng với --enable-aggregator-routing để fix việc control plane không chạy CNI):
ssh controller-0 'grep -oE "\-\-(requestheader-[a-z-]*|proxy-client-[a-z-]*|enable-aggregator-routing)[^ ]*" \
/etc/systemd/system/kube-apiserver.service | sort -u'
--enable-aggregator-routing=true
--proxy-client-cert-file=/var/lib/kubernetes/front-proxy-client.pem
--proxy-client-key-file=/var/lib/kubernetes/front-proxy-client-key.pem
--requestheader-allowed-names=front-proxy-client
--requestheader-client-ca-file=/var/lib/kubernetes/front-proxy-ca.pem
--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
Khi proxy request, API server chính xác thực người dùng gốc, rồi gắn danh tính họ vào các header X-Remote-User/X-Remote-Group và ký bằng front-proxy-client.pem. Server thứ hai tin các header đó vì chúng tới qua cert mà front-proxy-ca.pem chứng nhận. Đây là lý do Bài 4 phải tạo riêng một CA front-proxy.
Dữ liệu live, không nằm etcd
Gọi thẳng nhóm API aggregated để thấy số liệu thật, do metrics-server trả qua đường proxy:
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes
worker-0 {'cpu': '97791959n', 'memory': '980620Ki'}
worker-1 {'cpu': '139388326n', 'memory': '791264Ki'}
Số liệu này không lưu ở đâu cả — mỗi lần hỏi, metrics-server tính từ kubelet rồi trả. Kiểm chứng bằng cách tìm trong etcd: dữ liệu metrics không có, chỉ có đúng một key là chính object đăng ký:
sudo etcdctl ... get /registry/ --prefix --keys-only | grep metrics.k8s.io
/registry/apiregistration.k8s.io/apiservices/v1beta1.metrics.k8s.io
Chỉ một key — object APIService (bản thân nó là resource thường, lưu etcd như mọi object). Không có key nào chứa số đo node/pod. Đây là khác biệt then chốt với CRD ở Bài 57: Widget được lưu tại /registry/kkloud.io/widgets/..., còn metrics thì server thứ hai tính tại chỗ và không cất gì. Aggregation hợp đúng cho dữ liệu kiểu này — tính live, hoặc lưu nơi khác — mà CRD (luôn dùng etcd) không làm được.
🧹 Dọn dẹp
Bài này không tạo gì — chỉ soi metrics-server đã có từ Bài 39, vốn được giữ làm hạ tầng cho HPA/kubectl top. Không có gì để dọn. Lệnh dùng trong bài ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 60-api-aggregation.
Tổng kết
API aggregation gắn một API server thứ hai vào sau API server chính: một object APIService đăng ký một nhóm/version và trỏ về Service phục vụ nó, sau đó API server chính proxy mọi request của nhóm đó sang. Ta soi metrics-server (Bài 39) như một API aggregated thật — v1beta1.metrics.k8s.io trỏ kube-system/metrics-server, Available=True, trong khi nhóm gốc ghi Local. Aggregation layer xác thực bằng bộ cert front-proxy (Bài 4): API server chính gắn danh tính người dùng vào header X-Remote-* ký bởi front-proxy-client, server thứ hai tin nhờ front-proxy-ca. Khác CRD ở chỗ lưu trữ: kubectl get --raw /apis/metrics.k8s.io/... trả số liệu CPU/memory tính live, và etcd chỉ chứa đúng object APIService đăng ký, không có dữ liệu metrics — vì server thứ hai tự lo lưu trữ. Dùng aggregation khi cần dữ liệu tính live hoặc lưu ngoài etcd; dùng CRD khi chỉ cần thêm một kiểu lưu trong etcd.
Bốn bài qua mở rộng API server bằng dữ liệu và controller. Bài 61 khép Part XII ở một hướng mở rộng khác — phần cứng: device plugin cho phép node quảng bá tài nguyên ngoài CPU/memory (GPU, thiết bị đặc biệt) để pod xin và scheduler chia.