1. 程式人生 > 實用技巧 >使用Operator部署Prometheus監控k8s叢集

使用Operator部署Prometheus監控k8s叢集

一、什麼是Operator

Operator是由CoreOS開發的,用來擴充套件Kubernetes API,特定的應用程式控制器,它用來建立、配置和管理複雜的有狀態應用,如資料庫、快取和監控系統。Operator基於Kubernetes的資源和控制器概念之上構建,但同時又包含了應用程式特定的領域知識。建立Operator的關鍵是CRD(自定義資源)的設計。

Operator是將運維人員對軟體操作的知識給程式碼化,同時利用 Kubernetes 強大的抽象來管理大規模的軟體應用。目前CoreOS官方提供了幾種Operator的實現,其中就包括我們今天的主角:Prometheus Operator

Operator的核心實現就是基於 Kubernetes 的以下兩個概念:

  • 資源:物件的狀態定義
  • 控制器:觀測、分析和行動,以調節資源的分佈

當前CoreOS提供的以下四種Operator:

  • etcd:建立etcd叢集
  • Rook:雲原生環境下的檔案、塊、物件儲存服務
  • Prometheus:建立Prometheus監控例項
  • Tectonic:部署Kubernetes叢集

接下來我們將使用Operator建立Prometheus。

二、安裝

我們這裡直接通過 Prometheus-Operator 的原始碼來進行安裝,當然也可以用 Helm 來進行一鍵安裝,我們採用原始碼安裝可以去了解更多的實現細節。首頁將原始碼 Clone 下來:

git clone https://github.com/coreos/prometheus-operator     ### 0.30.0版本之前
git clone https://github.com/coreos/kube-prometheus         ### 0.30.0版本之後
cd prometheus-operator/contrib/kube-prometheus/manifests

進入到 manifests 目錄下面,這個目錄下面包含我們所有的資源清單檔案,直接在該資料夾下面執行建立資源命令即可:

kubectl apply -f .

部署完成後,會建立一個名為monitoring的 namespace,所以資源物件對將部署在改名稱空間下面,此外 Operator 會自動建立4個 CRD 資源物件:

 kubectl get crd |grep coreos
alertmanagers.monitoring.coreos.com           2019-03-18T02:43:57Z
prometheuses.monitoring.coreos.com            2019-03-18T02:43:58Z
prometheusrules.monitoring.coreos.com         2019-03-18T02:43:58Z
servicemonitors.monitoring.coreos.com         2019-03-18T02:43:58Z

可以在 monitoring 名稱空間下面檢視所有的 Pod,其中 alertmanager 和 prometheus 是用 StatefulSet 控制器管理的,其中還有一個比較核心的 prometheus-operator 的 Pod,用來控制其他資源物件和監聽物件變化的:

kubectl get pods -n monitoring
NAME                                   READY     STATUS    RESTARTS   AGE
alertmanager-main-0                    2/2       Running   0          37m
alertmanager-main-1                    2/2       Running   0          34m
alertmanager-main-2                    2/2       Running   0          33m
grafana-7489c49998-pkl8w               1/1       Running   0          40m
kube-state-metrics-d6cf6c7b5-7dwpg     4/4       Running   0          27m
node-exporter-dlp25                    2/2       Running   0          40m
node-exporter-fghlp                    2/2       Running   0          40m
node-exporter-mxwdm                    2/2       Running   0          40m
node-exporter-r9v92                    2/2       Running   0          40m
prometheus-adapter-84cd9c96c9-n92n4    1/1       Running   0          40m
prometheus-k8s-0                       3/3       Running   1          37m
prometheus-k8s-1                       3/3       Running   1          37m
prometheus-operator-7b74946bd6-vmbcj   1/1       Running   0          40m

檢視建立的 Service:

kubectl get svc -n monitoring
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
alertmanager-main       ClusterIP   10.110.43.207   <none>        9093/TCP            40m
alertmanager-operated   ClusterIP   None            <none>        9093/TCP,6783/TCP   38m
grafana                 ClusterIP   10.109.160.0    <none>        3000/TCP            40m
kube-state-metrics      ClusterIP   None            <none>        8443/TCP,9443/TCP   40m
node-exporter           ClusterIP   None            <none>        9100/TCP            40m
prometheus-adapter      ClusterIP   10.105.174.21   <none>        443/TCP             40m
prometheus-k8s          ClusterIP   10.97.195.143   <none>        9090/TCP            40m
prometheus-operated     ClusterIP   None            <none>        9090/TCP            38m
prometheus-operator     ClusterIP   None            <none>        8080/TCP            40m

可以看到上面針對 grafana 和 prometheus 都建立了一個型別為 ClusterIP 的 Service,當然如果我們想要在外網訪問這兩個服務的話可以通過建立對應的 Ingress 物件或者使用 NodePort 型別的 Service,我們這裡為了簡單,直接使用 NodePort 型別的服務即可,編輯 grafana 和 prometheus-k8s 這兩個 Service,將服務型別更改為 NodePort:

kubectl edit svc grafana -n monitoring
kubectl edit svc prometheus-k8s -n monitoring
kubectl get svc -n monitoring
NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
.....
grafana                 NodePort    10.109.160.0    <none>        3000:31740/TCP      42m
prometheus-k8s          NodePort    10.97.195.143   <none>        9090:31310/TCP      42m

更改完成後,我們就可以通過去訪問上面的兩個服務了,比如檢視 prometheus 的 targets 頁面:

我們可以看到大部分的配置都是正常的,只有兩三個沒有管理到對應的監控目標,比如 kube-controller-manager 和 kube-scheduler 這兩個系統元件,這就和 ServiceMonitor 的定義有關係了,我們先來檢視下 kube-scheduler 元件對應的 ServiceMonitor 資源的定義:(prometheus-serviceMonitorKubeScheduler.yaml)

配置kube-scheduler

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    k8s-app: kube-scheduler
  name: kube-scheduler
  namespace: monitoring
spec:
  endpoints:
  - interval: 30s  #30s獲取一次資訊
    port: http-metrics  # 對應service的埠名
  jobLabel: k8s-app  
  namespaceSelector:  # 表示去匹配某一名稱空間中的service,如果想從所有的namespace中匹配用any: true
    matchNames:
    - kube-system
  selector:    # 匹配的 Service 的labels,如果使用mathLabels,則下面的所有標籤都匹配時才會匹配該service,如果使用matchExpressions,則至少匹配一個標籤的service都會被選擇
    matchLabels:
      k8s-app: kube-scheduler

上面是一個典型的 ServiceMonitor 資原始檔的宣告方式,上面我們通過selector.matchLabels在 kube-system 這個名稱空間下面匹配具有k8s-app=kube-scheduler這樣的 Service,但是我們系統中根本就沒有對應的 Service,所以我們需要手動建立一個 Service:(prometheus-kubeSchedulerService.yaml)

apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-scheduler
  labels:
    k8s-app: kube-scheduler
spec:
  selector:
    component: kube-scheduler
  ports:
  - name: http-metrics
    port: 10251
    targetPort: 10251
    protocol: TCP

其中最重要的是上面 labels 和 selector 部分,labels 區域的配置必須和我們上面的 ServiceMonitor 物件中的 selector 保持一致,selector下面配置的是component=kube-scheduler,為什麼會是這個 label 標籤呢,我們可以去 describe 下 kube-scheduelr 這個 Pod:

$ kubectl describe pod kube-scheduler-k8s-master -n kube-system
Name:               kube-scheduler-k8s-master
Namespace:          kube-system
Priority:           2000000000
PriorityClassName:  system-cluster-critical
Node:               k8s-master/172.16.138.40
Start Time:         Tue, 19 Feb 2019 21:15:05 -0500
Labels:             component=kube-scheduler
                    tier=control-plane
......

我們可以看到這個 Pod 具有component=kube-schedulertier=control-plane這兩個標籤,而前面這個標籤具有更唯一的特性,所以使用前面這個標籤較好,這樣上面建立的 Service 就可以和我們的 Pod 進行關聯了,直接建立即可:

$ kubectl create -f prometheus-kubeSchedulerService.yaml
$ kubectl get svc -n kube-system -l k8s-app=kube-scheduler
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
kube-scheduler   ClusterIP   10.103.165.58   <none>        10251/TCP   4m

建立完成後,隔一小會兒後去 prometheus 檢視 targets 下面 kube-scheduler 的狀態:

我們可以看到現在已經發現了 target,但是抓取資料結果出錯了,這個錯誤是因為我們叢集是使用 kubeadm 搭建的,其中 kube-scheduler 預設是繫結在127.0.0.1上面的,而上面我們這個地方是想通過節點的 IP 去訪問,所以訪問被拒絕了,我們只要把 kube-scheduler 繫結的地址更改成0.0.0.0即可滿足要求,由於 kube-scheduler 是以靜態 Pod 的形式執行在叢集中的,所以我們只需要更改靜態 Pod 目錄下面對應的 YAML (kube-scheduler.yaml)檔案即可:

$ cd /etc/kubernetes/manifests
將 kube-scheduler.yaml 檔案中-command的--address地址更改成0.0.0.0
$ vim kube-scheduler.yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    scheduler.alpha.kubernetes.io/critical-pod: ""
  creationTimestamp: null
  labels:
    component: kube-scheduler
    tier: control-plane
  name: kube-scheduler
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-scheduler
    - --address=0.0.0.0
    - --kubeconfig=/etc/kubernetes/scheduler.conf
    - --leader-elect=true
....

修改完成後我們將該檔案從當前資料夾中移除,隔一會兒再移回該目錄,就可以自動更新了,然後再去看 prometheus 中 kube-scheduler 這個 target 是否已經正常了:

配置kube-controller-manager

我們來檢視一下kube-controller-manager的ServiceMonitor資源的定義:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    k8s-app: kube-controller-manager
  name: kube-controller-manager
  namespace: monitoring
spec:
  endpoints:
  - interval: 30s
    metricRelabelings:
    - action: drop
      regex: etcd_(debugging|disk|request|server).*
      sourceLabels:
      - __name__
    port: http-metrics
  jobLabel: k8s-app
  namespaceSelector:
    matchNames:
    - kube-system
  selector:
    matchLabels:
      k8s-app: kube-controller-manager

上面我們可以看到是通過k8s-app: kube-controller-manager這個標籤選擇的service,但系統中沒有這個service。這裡我們手動建立一個:

建立前我們需要看確定pod的標籤:

$ kubectl describe pod kube-controller-manager-k8s-master -n kube-system
Name:               kube-controller-manager-k8s-master
Namespace:          kube-system
Priority:           2000000000
PriorityClassName:  system-cluster-critical
Node:               k8s-master/172.16.138.40
Start Time:         Tue, 19 Feb 2019 21:15:16 -0500
Labels:             component=kube-controller-manager
                    tier=control-plane
....

建立svc

apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-controller-manager
  labels:
    k8s-app: kube-controller-manager
spec:
  selector:
    component: kube-controller-manager
  ports:
  - name: http-metrics
    port: 10252
    targetPort: 10252
    protocol: TCP

建立完後,我們檢視targer

這裡和上面是同一個問題。讓我們使用上面的方法修改。讓我們修改kube-controller-manager.yaml:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    scheduler.alpha.kubernetes.io/critical-pod: ""
  creationTimestamp: null
  labels:
    component: kube-controller-manager
    tier: control-plane
  name: kube-controller-manager
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-controller-manager
    - --node-monitor-grace-period=10s
    - --pod-eviction-timeout=10s
    - --address=0.0.0.0   #修改
......

修改完成後我們將該檔案從當前資料夾中移除,隔一會兒再移回該目錄,就可以自動更新了,然後再去看 prometheus 中 kube-controller-manager 這個 target 是否已經正常了:

配置coredns

coredns啟動的metrics埠是9153,我們檢視kube-system下的svc是否有這個埠:

kubectl get svc -n kube-system
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
heapster                             ClusterIP   10.96.28.220     <none>        80/TCP          19d
kube-controller-manager              ClusterIP   10.99.208.51     <none>        10252/TCP       1h
kube-dns                             ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP   188d
kube-scheduler                       ClusterIP   10.103.165.58    <none>        10251/TCP       2h
kubelet                              ClusterIP   None             <none>        10250/TCP       5h
kubernetes-dashboard                 NodePort    10.103.15.27     <none>        443:30589/TCP   131d
monitoring-influxdb                  ClusterIP   10.103.155.57    <none>        8086/TCP        19d
tiller-deploy                        ClusterIP   10.104.114.83    <none>        44134/TCP       18d

這裡我們看到kube-dns沒有metrics的埠,但是metrics後端是啟動,所以我們需要把這個埠通過svc暴露出來。建立svc:

apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: kube-prometheus-prometheus-coredns
  labels:
    k8s-app: prometheus-operator-coredns
spec:
  selector:
    k8s-app: kube-dns
  ports:
  - name: metrics
    port: 9153
    targetPort: 9153
    protocol: TCP

這裡我們啟動一個svc,labels是k8s-app: prometheus-operator-coredns,所有我們需要修改DNS的serviceMonitor下的labels值。

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    k8s-app: coredns
  name: coredns
  namespace: monitoring
spec:
  endpoints:
  - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
    interval: 15s
    port: metrics
  jobLabel: k8s-app
  namespaceSelector:
    matchNames:
    - kube-system
  selector:
    matchLabels:
      k8s-app: prometheus-operator-coredns

建立檢視這兩個資源:

$ kubectl apply  -f prometheus-serviceMonitorCoreDNS.yaml
$ kubectl create -f prometheus-KubeDnsSvc.yaml
$ kubectl get svc -n kube-system
NAME                                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
kube-prometheus-prometheus-coredns   ClusterIP   10.100.205.135   <none>        9153/TCP        1h

讓我們再去看 prometheus 中 coredns 這個 target 是否已經正常了:

上面的監控資料配置完成後,現在我們可以去檢視下 grafana 下面的 dashboard,同樣使用上面的 NodePort 訪問即可,第一次登入使用 admin:admin 登入即可,進入首頁後,可以發現已經和我們的 Prometheus 資料來源關聯上了,正常來說可以看到一些監控圖表了:

自定義監控項

除了 Kubernetes 叢集中的一些資源物件、節點以及元件需要監控,有的時候我們可能還需要根據實際的業務需求去新增自定義的監控項,新增一個自定義監控的步驟也是非常簡單的。

  • 第一步建立一個 ServiceMonitor 物件,用於 Prometheus 新增監控項
  • 第二步為 ServiceMonitor 物件關聯 metrics 資料介面的一個 Service 物件
  • 第三步確保 Service 物件可以正確獲取到 metrics 資料

接下來演示如何新增 etcd 叢集的監控。

無論是 Kubernetes 叢集外的還是使用 Kubeadm 安裝在叢集內部的 etcd 叢集,我們這裡都將其視作叢集外的獨立叢集,因為對於二者的使用方法沒什麼特殊之處。

etcd 證書

對於 etcd 叢集一般情況下,為了安全都會開啟 https 證書認證的方式,所以要想讓 Prometheus 訪問到 etcd 叢集的監控資料,就需要提供相應的證書校驗。

由於我們這裡演示環境使用的是 Kubeadm 搭建的叢集,我們可以使用 kubectl 工具去獲取 etcd 啟動的時候使用的證書路徑:

$ kubectl get pods -n kube-system | grep etcd
etcd-k8s-master                         1/1       Running   2773       188d
etcd-k8s-node01                         1/1       Running   2          104d
$ kubectl get pod etcd-k8s-master -n kube-system -o yaml
.....
spec:
  containers:
  - command:
    - etcd
    - --advertise-client-urls=https://172.16.138.40:2379
    - --initial-advertise-peer-urls=https://172.16.138.40:2380
    - --initial-cluster=k8s-master=https://172.16.138.40:2380
    - --listen-client-urls=https://127.0.0.1:2379,https://172.16.138.40:2379
    - --listen-peer-urls=https://172.16.138.40:2380
    - --cert-file=/etc/kubernetes/pki/etcd/server.crt
    - --client-cert-auth=true
    - --data-dir=/var/lib/etcd
    - --key-file=/etc/kubernetes/pki/etcd/server.key
    - --name=k8s-master
    - --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
    - --peer-client-cert-auth=true
    - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
    - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    - --snapshot-count=10000
    - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    image: registry.cn-hangzhou.aliyuncs.com/google_containers/etcd-amd64:3.2.18
    imagePullPolicy: IfNotPresent
    livenessProbe:
      exec:
        command:
        - /bin/sh
        - -ec
        - ETCDCTL_API=3 etcdctl --endpoints=https://[127.0.0.1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt
          --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key
          get foo
      failureThreshold: 8
      initialDelaySeconds: 15
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 15
    name: etcd
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/lib/etcd
      name: etcd-data
    - mountPath: /etc/kubernetes/pki/etcd
      name: etcd-certs
......
  tolerations:
  - effect: NoExecute
    operator: Exists
  volumes:
  - hostPath:
      path: /var/lib/etcd
      type: DirectoryOrCreate
    name: etcd-data
  - hostPath:
      path: /etc/kubernetes/pki/etcd
      type: DirectoryOrCreate
    name: etcd-certs
.....

我們可以看到 etcd 使用的證書都對應在節點的 /etc/kubernetes/pki/etcd 這個路徑下面,所以首先我們將需要使用到的證書通過 secret 物件儲存到叢集中去:(在 etcd 執行的節點)

$ kubectl -n monitoring create secret generic etcd-certs --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.crt --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.key --from-file=/etc/kubernetes/pki/etcd/ca.crt
secret/etcd-certs created

然後將上面建立的 etcd-certs 物件配置到 prometheus 資源物件中,直接更新 prometheus 資源物件即可:

nodeSelector:
  beta.kubernetes.io/os: linux
replicas: 2
secrets:
- etcd-certs

更新完成後,我們就可以在 Prometheus 的 Pod 中獲取到上面建立的 etcd 證書檔案了,具體的路徑我們可以進入 Pod 中檢視:

$ kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring
Defaulting container name to prometheus.
Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
/prometheus $ ls /etc/prometheus/
config_out/         console_libraries/  consoles/           prometheus.yml      rules/              secrets/
/prometheus $ ls /etc/prometheus/secrets/etcd-certs/
ca.crt                  healthcheck-client.crt  healthcheck-client.key
/prometheus $

建立 ServiceMonitor

現在 Prometheus 訪問 etcd 叢集的證書已經準備好了,接下來建立 ServiceMonitor 物件即可(prometheus-serviceMonitorEtcd.yaml)

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: etcd-k8s
  namespace: monitoring
  labels:
    k8s-app: etcd-k8s
spec:
  jobLabel: k8s-app
  endpoints:
  - port: port
    interval: 30s
    scheme: https
    tlsConfig:
      caFile: /etc/prometheus/secrets/etcd-certs/ca.crt
      certFile: /etc/prometheus/secrets/etcd-certs/healthcheck-client.crt
      keyFile: /etc/prometheus/secrets/etcd-certs/healthcheck-client.key
      insecureSkipVerify: true
  selector:
    matchLabels:
      k8s-app: etcd
  namespaceSelector:
    matchNames:
    - kube-system

上面我們在 monitoring 名稱空間下面建立了名為 etcd-k8s 的 ServiceMonitor 物件,基本屬性和前面章節中的一致,匹配 kube-system 這個名稱空間下面的具有 k8s-app=etcd 這個 label 標籤的 Service,jobLabel 表示用於檢索 job 任務名稱的標籤,和前面不太一樣的地方是 endpoints 屬性的寫法,配置上訪問 etcd 的相關證書,endpoints 屬性下面可以配置很多抓取的引數,比如 relabel、proxyUrl,tlsConfig 表示用於配置抓取監控資料端點的 tls 認證,由於證書 serverName 和 etcd 中籤發的可能不匹配,所以加上了 insecureSkipVerify=true

直接建立這個 ServiceMonitor 物件:

$ kubectl create -f prometheus-serviceMonitorEtcd.yaml
servicemonitor.monitoring.coreos.com/etcd-k8s created

建立 Service

ServiceMonitor 建立完成了,但是現在還沒有關聯的對應的 Service 物件,所以需要我們去手動建立一個 Service 物件(prometheus-etcdService.yaml):

apiVersion: v1
kind: Service
metadata:
  name: etcd-k8s
  namespace: kube-system
  labels:
    k8s-app: etcd
spec:
  type: ClusterIP
  clusterIP: None
  ports:
  - name: port
    port: 2379
    protocol: TCP

---
apiVersion: v1
kind: Endpoints
metadata:
  name: etcd-k8s
  namespace: kube-system
  labels:
    k8s-app: etcd
subsets:
- addresses:
  - ip: 172.16.138.40
    nodeName: etcd-k8s-master
  - ip: 172.16.138.41
    nodeName: etcd-k8s-node01
  ports:
  - name: port
    port: 2379
    protocol: TCP

我們這裡建立的 Service 沒有采用前面通過 label 標籤的形式去匹配 Pod 的做法,因為前面我們說過很多時候我們建立的 etcd 叢集是獨立於叢集之外的,這種情況下面我們就需要自定義一個 Endpoints,要注意 metadata 區域的內容要和 Service 保持一致,Service 的 clusterIP 設定為 None,對改知識點不太熟悉的,可以去檢視我們前面關於 Service 部分的講解。

Endpoints 的 subsets 中填寫 etcd 叢集的地址即可,我們這裡是建立的是高可用測試叢集,我們建立的時候指定了node的主機IP地址(2個etcd也是不符合規範的。因為etcd是選舉制,2個就等於一個是一樣的。),直接建立該 Service 資源:

$ kubectl create -f prometheus-etcdService.yaml
service/etcd-k8s created
endpoints/etcd-k8s created

建立完成後,隔一會兒去 Prometheus 的 Dashboard 中檢視 targets,便會有 etcd 的監控項了:

資料採集到後,可以在 grafana 中匯入編號為3070的 dashboard,獲取到 etcd 的監控圖表。

配置 PrometheusRule

現在我們知道怎麼自定義一個 ServiceMonitor 物件了,但是如果需要自定義一個報警規則的話呢?比如現在我們去檢視 Prometheus Dashboard 的 Alert 頁面下面就已經有一些報警規則了,還有一些是已經觸發規則的了:

但是這些報警資訊是哪裡來的呢?他們應該用怎樣的方式通知我們呢?我們知道之前我們使用自定義的方式可以在 Prometheus 的配置檔案之中指定 AlertManager 例項和 報警的 rules 檔案,現在我們通過 Operator 部署的呢?我們可以在 Prometheus Dashboard 的 Config 頁面下面檢視關於 AlertManager 的配置:

alerting:
  alert_relabel_configs:
  - separator: ;
    regex: prometheus_replica
    replacement: $1
    action: labeldrop
  alertmanagers:
  - kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
        - monitoring
    scheme: http
    path_prefix: /
    timeout: 10s
    relabel_configs:
    - source_labels: [__meta_kubernetes_service_name]
      separator: ;
      regex: alertmanager-main
      replacement: $1
      action: keep
    - source_labels: [__meta_kubernetes_endpoint_port_name]
      separator: ;
      regex: web
      replacement: $1
      action: keep
rule_files:
- /etc/prometheus/rules/prometheus-k8s-rulefiles-0/*.yaml

上面 alertmanagers 例項的配置我們可以看到是通過角色為 endpoints 的 kubernetes 的服務發現機制獲取的,匹配的是服務名為 alertmanager-main,埠名未 web 的 Service 服務,我們檢視下 alertmanager-main 這個 Service:

kubectl describe svc alertmanager-main -n monitoring
Name:              alertmanager-main
Namespace:         monitoring
Labels:            alertmanager=main
Annotations:       kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"alertmanager":"main"},"name":"alertmanager-main","namespace":"monitoring"},...
Selector:          alertmanager=main,app=alertmanager
Type:              ClusterIP
IP:                10.110.43.207
Port:              web  9093/TCP
TargetPort:        web/TCP
Endpoints:         10.244.0.31:9093,10.244.2.42:9093,10.244.3.40:9093
Session Affinity:  None
Events:            <none>

可以看到服務名正是 alertmanager-main,Port 定義的名稱也是 web,符合上面的規則,所以 Prometheus 和 AlertManager 元件就正確關聯上了。而對應的報警規則檔案位於:/etc/prometheus/rules/prometheus-k8s-rulefiles-0/目錄下面所有的 YAML 檔案。我們可以進入 Prometheus 的 Pod 中驗證下該目錄下面是否有 YAML 檔案:

$ kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring
Defaulting container name to prometheus.
Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
/prometheus $ ls /etc/prometheus/rules/prometheus-k8s-rulefiles-0/
monitoring-prometheus-k8s-rules.yaml
/prometheus $ cat /etc/prometheus/rules/prometheus-k8s-rulefiles-0/monitoring-prometheus-k8s-rules.yaml
groups:
- name: k8s.rules
  rules:
  - expr: |
      sum(rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])) by (namespace)
    record: namespace:container_cpu_usage_seconds_total:sum_rate
  - expr: |
      sum by (namespace, pod_name, container_name) (
        rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])
      )
    record: namespace_pod_name_container_name:container_cpu_usage_seconds_total:sum_rate
...........

這個 YAML 檔案實際上就是我們之前建立的一個 PrometheusRule 檔案包含的:

$ cat prometheus-rules.yaml 
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  labels:
    prometheus: k8s
    role: alert-rules
  name: prometheus-k8s-rules
  namespace: monitoring
spec:
  groups:
  - name: k8s.rules
    rules:
    - expr: |
        sum(rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])) by (namespace)
      record: namespace:container_cpu_usage_seconds_total:sum_rate
    - expr: |
        sum by (namespace, pod_name, container_name) (
          rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])
        )
      record: namespace_pod_name_container_name:container_cpu_usage_seconds_total:sum_rate
.....

我們這裡的 PrometheusRule 的 name 為 prometheus-k8s-rules,namespace 為 monitoring,我們可以猜想到我們建立一個 PrometheusRule 資源物件後,會自動在上面的 prometheus-k8s-rulefiles-0 目錄下面生成一個對應的<namespace>-<name>.yaml檔案,所以如果以後我們需要自定義一個報警選項的話,只需要定義一個 PrometheusRule 資源物件即可。至於為什麼 Prometheus 能夠識別這個 PrometheusRule 資源物件呢?這就需要檢視我們建立的 prometheus 這個資源物件了,裡面有非常重要的一個屬性 ruleSelector,用來匹配 rule 規則的過濾器,要求匹配具有 prometheus=k8s 和 role=alert-rules 標籤的 PrometheusRule 資源物件,現在明白了吧?

ruleSelector:
  matchLabels:
    prometheus: k8s
    role: alert-rules

所以我們要想自定義一個報警規則,只需要建立一個具有 prometheus=k8s 和 role=alert-rules 標籤的 PrometheusRule 物件就行了,比如現在我們新增一個 etcd 是否可用的報警,我們知道 etcd 整個叢集有一半以上的節點可用的話叢集就是可用的,所以我們判斷如果不可用的 etcd 數量超過了一半那麼就觸發報警,建立檔案 prometheus-etcdRules.yaml:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  labels:
    prometheus: k8s
    role: alert-rules
  name: etcd-rules
  namespace: monitoring
spec:
  groups:
  - name: etcd
    rules:
    - alert: EtcdClusterUnavailable
      annotations:
        summary: etcd cluster small
        description: If one more etcd peer goes down the cluster will be unavailable
      expr: |
        count(up{job="etcd"} == 0) > (count(up{job="etcd"}) / 2 - 1)
      for: 3m
      labels:
        severity: critical
.....
$ kubectl create -f prometheus-etcdRules.yam

注意 label 標籤一定至少要有 prometheus=k8s 和 role=alert-rules,建立完成後,隔一會兒再去容器中檢視下 rules 資料夾:

$ kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring
Defaulting container name to prometheus.
Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod.
/prometheus $ ls /etc/prometheus/rules/prometheus-k8s-rulefiles-0/
monitoring-etcd-rules.yaml            monitoring-prometheus-k8s-rules.yaml

可以看到我們建立的 rule 檔案已經被注入到了對應的 rulefiles 資料夾下面了,證明我們上面的設想是正確的。然後再去 Prometheus Dashboard 的 Alert 頁面下面就可以檢視到上面我們新建的報警規則了:

配置報警

我們知道了如何去新增一個報警規則配置項,但是這些報警資訊用怎樣的方式去傳送呢?前面的課程中我們知道我們可以通過 AlertManager 的配置檔案去配置各種報警接收器,現在我們是通過 Operator 提供的 alertmanager 資源物件建立的元件,應該怎樣去修改配置呢?

首先我們將 alertmanager-main 這個 Service 改為 NodePort 型別的 Service,修改完成後我們可以在頁面上的 status 路徑下面檢視 AlertManager 的配置資訊:

$ kubectl edit svc alertmanager-main -n monitoring
......
  selector:
    alertmanager: main
    app: alertmanager
  sessionAffinity: None
  type: NodePort
.....

這些配置資訊實際上是來自於我們之前在prometheus-operator/contrib/kube-prometheus/manifests目錄下面建立的 alertmanager-secret.yaml 檔案:

apiVersion: v1
data:
  alertmanager.yaml: Imdsb2JhbCI6IAogICJyZXNvbHZlX3RpbWVvdXQiOiAiNW0iCiJyZWNlaXZlcnMiOiAKLSAibmFtZSI6ICJudWxsIgoicm91dGUiOiAKICAiZ3JvdXBfYnkiOiAKICAtICJqb2IiCiAgImdyb3VwX2ludGVydmFsIjogIjVtIgogICJncm91cF93YWl0IjogIjMwcyIKICAicmVjZWl2ZXIiOiAibnVsbCIKICAicmVwZWF0X2ludGVydmFsIjogIjEyaCIKICAicm91dGVzIjogCiAgLSAibWF0Y2giOiAKICAgICAgImFsZXJ0bmFtZSI6ICJEZWFkTWFuc1N3aXRjaCIKICAgICJyZWNlaXZlciI6ICJudWxsIg==
kind: Secret
metadata:
  name: alertmanager-main
  namespace: monitoring
type: Opaque

可以將 alertmanager.yaml 對應的 value 值做一個 base64 解碼:

echo Imdsb2JhbCI6IAogICJyZXNvbHZlX3RpbWVvdXQiOiAiNW0iCiJyZWNlaXZlcnMiOiAKLSAibmFtZSI6ICJudWxsIgoicm91dGUiOiAKICAiZ3JvdXBfYnkiOiAKICAtICJqb2IiCiAgImdyb3VwX2ludGVydmFsIjogIjVtIgogICJncm91cF93YWl0IjogIjMwcyIKICAicmVjZWl2ZXIiOiAibnVsbCIKICAicmVwZWF0X2ludGVydmFsIjogIjEyaCIKICAicm91dGVzIjogCiAgLSAibWF0Y2giOiAKICAgICAgImFsZXJ0bmFtZSI6ICJEZWFkTWFuc1N3aXRjaCIKICAgICJyZWNlaXZlciI6ICJudWxsIg== | base64 -d
解碼出來的結果
"global":
  "resolve_timeout": "5m"
"receivers":
- "name": "null"
"route":
  "group_by":
  - "job"
  "group_interval": "5m"
  "group_wait": "30s"
  "receiver": "null"
  "repeat_interval": "12h"
  "routes":
  - "match":
      "alertname": "DeadMansSwitch"
    "receiver": "null"

我們可以看到內容和上面檢視的配置資訊是一致的,所以如果我們想要新增自己的接收器,或者模板訊息,我們就可以更改這個檔案:

global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.qq.com:587'
  smtp_from: '[email protected]'
  smtp_auth_username: '[email protected]'
  smtp_auth_password: '***'
  smtp_hello: 'qq.com'
  smtp_require_tls: true
templates:
  - "/etc/alertmanager-tmpl/wechat.tmpl"
route:
  group_by: ['job', 'severity']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 5m
  receiver: default
  routes:
  - receiver: 'wechat'
    group_wait: 10s
    match:
      alertname: CoreDNSDown
receivers:
- name: 'default'
  email_configs:
  - to: '[email protected]'
    send_resolved: true
- name: 'wechat'
  wechat_configs:
  - corp_id: '***'
    to_party: '*'
    to_user: "**"
    agent_id: '***'
    api_secret: '***'
    send_resolved: true

將上面檔案儲存為 alertmanager.yaml,然後使用這個檔案建立一個 Secret 物件:

#刪除原secret物件
kubectl delete secret alertmanager-main -n monitoring
secret "alertmanager-main" deleted
#將自己的配置檔案匯入到新的secret
kubectl create secret generic alertmanager-main --from-file=alertmanager.yaml -n monitoring

我們添加了兩個接收器,預設的通過郵箱進行傳送,對於 CoreDNSDown 這個報警我們通過 wechat 來進行傳送,上面的步驟建立完成後,很快我們就會收到一條釘釘訊息:

同樣郵箱中也會收到報警資訊:

我們再次檢視 AlertManager 頁面的 status 頁面的配置資訊可以看到已經變成上面我們的配置資訊了:

AlertManager 配置也可以使用模板(.tmpl檔案),這些模板可以與 alertmanager.yaml 配置檔案一起新增到 Secret 物件中,比如:

apiVersion:v1
kind:secret
metadata:
   name:alertmanager-example
data:
  alertmanager.yaml:{BASE64_CONFIG}
  template_1.tmpl:{BASE64_TEMPLATE_1}
  template_2.tmpl:{BASE64_TEMPLATE_2}
  ...

模板會被放置到與配置檔案相同的路徑,當然要使用這些模板檔案,還需要在 alertmanager.yaml 配置檔案中指定:

templates:
- '*.tmpl'

建立成功後,Secret 物件將會掛載到 AlertManager 物件建立的 AlertManager Pod 中去。

樣例:我們建立一個alertmanager-tmpl.yaml檔案,新增如下內容:

{{ define "wechat.default.message" }}
{{ range .Alerts }}
========start==========
告警程式: prometheus_alert
告警級別: {{ .Labels.severity }}
告警型別: {{ .Labels.alertname }}
故障主機: {{ .Labels.instance }}
告警主題: {{ .Annotations.summary }}
告警詳情: {{ .Annotations.description }}
觸發時間: {{ .StartsAt.Format "2013-12-02 15:04:05" }}
========end==========
{{ end }}
{{ end }}

刪除原secret物件

$ kubectl delete secret alertmanager-main -n monitoring
secret "alertmanager-main" deleted

建立新的secret物件

$ kubectl create secret generic alertmanager-main --from-file=alertmanager.yaml --from-file=alertmanager-tmpl.yaml -n monitoring
secret/alertmanager-main created

過一會我們的微信就會收到告警資訊。當然這裡標籤定義的問題,獲取的值不全,我們可以根據實際情況自定義。

自動發現配置

我們想一個問題,如果在我們的 Kubernetes 叢集中有了很多的 Service/Pod,那麼我們都需要一個一個的去建立一個對應的 ServiceMonitor 物件來進行監控嗎?這樣豈不是又變得麻煩起來了?

為解決這個問題,Prometheus Operator 為我們提供了一個額外的抓取配置的來解決這個問題,我們可以通過新增額外的配置來進行服務發現進行自動監控。和前面自定義的方式一樣,我們想要在 Prometheus Operator 當中去自動發現並監控具有prometheus.io/scrape=true這個 annotations 的 Service,之前我們定義的 Prometheus 的配置如下:

- job_name: 'kubernetes-service-endpoints'
  kubernetes_sd_configs:
  - role: endpoints
  relabel_configs:
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
    action: keep
    regex: true
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
    action: replace
    target_label: __scheme__
    regex: (https?)
  - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
    action: replace
    target_label: __metrics_path__
    regex: (.+)
  - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
    action: replace
    target_label: __address__
    regex: ([^:]+)(?::\d+)?;(\d+)
    replacement: $1:$2
  - action: labelmap
    regex: __meta_kubernetes_service_label_(.+)
  - source_labels: [__meta_kubernetes_namespace]
    action: replace
    target_label: kubernetes_namespace
  - source_labels: [__meta_kubernetes_service_name]
    action: replace
    target_label: kubernetes_name

要想自動發現叢集中的 Service,就需要我們在 Service 的annotation區域新增prometheus.io/scrape=true的宣告,將上面檔案直接儲存為 prometheus-additional.yaml,然後通過這個檔案建立一個對應的 Secret 物件:

$ kubectl create secret generic additional-configs --from-file=prometheus-additional.yaml -n monitoring
secret/additional-configs created

建立完成後,會將上面配置資訊進行 base64 編碼後作為 prometheus-additional.yaml 這個 key 對應的值存在:

$ kubectl get secret additional-configs -n monitoring -o yaml
apiVersion: v1
data:
  prometheus-additional.yaml: LSBqb2JfbmFtZTogJ2t1YmVybmV0ZXMtc2VydmljZS1lbmRwb2ludHMnCiAga3ViZXJuZXRlc19zZF9jb25maWdzOgogIC0gcm9sZTogZW5kcG9pbnRzCiAgcmVsYWJlbF9jb25maWdzOgogIC0gc291cmNlX2xhYmVsczogW19fbWV0YV9rdWJlcm5ldGVzX3NlcnZpY2VfYW5ub3RhdGlvbl9wcm9tZXRoZXVzX2lvX3NjcmFwZV0KICAgIGFjdGlvbjoga2VlcAogICAgcmVnZXg6IHRydWUKICAtIHNvdXJjZV9sYWJlbHM6IFtfX21ldGFfa3ViZXJuZXRlc19zZXJ2aWNlX2Fubm90YXRpb25fcHJvbWV0aGV1c19pb19zY2hlbWVdCiAgICBhY3Rpb246IHJlcGxhY2UKICAgIHRhcmdldF9sYWJlbDogX19zY2hlbWVfXwogICAgcmVnZXg6IChodHRwcz8pCiAgLSBzb3VyY2VfbGFiZWxzOiBbX19tZXRhX2t1YmVybmV0ZXNfc2VydmljZV9hbm5vdGF0aW9uX3Byb21ldGhldXNfaW9fcGF0aF0KICAgIGFjdGlvbjogcmVwbGFjZQogICAgdGFyZ2V0X2xhYmVsOiBfX21ldHJpY3NfcGF0aF9fCiAgICByZWdleDogKC4rKQogIC0gc291cmNlX2xhYmVsczogW19fYWRkcmVzc19fLCBfX21ldGFfa3ViZXJuZXRlc19zZXJ2aWNlX2Fubm90YXRpb25fcHJvbWV0aGV1c19pb19wb3J0XQogICAgYWN0aW9uOiByZXBsYWNlCiAgICB0YXJnZXRfbGFiZWw6IF9fYWRkcmVzc19fCiAgICByZWdleDogKFteOl0rKSg/OjpcZCspPzsoXGQrKQogICAgcmVwbGFjZW1lbnQ6ICQxOiQyCiAgLSBhY3Rpb246IGxhYmVsbWFwCiAgICByZWdleDogX19tZXRhX2t1YmVybmV0ZXNfc2VydmljZV9sYWJlbF8oLispCiAgLSBzb3VyY2VfbGFiZWxzOiBbX19tZXRhX2t1YmVybmV0ZXNfbmFtZXNwYWNlXQogICAgYWN0aW9uOiByZXBsYWNlCiAgICB0YXJnZXRfbGFiZWw6IGt1YmVybmV0ZXNfbmFtZXNwYWNlCiAgLSBzb3VyY2VfbGFiZWxzOiBbX19tZXRhX2t1YmVybmV0ZXNfc2VydmljZV9uYW1lXQogICAgYWN0aW9uOiByZXBsYWNlCiAgICB0YXJnZXRfbGFiZWw6IGt1YmVybmV0ZXNfbmFtZQo=
kind: Secret
metadata:
  creationTimestamp: 2019-03-20T03:38:37Z
  name: additional-configs
  namespace: monitoring
  resourceVersion: "29056864"
  selfLink: /api/v1/namespaces/monitoring/secrets/additional-configs
  uid: a579495b-4ac1-11e9-baf3-005056930126
type: Opaque

然後我們只需要在宣告 prometheus 的資源物件檔案中新增上這個額外的配置:(prometheus-prometheus.yaml)

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  labels:
    prometheus: k8s
  name: k8s
  namespace: monitoring
spec:
  alerting:
    alertmanagers:
    - name: alertmanager-main
      namespace: monitoring
      port: web
  baseImage: quay.io/prometheus/prometheus
  nodeSelector:
    beta.kubernetes.io/os: linux
  replicas: 2
  secrets:
  - etcd-certs
  resources:
    requests:
      memory: 400Mi
  ruleSelector:
    matchLabels:
      prometheus: k8s
      role: alert-rules
  securityContext:
    fsGroup: 2000
    runAsNonRoot: true
    runAsUser: 1000
  additionalScrapeConfigs:
    name: additional-configs
    key: prometheus-additional.yaml
  serviceAccountName: prometheus-k8s
  serviceMonitorNamespaceSelector: {}
  serviceMonitorSelector: {}
  version: v2.5.0

新增完成後,直接更新 prometheus 這個 CRD 資源物件:

$ kubectl apply -f prometheus-prometheus.yaml
prometheus.monitoring.coreos.com/k8s configured

隔一小會兒,可以前往 Prometheus 的 Dashboard 中檢視配置是否生效:

在 Prometheus Dashboard 的配置頁面下面我們可以看到已經有了對應的的配置資訊了,但是我們切換到 targets 頁面下面卻並沒有發現對應的監控任務,檢視 Prometheus 的 Pod 日誌:

$ kubectl logs -f prometheus-k8s-0 prometheus -n monitoring
evel=error ts=2019-03-20T03:55:01.298281581Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:302: Failed to list *v1.Pod: pods is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list pods at the cluster scope"
level=error ts=2019-03-20T03:55:02.29813427Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:301: Failed to list *v1.Service: services is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list services at the cluster scope"
level=error ts=2019-03-20T03:55:02.298431046Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:300: Failed to list *v1.Endpoints: endpoints is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list endpoints at the cluster scope"
level=error ts=2019-03-20T03:55:02.299312874Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:302: Failed to list *v1.Pod: pods is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list pods at the cluster scope"
level=error ts=2019-03-20T03:55:03.299674406Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:301: Failed to list *v1.Service: services is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list services at the cluster scope"
level=error ts=2019-03-20T03:55:03.299757543Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:300: Failed to list *v1.Endpoints: endpoints is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list endpoints at the cluster scope"
level=error ts=2019-03-20T03:55:03.299907982Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:302: Failed to list *v1.Pod: pods is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list pods at the cluster scope"

可以看到有很多錯誤日誌出現,都是xxx is forbidden,這說明是 RBAC 許可權的問題,通過 prometheus 資源物件的配置可以知道 Prometheus 綁定了一個名為 prometheus-k8s 的 ServiceAccount 物件,而這個物件繫結的是一個名為 prometheus-k8s 的 ClusterRole:(prometheus-clusterRole.yaml)

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus-k8s
rules:
- apiGroups:
  - ""
  resources:
  - nodes/metrics
  verbs:
  - get
- nonResourceURLs:
  - /metrics
  verbs:
  - get

上面的許可權規則中我們可以看到明顯沒有對 Service 或者 Pod 的 list 許可權,所以報錯了,要解決這個問題,我們只需要新增上需要的許可權即可:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus-k8s
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  - services
  - endpoints
  - pods
  - nodes/proxy
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - configmaps
  - nodes/metrics
  verbs:
  - get
- nonResourceURLs:
  - /metrics
  verbs:
  - get

更新上面的 ClusterRole 這個資源物件,然後重建下 Prometheus 的所有 Pod,正常就可以看到 targets 頁面下面有 kubernetes-service-endpoints 這個監控任務了:

$ kubectl apply -f prometheus-clusterRole.yaml
clusterrole.rbac.authorization.k8s.io/prometheus-k8s configured

我們這裡自動監控了兩個 Service,這兩個都是coredns的,我們在 Service 中有兩個特殊的 annotations:

$ kubectl describe svc kube-dns -n kube-system
Name:              kube-dns
Namespace:         kube-system
....
Annotations:       prometheus.io/port=9153
                   prometheus.io/scrape=true
...

所以被自動發現了,當然我們也可以用同樣的方式去配置 Pod、Ingress 這些資源物件的自動發現。

資料持久化

上面我們在修改完許可權的時候,重啟了 Prometheus 的 Pod,如果我們仔細觀察的話會發現我們之前採集的資料已經沒有了,這是因為我們通過 prometheus 這個 CRD 建立的 Prometheus 並沒有做資料的持久化,我們可以直接檢視生成的 Prometheus Pod 的掛載情況就清楚了:

............
    volumeMounts:
    - mountPath: /etc/prometheus/config_out
      name: config-out
      readOnly: true
    - mountPath: /prometheus
      name: prometheus-k8s-db
    - mountPath: /etc/prometheus/rules/prometheus-k8s-rulefiles-0 
.........
 volumes:
  - name: config
    secret:
      defaultMode: 420
      secretName: prometheus-k8s
  - emptyDir: {}

我們可以看到 Prometheus 的資料目錄 /prometheus 實際上是通過 emptyDir 進行掛載的,我們知道 emptyDir 掛載的資料的生命週期和 Pod 生命週期一致的,所以如果 Pod 掛掉了,資料也就丟失了,這也就是為什麼我們重建 Pod 後之前的資料就沒有了的原因,對應線上的監控資料肯定需要做資料的持久化的,同樣的 prometheus 這個 CRD 資源也為我們提供了資料持久化的配置方法,由於我們的 Prometheus 最終是通過 Statefulset 控制器進行部署的,所以我們這裡需要通過 storageclass 來做資料持久化, 我們之前用rook已經搭建過storageclass。所以我們就可以直接用了。我們讓prometheus 的 CRD 資源物件(prometheus-prometheus.yaml)中新增如下配置:

  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: rook-ceph-block
        resources:
          requests:
            storage: 10Gi

注意這裡的 storageClassName 名字為上面我們建立的 StorageClass 物件名稱,然後更新 prometheus 這個 CRD 資源。更新完成後會自動生成兩個 PVC 和 PV 資源物件:

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM                                           STORAGECLASS      REASON    AGE
pvc-dba11961-4ad6-11e9-baf3-005056930126   10Gi       RWO            Delete           Bound     monitoring/prometheus-k8s-db-prometheus-k8s-0   rook-ceph-block             1m
pvc-dbc6bac5-4ad6-11e9-baf3-005056930126   10Gi       RWO            Delete           Bound     monitoring/prometheus-k8s-db-prometheus-k8s-1   rook-ceph-block             1m
$ kubectl get pvc -n monitoring
NAME                                 STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
prometheus-k8s-db-prometheus-k8s-0   Bound     pvc-dba11961-4ad6-11e9-baf3-005056930126   10Gi       RWO            rook-ceph-block   2m
prometheus-k8s-db-prometheus-k8s-1   Bound     pvc-dbc6bac5-4ad6-11e9-baf3-005056930126   10Gi       RWO            rook-ceph-block   2m

現在我們再去看 Prometheus Pod 的資料目錄就可以看到是關聯到一個 PVC 物件上了。

.......
volumeMounts:
    - mountPath: /etc/prometheus/config_out
      name: config-out
      readOnly: true
    - mountPath: /prometheus
      name: prometheus-k8s-db
      subPath: prometheus-db
    - mountPath: /etc/prometheus/rules/prometheus-k8s-rulefiles-0
      name: prometheus-k8s-rulefiles-0
.........
  volumes:
  - name: prometheus-k8s-db
    persistentVolumeClaim:
      claimName: prometheus-k8s-db-prometheus-k8s-0
.........

現在即使我們的 Pod 掛掉了,資料也不會丟失了。讓我們測試一下。

我們先隨便查一下資料

刪除pod

kubectl delete pod prometheus-k8s-1 -n monitorin
kubectl delete pod prometheus-k8s-0 -n monitorin

檢視pod狀態

kubectl get pod -n monitoring
NAME                                   READY     STATUS              RESTARTS   AGE
alertmanager-main-0                    2/2       Running             0          2d
alertmanager-main-1                    2/2       Running             0          2d
alertmanager-main-2                    2/2       Running             0          2d
grafana-7489c49998-pkl8w               1/1       Running             0          2d
kube-state-metrics-d6cf6c7b5-7dwpg     4/4       Running             0          2d
node-exporter-dlp25                    2/2       Running             0          2d
node-exporter-fghlp                    2/2       Running             0          2d
node-exporter-mxwdm                    2/2       Running             0          2d
node-exporter-r9v92                    2/2       Running             0          2d
prometheus-adapter-84cd9c96c9-n92n4    1/1       Running             0          2d
prometheus-k8s-0                       0/3       ContainerCreating   0          3s
prometheus-k8s-1                       3/3       Running             0          9s
prometheus-operator-7b74946bd6-vmbcj   1/1       Running             0          2d

pod正在重新建立。等建立完成,我們再檢視一下資料

我們的資料是正常的,沒有丟失。