java kubernetes client 擷取 叢集 metrics資訊
K8S client 擷取資源使用率和 metrics 資訊
擷取與展示 POD 級甚至 Container 級的資源使用率是很常見的釋出系統需求,然而網上并沒有什麼資料告訴大家怎麼做,本文将告訴大家原理以及 java 代碼實踐。
指令行擷取
其實 kubectl 是可以擷取到 node、pod、container 三個級别的資源使用率情況的,隻不過大家可能不了解。
[root@10-42-74-90 ~]# kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
10.42.187.205 1691m 5% 14876Mi 24%
10.42.37.63 513m 6% 12994Mi 92%
10.42.8.102 231m 2% 8124Mi 57%
利用 top 擷取了節點級别的資源使用率。
[root@10-42-74-90 ~]# kubectl top pods redis-master-fsx46
NAME CPU(cores) MEMORY(bytes)
redis-master-fsx46 1m 14Mi
擷取 default 命名空間下,redis-master-fsx46 這個 POD 的資源使用率。
[root@10-42-74-90 ~]# kubectl top pods redis-master-fsx46 --containers
POD NAME CPU(cores) MEMORY(bytes)
redis-master-fsx46 master 1m 14Mi
甚至列印出具體每個 container 的資源利用情況(這個 POD 隻有 1 個 container 叫做 master)。
[root@10-42-74-90 ~]# kubectl top pods -l name=redis-master
redis-master-fsx46 2m 14Mi
還能按标簽篩選 pods。
原理說明
其實目前新版的 K8S 在監控這塊的架構已經非常明确了,隻不過國内很少有文章解釋這一塊,其官方架構說明見:
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/monitoring_architecture.md。其架構分位 2 個部分:
内置于 K8S 的核心名額采集,安裝 K8S 就自帶了(下圖黑色部分)。
第三方的監控采集方案,需要大家自己選型,比如 prometheus 等(下圖藍色部分)。
像 kubectl top 擷取的 cpu/mem 使用情況,就屬于 K8S 内置的核心名額采集而來,完全不需要第三方的支援。
大家可能聽過下面幾個監控相關的東西:
cadvisor
kube-state-metrics
metrics-server
heapster
prometheus
不知道到底它們是什麼關系,到底要用哪個。我簡單給它們分分類,幫助大家清楚它們的定位:
非 K8S 内置(第三方):
cadvisor:每個 node 部署 1 個,獨立程式,用于采集 dockerd 容器資訊,暴露 prometheus 格式接口供采集。
kube-state-metrics:叢集級,監聽 K8S 資源變化,暴露 prometheus 格式接口供采集。
prometheus:采集器,其資料源可以是 cadvisor/kube-state-metrics 或者 k8s 内置的資料源。
K8S 内置(官方方案):
kubelet:每個 node 部署 1 個,其内置了部分 cadvisor 功能,是以它也知道所有容器監控資訊,也對外提供 prometheus 格式的采集接口。
metrics-server:叢集級,它采集 kubelet 最新資源使用率資料到記憶體,不儲存曆史,并且通過 API Server 開放對外 Restful 資源接口擷取 JSON 格式的實時 metrics 資料,可以按 node/pod/container 級别檢視資源使用率資訊。
heapster:其實就是 metrics-server 的前身,作用基本類似,不過已經被 metrics-server 徹底取代,其官網有如下說明,也就是說基本的 CPU/memory 監控已經被 metrics-server 取代:
RETIRED: Heapster is now retired. See the deprecation timeline for more information on support. We will not be making changes to Heapster.
The following are potential migration paths for Heapster functionality:
For basic CPU/memory HPA metrics: Use metrics-server.
如果大家了解上述内容的話就會知道,我們隻需要確定 metrics-server 運作在 K8S 叢集中,那麼就可以通過 API server 得到所有資源使用率情況,這也是 kubectl top 的工作原理。
與 metrics-server 通訊
那麼目前,metrics-server 在 API SERVER 注冊的 GROUP 叫做 metrics.k8s.io,VERSION 是 v1beta1,是以其對應的 Restful 資源 URI 就是以:/apis/metrics.k8s.io/v1beta1/為字首的。
metrics-server 官方已經說明了其資源 URI 的幾種形式:
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/resource-metrics-api.md。
The list of supported endpoints:
/nodes – all node metrics; type []NodeMetrics
/nodes/{node} – metrics for a specified node; type NodeMetrics
/namespaces/{namespace}/pods – all pod metrics within namespace with support for all-namespaces; type []PodMetrics
/namespaces/{namespace}/pods/{pod} – metrics for a specified pod; type PodMetrics
The following query parameters are supported:
labelSelector – restrict the list of returned objects by labels (list endpoints only)
是以,為了擷取某個 namespace 下面的 pods 的資源使用率,我們可以有 2 種方式:
按标簽篩選:/apis/metrics.k8s.io/v1beta1/namespaces/{namespace}/pods?labelSelector=xxxx
按 pod 名字擷取:/apis/metrics.k8s.io/v1beta1/namespaces/{namespace}/pods/{pod}
下面先教大家如何在指令行下按 HTTP 調用 URL 的擷取資源資訊。首先啟動一個 proxy,它會幫我們解決和 API SERVER 之間的認證問題,我們隻需要關注于接口參數即可:然後我們請求 localhost:8888 就可以免認證的調用到 API SERVER 了:會得到 nodes 級的資源使用率:
{
"kind": "NodeMetricsList",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes"
},
"items": [
{
"metadata": {
"name": "10.42.8.102",
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/10.42.8.102",
"creationTimestamp": "2019-11-26T06:23:17Z"
},
"timestamp": "2019-11-26T06:22:21Z",
"window": "30s",
"usage": {
"cpu": "250264852n",
"memory": "8318172Ki"
}
},
{
"metadata": {
"name": "10.42.37.63",
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/10.42.37.63",
"creationTimestamp": "2019-11-26T06:23:17Z"
},
"timestamp": "2019-11-26T06:22:27Z",
"window": "30s",
"usage": {
"cpu": "551516196n",
"memory": "13280692Ki"
}
},
{
"metadata": {
"name": "10.42.187.205",
"selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/10.42.187.205",
"creationTimestamp": "2019-11-26T06:23:17Z"
},
"timestamp": "2019-11-26T06:22:20Z",
"window": "30s",
"usage": {
"cpu": "1630534153n",
"memory": "15209140Ki"
}
}
]
}
[root@10-42-74-90 ~]# curl localhost:8888/apis/metrics.k8s.io/v1beta1/namespaces/default/pods/redis-master-fsx46
"kind": "PodMetrics",
"name": "redis-master-fsx46",
"namespace": "default",
"selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/default/pods/redis-master-fsx46",
"creationTimestamp": "2019-11-26T06:24:40Z"
"timestamp": "2019-11-26T06:24:16Z",
"window": "30s",
"containers": [
{
"name": "master",
"usage": {
"cpu": "969654n",
"memory": "15012Ki"
}
}
kubernetes 資源描述
是不是很簡單?但是 cpu 裡面為什麼有個字母 n?memory 裡面為什麼有一個 Ki?到底是啥意思?
cpu
請參考官方文檔:
https://kubernetes.io/zh/docs/concepts/configuration/manage-compute-resources-container/#cpu-的含義
kubernetes 中,CPU 資源的限制和請求以 cpu 為機關, metric 中也以此為機關。
Kubernetes 中的一個 cpu 等于:
1 AWS vCPU
1 GCP Core
1 Azure vCore
1 Hyperthread 在帶有超線程的裸機 Intel 處理器上
1 processor 在實體機上部署的 kubernetes,表示一個邏輯處理器
允許浮點數請求。具有 spec.containers[].resources.requests.cpu 為 0.5 的容器保證了一半 CPU 要求 1 CPU 的一半。表達式 0.1 等價于表達式 100m,可以看作 “100 millicpu”。同理,100m 等于100 * 1000n cpu, 即100000n cpu ,可讀作 100000 納 cpu,n是 cpu 的最小計量機關。
具有小數點(如 0.1)的請求由 API 轉換為100m,精度不超過 1m。是以,可能會優先選擇 100m 的形式。
記憶體
記憶體的限制和請求以位元組為機關。您可以使用以下字尾之一作為平均整數或定點整數表示記憶體:E,P,T,G,M,K(非精确值)。您還可以使用兩個字母的等效的幂數:Ei,Pi,Ti ,Gi,Mi,Ki(精确值)。例如,以下代表大緻相同的值:
128974848, 129e6, 129M, 123Mi(12310241024)
kubernetes client 擷取 metrics 資訊
kubernetes 得 部署了 metrics 元件, 這樣在使用kubectl get --raw "/openapi/v2" 可擷取到 metrics api 的 openapi 說明。
可以通過 openapi-generator-cli,重新生成 kubernetes client 。官方的 client 庫,也是這樣自動生成的,官方的 kubernetes client 生成工具位于:
https://github.com/kubernetes-client/gen該工具的核心邏輯:
配置一系列的參數,使用./java.sh out settings運作生成 java 用戶端, 其中out 是輸出目錄, settings 是腳本需要的環境變量配置檔案。;
運作preprocess_spec.py 從 kubernetes 官方倉庫擷取 swagger api 文檔,并将同目錄的custom_objects_spec.json 檔案合并到swagger.json檔案中。
運作 swagger generate 生成對應的程式語言 client 檔案。
生成後,隻需要使用 maven/gradle 編譯打包即可。
官方 kubenetes client 中缺少了 metrics 相關的 api, 我們可以在 custom_objects_spec.json 中添加上 metrics 相關的 api 描述資訊,加入即可(api + Model 定義)。然後執行編譯。
此過程中如果 Model 不全,可能會造成 編譯 kubernetes client 時,出現如下類似的錯誤:
[ERROR] /Users/guangfuhe/Projects/java/wde/k8sclient/src/test/java/io/kubernetes/client/model/IoK8sApiCoreV1NodeStatusTest.java:[25,34] 找不到符号
符号: 類 IoK8sApiCoreV1NodeConfigStatus
位置: 程式包 io.kubernetes.client.model
上面的缺失類是 Model 中缺少了定義造成的,可以在自己的叢集 openapi 文檔中查找 Model。
依據叢集的 openapi 文檔,自動生成 kubernetes client 的方法和詳細步驟如下:
擷取叢集的 openapi 文檔,kubectl get --raw "/openapi/v2"> k8s-client-swagger.json
在 kubernetes-client/gen 項目中openapi/custom_objects_spec.json , 插入自定義的 api 接口描述和 model 描述資訊,此時需要注意 custom_objects_spec.json 檔案的json 格式。
kubernetes-client/gen 項目,會在preprocess_spec.py 中将openapi/custom_objects_spec.json自動合并到 kubernetes 官方 openapi 文檔中。
運作 kubernetes-client/gen 需要setting檔案,詳情看“我的 settings 檔案配置“。
運作。在 kubernetes-client/gen 中的openapi/目錄下,運作sh java.sh out settings。自動生成的 kubernetes client 會放在out 目錄下。此過程會生成 docker 鏡像,并使用 docker 鏡像完成 api 生成。
編譯還需要 io.kubernetes.fluent和 io.kubernetes.custom 兩個 package, 可以通過原來的io.kubernetes:client-java:6.0.1中擷取源代碼, 然後從源代碼提取 custom 和 fluent package。
由于處理 Model 比較繁瑣,處理完上述問題後,還有 n 個 test Model, 由于不影響功能,我直接取消了 test 類的生成(生成後直接删除了,就不需要處理某些單元測試類找不到的問題)。
生成後,使用 maven 或者 gradle 編譯,我使用的是 maven。mvn clean compile package -DskipTests
最終的目錄結構如圖:
我的kubernetes gen配置
KUBERNETES_BRANCH=master
CLIENT_VERSION=6.1.0
PACKAGE_NAME=io.kubernetes.client
生成 k8s 本地叢集的 api 文檔描述檔案(openapi),kubectl get --raw "/openapi/v2"> k8s-client-swagger.json
我的 custom_object_spec.json 如下, github gist 位址:
https://gist.github.com/hgfkeep/9e65f5fb8f583f81fd4c8dc653793028:
[custom_objects_spec.json (114KB)]
可能出現的問題
編譯過程可能缺少依賴
<groupid>io.sundr</groupid>
<artifactid>builder-annotations</artifactid>
<version>0.2.1</version>
<exclusions>
<exclusion>
<groupid>com.sun</groupid>
<artifactid>tools</artifactid>
</exclusion>
</exclusions>
但是 builder-annotations 強依賴com.sun.tools.jar, 對于高版本的 jdk 來說,可以直接 exclude 掉。
java doc 問題
可能會出現類似如下的錯誤, 該錯誤是生成 java doc 時,提示的。可能是我用的 jdk 比較新:
[ERROR] /Users/guangfuhe/Projects/java/wde/k8sclient/src/main/java/io/kubernetes/client/apis/AdmissionregistrationV1beta1Api.java:1323: 錯誤: 屬性在 HTML5 中不受支援: summary
[ERROR]
單個 java 檔案過大導緻類存在卻飄紅
java 單個檔案過大,導緻 idea code insight 特效無法生效,可以直接清理自動生成的注釋資訊(使用 IDEA 替換功能,快速清理),清理的資訊如下:
</table><table summary="Response Details" border="1">
<tbody><tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr>
<tr><td> 200 </td><td> OK </td><td> - </td></tr>
<tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr>
将CoreV1Api.java中的上述資訊清理後即可。
使用 custom kubernetes java client
pom.xml 配置,需要引入之前生成的用戶端:
<groupid>cn.ac.ict.wde</groupid>
<artifactid>k8sclient</artifactid>
<version>0.0.1-SNAPSHOT</version>
擷取 node metrics 資訊
核心代碼如下:
// 初始化時,根據配置的kube config 檔案路徑, 擷取api client
public void init() {
try (InputStreamReader in = new InputStreamReader(new ClassPathResource(config.getKubeconfigFilePath()).getInputStream())) {
KubeConfig kubeconfig = KubeConfig.loadKubeConfig(in);
this.apiClient = ClientBuilder.kubeconfig(kubeconfig).build();
} catch (IOException e) {
log.error("create kubernetes api client error! {}", e);
}
// 列印node metrics資訊
public void printNodeMetrics() throws ApiException {
checkApiClient();
MetricsV1beta1Api metricsV1beta1Api = new MetricsV1beta1Api(this.apiClient);
V1beta1NodeMetricsList nodeMetricsList = metricsV1beta1Api.listNodeMetrics(null, null, null, 100, null, null, 3000, false);
if (nodeMetricsList != null) {
for (V1beta1NodeMetrics nodeMetrics : nodeMetricsList.getItems()) {
log.debug("node {}: {}", nodeMetrics.getMetadata().getName(), nodeMetrics.getUsage());
}
}
// 列印 namespce 中pod 裡面的 每個 container metrics 資訊
public void printNamespacePodMetrics(String namespace) throws ApiException {
checkApiClient();
MetricsV1beta1Api metricsV1beta1Api = new MetricsV1beta1Api(this.apiClient);
V1beta1PodMetricsList nodeMetricsList = metricsV1beta1Api.listNamespacedPodMetrics(namespace, null, null, null, 100, null, null, 3000, false);
if (nodeMetricsList != null) {
for (V1beta1PodMetrics podMetrics : nodeMetricsList.getItems()) {
for (V1beta1ContainerMetrics containerMetrics : podMetrics.getContainers()) {
log.debug("container {}: {}", containerMetrics.getName(), containerMetrics.getUsage());
}
}
}
NodeMetrics 輸出資訊如下:
16:35:44.911 [main] DEBUG cn.ac.ict.wde.service.KubernetesService - node *15: {cpu=Quantity{number=0.828512394, format=DECIMAL_SI}, memory=Quantity{number=108825636864, format=BINARY_SI}}
16:35:44.915 [main] DEBUG cn.ac.ict.wde.service.KubernetesService - node *17: {cpu=Quantity{number=0.328837919, format=DECIMAL_SI}, memory=Quantity{number=25566048256, format=BINARY_SI}}
ContainerMetrics 資訊如下:
16:44:28.536 [main] DEBUG cn.ac.ict.wde.service.KubernetesService - container prometheus-demo-app: {cpu=Quantity{number=0.000912205, format=DECIMAL_SI}, memory=Quantity{number=42446848, format=BINARY_SI}}
16:44:28.543 [main] DEBUG cn.ac.ict.wde.service.KubernetesService - container hello: {cpu=Quantity{number=0.000107881, format=DECIMAL_SI}, memory=Quantity{number=21565440, format=BINARY_SI}}
後續
如果您不想這麼麻煩,可以直接使用我編譯好的 k8s 庫,github 位址:
https://github.com/hgfkeep/k8s-java-clientwin.hgfdodo
k8sclient
0.1.0
其中:
tested kubernetes version:
Server Version: v1.13.2
Client Version: v1.15.5
jdk: 1.8 及以上版本
原文位址
https://my.oschina.net/hgfdoing/blog/3209372