天天看點

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

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