寫在前面
如果是用的公有雲托管的 Kubernetes 叢集,控制面的元件都交由雲廠商托管的,那作為客戶的我們就省事了,基本不用操心 APIServer 的運維。個人也推薦使用雲廠商這個服務,畢竟 Kubernetes 還是有點複雜的,更新也不好搞,我們自己來維護整個叢集,成本效益有點低。當然,如果因為各種原因最後我們還是要維護控制面這些元件,那就要好好看看本系列接下來的幾篇部落格了。
黑盒測試
APIServer 在 Kubernetes 架構中非常核心,是所有 API 的入口,APIServer 也暴露了 metrics 資料,我們嘗試擷取一下:
[[email protected] etcd]# ss -tlpn|grep apiserver
LISTEN 0 128 *:6443 *:* users:(("kube-apiserver",pid=164445,fd=7))
[[email protected] etcd]# curl -s http://localhost:6443/metrics
Client sent an HTTP request to an HTTPS server.
[[email protected] etcd]# curl -s -k https://localhost:6443/metrics
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/metrics\"",
"reason": "Forbidden",
"details": {},
"code": 403
}
解釋一下上面的指令和結果。首先我通過 ss 指令檢視 apiserver 子產品監聽在哪些端口,發現這個程序在 6443 端口有監聽。然後,使用 curl 指令請求 6443 的 metrics 接口,結果又說這是一個 HTTPS Server,不能用 HTTP 協定請求。好,那我用 HTTPS 協定請求,自簽證書,加了 -k 參數,傳回 Forbidden,說沒權限通路
/metrics
接口。OK,那看來是需要 Token 鑒權,我們建立一下相關的 ServiceAccount。
準備認證資訊
下面的内容可以儲存為 auth-server.yaml。
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: categraf
rules:
- apiGroups: [""]
resources:
- nodes
- nodes/metrics
- nodes/stats
- nodes/proxy
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics", "/metrics/cadvisor"]
verbs: ["get"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: categraf
namespace: flashcat
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: categraf
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: categraf
subjects:
- kind: ServiceAccount
name: categraf
namespace: flashcat
在上一節《Kubernetes監控手冊05-監控Kubelet》中,我們為 daemonset 建立過認證資訊,那個認證資訊主要是用于調用 kubelet 的接口。而這次我們要調用的是 apiserver 的接口,是以增加了一些權限點,當然,上例 yaml 中給出的權限點有點多,沒關系,反正都是隻讀的,後面再需要其他權限的時候,省的再建立新的 ServiceAccount 了。與上一講相比,這次 ServiceAccount 名字改成了 categraf,與上一講用到的 ServiceAccount 區分開。
通過下面的指令建立相關内容,然後檢視一下是否建立成功:
[[email protected] yamls]# kubectl apply -f auth-server.yaml -n flashcat
clusterrole.rbac.authorization.k8s.io/categraf unchanged
serviceaccount/categraf unchanged
clusterrolebinding.rbac.authorization.k8s.io/categraf unchanged
[[email protected] yamls]# kubectl get sa categraf -n flashcat
NAME SECRETS AGE
categraf 1 7h13m
[[email protected] yamls]# kubectl get sa categraf -n flashcat -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"categraf","namespace":"flashcat"}}
creationTimestamp: "2022-11-28T05:00:17Z"
name: categraf
namespace: flashcat
resourceVersion: "127151612"
uid: 8b473b31-ce09-4abe-ae55-ea799160a9d5
secrets:
- name: categraf-token-6whbs
[[email protected] yamls]# kubectl get secret categraf-token-6whbs -n flashcat
NAME TYPE DATA AGE
categraf-token-6whbs kubernetes.io/service-account-token 3 7h15m
上例中,因為我之前建立過了,是以顯示的是 unchanged,擷取 sa 的時候,可以看到 AGE 已經七個多小時了。通過
-o yaml
可以看到 sa 對應的 secret 的名字,最下面那一行,可以看到 secret 名字是 categraf-token-6whbs。然後我們用這個 secret 中的 token 來調用一下 APIServer 試試:
[[email protected] yamls]# token=`kubectl get secret categraf-token-6whbs -n flashcat -o jsonpath={.data.token} | base64 -d`
[[email protected] yamls]# curl -s -k -H "Authorization: Bearer $token" https://localhost:6443/metrics > metrics
[[email protected] yamls]# head -n 6 metrics
# HELP aggregator_openapi_v2_regeneration_count [ALPHA] Counter of OpenAPI v2 spec regeneration count broken down by causing APIService name and reason.
# TYPE aggregator_openapi_v2_regeneration_count counter
aggregator_openapi_v2_regeneration_count{apiservice="*",reason="startup"} 0
aggregator_openapi_v2_regeneration_count{apiservice="k8s_internal_local_delegation_chain_0000000002",reason="update"} 0
aggregator_openapi_v2_regeneration_count{apiservice="v1beta1.metrics.k8s.io",reason="add"} 0
aggregator_openapi_v2_regeneration_count{apiservice="v1beta1.metrics.k8s.io",reason="update"} 0
OK,這個新的 Token 是可以擷取到資料的了,權限認證通過。
采集原理
既然 Token 已經有了,采集器抓取 APIServer 的資料的時候,隻要在 Header 裡傳入這個 Token 理論上就可以拿到資料了。如果 APIServer 是二進制方式部署,咱們就直接通過 Categraf 的 Prometheus 插件來抓取就可以了。如果 APIServer 是部署在 Kubernetes 的容器裡,咱們最好是使用服務發現機制來做。
支援 Kubernetes 服務發現的 agent 有不少,但是要說最原汁原味的還是 Prometheus 自身,Prometheus 新版本(v2.32.0)支援了 agent mode 模式,即把 Prometheus 程序當做采集器 agent,采集了資料之後通過 remote write 方式傳給中心(這裡使用早就準備好的 Nightingale 作為資料接收服務端)。那這裡我就使用 Prometheus 的 agent mode 方式來采集 APIServer。
部署 agent mode prometheus
首先準備一下 Prometheus agent 需要的配置檔案,我們做成一個 ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-agent-conf
labels:
name: prometheus-agent-conf
namespace: flashcat
data:
prometheus.yml: |-
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'apiserver'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
insecure_skip_verify: true
authorization:
credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https
remote_write:
- url: 'http://10.206.0.16:19000/prometheus/v1/write'
可以把上面的内容儲存為 prometheus-agent-configmap.yaml,然後
kubectl -f prometheus-agent-configmap.yaml
建立一下即可。
有了配置了,下面我們就可以部署 Prometheus 了,要把 Prometheus 程序當做 agent 來用,需要啟用這個 feature,通過指令行參數
--enable-feature=agent
即可輕松啟用了,我們把 agent mode 模式的 Prometheus 部署成一個 Deployment,單副本。
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-agent
namespace: flashcat
labels:
app: prometheus-agent
spec:
replicas: 1
selector:
matchLabels:
app: prometheus-agent
template:
metadata:
labels:
app: prometheus-agent
spec:
serviceAccountName: categraf
containers:
- name: prometheus
image: prom/prometheus
args:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--web.enable-lifecycle"
- "--enable-feature=agent"
ports:
- containerPort: 9090
resources:
requests:
cpu: 500m
memory: 500M
limits:
cpu: 1
memory: 1Gi
volumeMounts:
- name: prometheus-config-volume
mountPath: /etc/prometheus/
- name: prometheus-storage-volume
mountPath: /prometheus/
volumes:
- name: prometheus-config-volume
configMap:
defaultMode: 420
name: prometheus-agent-conf
- name: prometheus-storage-volume
emptyDir: {}
要特别注意
serviceAccountName: categraf
這一行内容别忘記了,以上 yaml 内容儲存為 prometheus-agent-deployment.yaml,然後 apply 一下:
[[email protected] yamls]$ kubectl apply -f prometheus-agent-deployment.yaml
deployment.apps/prometheus-agent created
可以通過
kubectl logs <podname> -n flashcat
檢視剛才建立的 prometheus-agent-xx 那個 Pod 的日志,如果沒有報錯,理論上就問題不大了。
檢視監控資料
在即時查詢裡查一下
apiserver_request_total
這個名額,如果可以查到,就說明資料上報是正常的。孔飛老師之前整理過夜莺的 Kubernetes / Apiserver 監控大盤,可以導入測試,位址在這裡。效果如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SYhZGOmVmYhdTN5cjZ2Y2Y3IzN2QjYiRzMzE2M0gjM28CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
另外,Apiserver 的關鍵名額的含義,孔飛老師也做了整理,我也給摘過來了:
# HELP apiserver_request_duration_seconds [STABLE] Response latency distribution in seconds for each verb, dry run value, group, version, resource, subresource, scope and component.
# TYPE apiserver_request_duration_seconds histogram
apiserver響應的時間分布,按照url 和 verb 分類
一般按照instance和verb+時間 彙聚
# HELP apiserver_request_total [STABLE] Counter of apiserver requests broken out for each verb, dry run value, group, version, resource, scope, component, and HTTP response code.
# TYPE apiserver_request_total counter
apiserver的請求總數,按照verb、 version、 group、resource、scope、component、 http傳回碼分類統計
# HELP apiserver_current_inflight_requests [STABLE] Maximal number of currently used inflight request limit of this apiserver per request kind in last second.
# TYPE apiserver_current_inflight_requests gauge
最大并發請求數, 按mutating(非get list watch的請求)和readOnly(get list watch)分别限制
超過max-requests-inflight(預設值400)和max-mutating-requests-inflight(預設200)的請求會被限流
apiserver變更時要注意觀察,也是回報叢集容量的一個重要名額
# HELP apiserver_response_sizes [STABLE] Response size distribution in bytes for each group, version, verb, resource, subresource, scope and component.
# TYPE apiserver_response_sizes histogram
apiserver 響應大小,機關byte, 按照verb、 version、 group、resource、scope、component分類統計
# HELP watch_cache_capacity [ALPHA] Total capacity of watch cache broken by resource type.
# TYPE watch_cache_capacity gauge
按照資源類型統計的watch緩存大小
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
每秒鐘使用者态和系統态cpu消耗時間, 計算apiserver程序的cpu的使用率
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
apiserver的記憶體使用量(機關:Byte)
# HELP workqueue_adds_total [ALPHA] Total number of adds handled by workqueue
# TYPE workqueue_adds_total counter
apiserver中包含的controller的工作隊列,已處理的任務總數
# HELP workqueue_depth [ALPHA] Current depth of workqueue
# TYPE workqueue_depth gauge
apiserver中包含的controller的工作隊列深度,表示目前隊列中要處理的任務的數量,數值越小越好
例如APIServiceRegistrationController admission_quota_controller
相關文章
- Kubernetes監控手冊01-體系介紹
- Kubernetes監控手冊02-宿主監控概述
- Kubernetes監控手冊03-宿主監控實操
- Kubernetes監控手冊04-監控Kube-Proxy
- Kubernetes監控手冊05-監控Kubelet
關于作者
本文作者秦曉輝,Flashcat合夥人,文章内容是Flashcat技術團隊共同沉澱的結晶,作者做了編輯整理,我們會持續輸出監控、穩定性保障相關的技術文章,文章可轉載,轉載請注明出處,尊重技術人員的成果。
如果對 Nightingale、Categraf、Prometheus 等技術感興趣,歡迎加入我們的微信群組,聯系我(picobyte)拉入部落,和社群同仁一起探讨監控技術。