java kubernetes client 獲取 集羣 metrics信息

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
NAME CPU(cores) MEMORY(bytes)
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",
"apiVersion": "metrics.k8s.io/v1beta1",
"metadata": {

"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-client


win.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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章