用 curl 直接呼叫 Kubernetes API:從 kubectl 背後的 REST API 開始理解 Kubernetes

  • Kubernetes version:v1.35.5
  • Docker Engine:v29.5.3
  • 測試環境:Docker Desktop 內建 Kubernetes,provisioning method 選用 kind,2 個 nodes

平常要操作 Kubernetes,我們最常用的應該就是 kubectl ,像是:

kubectl get po
kubectl describe po <pod-name>
kubectl apply -f deployment.yaml

kubectl 很方便,幫我們把很多細節都處理好了,像是讀取 kubeconfig、處理認證、送 request 到 API Server,最後再把 response 整理成我們比較好讀的格式。

不過 Kubernetes 本質上還是透過 API Server 對外提供 API,除了 kubectl 之外,我們也可以透過 client libraries,或是直接送 REST request 來操作 Kubernetes 資源。

先回到一個基礎的問題:如果不用 kubectl get pods,有辦法直接用 curl 去問 Kubernetes API Server 嗎?

答案是:可以。

這篇會使用本地 Kubernetes cluster,並用 ServiceAccount 的短期 token 去讀取指定 namespace 裡的 Pod 列表。

查看 kubeconfig

筆者目前是使用 Docker Desktop 內建的 Kubernetes 功能建立本地測試 cluster,並在 Cluster settings 中選用 kind 作為 provisioning method,建立完成後,kubeconfig 也會一起設定好。

查看目前的使用的 context:

kubectl config current-context

也可以看目前 context 對應的 cluster、user 和 API Server endpoint:

kubectl config view --minify

Output:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://127.0.0.1:65469
  name: docker-desktop
contexts:
- context:
    cluster: docker-desktop
    user: docker-desktop
  name: docker-desktop
current-context: docker-desktop
kind: Config
users:
- name: docker-desktop
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED
  • server 是 Kubernetes API Server 的位置
  • certificate-authority-data 是用來驗證 API Server 憑證的 CA
  • client-certificate-dataclient-key-data 則是目前 kubeconfig 使用者的 client certificate / key

不過這篇不打算直接使用 kubeconfig 裡的 admin client certificate,而是建立一個權限比較小的 ServiceAccount,專門拿來讀 Pod。

建立測試資源

先建立一個 test.yaml

# 建立 test namespace
apiVersion: v1
kind: Namespace
metadata:
  name: test
---
# 建立 pod-reader ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: pod-reader
  namespace: test
---
# 建立一個只能讀取 test namespace Pods 的 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: test
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - watch
---
# 用 RoleBinding 把 Role 綁給 pod-reader
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-reader
  namespace: test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: pod-reader
subjects:
- kind: ServiceAccount
  name: pod-reader
  namespace: test
---
# 建立一個 nginx Pod,方便等等測試 API response
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: test
spec:
  containers:
  - name: nginx
    image: nginx:1.27

部署:

kubectl apply -f test.yaml

確認 Pod 有起來:

kubectl get po -n test

測試 RBAC 權限

接著確認 pod-reader 這個 ServiceAccount 的權限

預期有權限列出 test namespace 裡的 Pods:

# 顯示 yes
kubectl auth can-i list pods -n test --as=system:serviceaccount:test:pod-reader

預期沒有權限列出所有 namespace 的 Pods:

# 顯示 no
kubectl auth can-i list pods -A --as=system:serviceaccount:test:pod-reader

這代表 RBAC 有照我們預期運作,pod-reader 只是一個 namespace-scoped 的 ServiceAccount,並且只透過 RoleBinding 拿到 test namespace 裡的 Pod 讀取權限。

測試 API

這裡我們開啟 Terminal,並使用 curl 測試 API,讀者有習慣的工具也可以用,像是 Postman 等。

先進行測試前的配置作業:

# 取得目前 kubeconfig 使用的 API Server endpoint:
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
# 把 kubeconfig 裡的 CA 存成檔案:
kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt
# 建立 pod-reader 的短期 token
TOKEN=$(kubectl create token pod-reader -n test --duration=10m)

列出 test namespace 裡的 Pods,會看到 Kubernetes 回傳一個 PodList

curl --cacert ca.crt \
  --header "Authorization: Bearer $TOKEN" \
  -X GET "$APISERVER/api/v1/namespaces/test/pods"

故意測試列出所有 namespace 的 Pods,這次會失敗,會看到 Forbidden 的錯誤訊息:

curl --cacert ca.crt \
  --header "Authorization: Bearer $TOKEN" \
  -X GET "$APISERVER/api/v1/pods"

可以得知 kubectl get pods 其實背後也是呼叫 API Server,拿到 JSON response 之後,再整理成我們平常看到的表格。