# Kubernetes 1.15.2监控(2)Monitor集群组件和PODS

在上一篇文章中，我们已经在 k8s 中安装了 Prometheus，并且收集了它自身的监控指标。而在这篇文章，我们将收集 k8s 所有组件和 pod 的监控指标。

在这之前需要先修改下之前监控 Prometheus 自身使用的一个配置，因为它来源于 prometheus-operator，更多的是我对它使用这个配置的理解。而我们自己直接使用的话，没有必要搞这么麻烦，下面是修改后的内容。

```
- job_name: prometheus
  honor_labels: false
  kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
          - monitoring
  scrape_interval: 30s
  relabel_configs:
    - action: keep
      source_labels:
        - __meta_kubernetes_service_label_prometheus
      regex: k8s
    - source_labels:
        - __meta_kubernetes_namespace
      target_label: namespace
    - source_labels:
        - __meta_kubernetes_service_name
      target_label: service
    - source_labels:
        - __meta_kubernetes_pod_name
      target_label: pod
    - target_label: endpoint
      replacement: web
复制代码
```

这一看就懂，我也就不多做解释了。下面我们开始进入正题，首先监控 etcd。

### 监控 etcd

之前我们通过 curl 命令访问到了 etcd 监控指标所在的 URL，知道了它的监控指标有哪些，现在我们要配置 Prometheus，让其收集这些监控数据。

之前也提到过，k8s 中 Prometheus 的监控是通过发现 endpoint 的方式进行，而 endpoint 又是 service 创建的，所以我们需要先创建一个 etcd 的 service。

我们新建一个名为 `kube-etcd-service.yml` 的文件，内容如下：

```
apiVersion: v1
kind: Service
metadata:
  name: kube-etcd
  labels:
    k8s-app: kube-etcd
  namespace: kube-system
spec:
  clusterIP: None
  ports:
    - name: http-metrics
      port: 2379
      protocol: TCP
      targetPort: 2379
  selector:
    component: etcd
  type: ClusterIP
复制代码
```

说明：

* 由于 etcd 处于 kube-system 名称空间，所以这里的 namespace 也应该是 kube-system；
* 因为 etcd pod 本身会存在 `component=etcd` 这个标签，所以这里的选择器使用的就是这个。

```
kubectl apply -f kube-etcd-service.yml
复制代码
```

创建成功后，可以通过如下命令查看它创建的 endpoint：

```
kubectl -n kube-system get endpoints kube-etcd
复制代码
```

现在通过这个 endpoint 就能够访问到后面三台 etcd，现在只需要在 Prometheus 中添加对应的配置即可，配置内容如下。

```
- job_name: kube-etcd
  honor_labels: false
  kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
          - kube-system
  scheme: https
  tls_config:
    insecure_skip_verify: false
    ca_file: /etc/prometheus/secrets/etcd-client-cert/ca.crt
    cert_file: /etc/prometheus/secrets/etcd-client-cert/healthcheck-client.crt
    key_file: /etc/prometheus/secrets/etcd-client-cert/healthcheck-client.key
  relabel_configs:
    - action: keep
      source_labels:
        - __meta_kubernetes_service_label_k8s_app
      regex: kube-etcd
    - source_labels:
        - __meta_kubernetes_namespace
      target_label: namespace
    - source_labels:
        - __meta_kubernetes_service_name
      target_label: service
    - source_labels:
        - __meta_kubernetes_pod_name
      target_label: pod
    - target_label: endpoint
      replacement: http-metrics
  metric_relabel_configs:
    - action: drop
      regex: (etcd_debugging|etcd_disk|etcd_request|etcd_server|grpc_server).*
      source_labels:
        - __name__
复制代码
```

因为访问 etcd 需要验证客户端证书，所以这里需要提供证书和私钥文件。这三个文件之前都已挂载到 Prometheus 容器中，直接拿来用就行。

下面的 `relabel_configs` 就不多提了，和前面监控 Prometheus 自身的配置差不多，最主要的还是 `metric_relabel_configs`，它是用来删除我们不需要的监控指标的。

这里将以 `etcd_debugging|etcd_disk|etcd_request|etcd_server|grpc_server` 这些开头的监控指标都删掉了，你们不要学我，最好搞清楚它的作用来决定是不是将其删掉，我纯粹是看不懂所以才删的。

最后将上面的内容粘贴进 `prometheus-config.yml`。当你 apply 配置文件后，不要急着 reload，因为 Prometheus 中可能没有立即更新，你可以通过 kubectl exec -it 连接到 Prometheus 的 pod 中验证下配置文件是否已经更新。

如果更新了，就直接通过下面命令让其 reload：

```
curl -XPOST PROMETHEUS_IP:9090/-/reload
复制代码
```

然后访问 Prometheus 的 web 页面，就可以在 targets 中看到 etcd 了。

![](data:image/svg+xml;utf8,<?xml%20version="1.0"?><svg%20xmlns="http://www.w3.org/2000/svg"%20version="1.1"%20width="972"%20height="562"></svg>)

### 监控 apiserver

apiserver 的监控方式更简单，因为它的 service 已经自动创建了。但你需要注意的是，它的 service 创建在 default 名称空间，名为 `kubernetes`。

没什么好说的，直接放出 Prometheus 监控 apiserver 的配置。

```
- job_name: kube-apiserver
  honor_labels: false
  kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
          - default
  scrape_interval: 30s
  scheme: https
  tls_config:
    insecure_skip_verify: false
    ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    server_name: kubernetes
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
  relabel_configs:
    - action: keep
      source_labels:
        - __meta_kubernetes_service_label_component
      regex: apiserver
  metric_relabel_configs:
    - source_labels:
        - __name__
      action: drop
      regex: (apiserver_storage_data_key_generation_latencies_microseconds_bucket|apiserver_admission_controller_admission_latencies_milliseconds_bucket|apiserver_admission_step_admission_latencies_milliseconds_bucket|apiserver_admission_step_admission_latencies_milliseconds_summary|apiserver_request_latencies_bucket|apiserver_request_latencies_summary|apiserver_storage_data_key_generation_latencies_microseconds_bucket|rest_client_request_latency_seconds_bucket)
复制代码
```

这里因为 `insecure_skip_verify` 设为了 false，也就是校验服务端证书，所以这里提供了 ca 文件和 server\_name。同样，这个证书事先已经挂载到了容器中，所以直接指定就行。

因为我不想关注的指标太多，所以删了一批。先声明，我是瞎删的，并不懂它们的确切意义，只是觉得没啥用，所以就删了，你们别学我！

最后你看着 reload 吧。

![](data:image/svg+xml;utf8,<?xml%20version="1.0"?><svg%20xmlns="http://www.w3.org/2000/svg"%20version="1.1"%20width="971"%20height="461"></svg>)

### 监控 pod

pod 的监控指标是 kubelet 提供的，前面也已经使用 curl 命令看到了，因此这里也是直接干。

我不知道是我之前手动创建的还是怎样，我这里已经存在了 kubelet 的 service，并且 service 只暴露了 10250 这一个端口，但是在其对应的 endpoint 上，却有以下三个端口。

```
ports:
  - name: http-metrics
    port: 10255
    protocol: TCP
  - name: cadvisor
    port: 4194
    protocol: TCP
  - name: https-metrics
    port: 10250
    protocol: TCP
复制代码
```

如果你们不是这种情况，那就自己创建一个 service。如果也是这种情况，那在 `relabel_configs` 中，不仅要对 service 标签进行过滤，还得对端口进行过滤，因为我们只需要 10250 端口。

它的配置如下：

```
- job_name: pods
  honor_labels: true
  kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
          - kube-system
  scrape_interval: 30s
  metrics_path: /metrics/cadvisor
  scheme: https
  tls_config:
    insecure_skip_verify: true
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
  relabel_configs:
    - action: keep
      source_labels:
        - __meta_kubernetes_service_label_k8s_app
      regex: kubelet
    - action: keep
      source_labels:
        - __meta_kubernetes_endpoint_port_name
      regex: https-metrics
    - source_labels:
        - __meta_kubernetes_namespace
      target_label: namespace
    - source_labels:
        - __meta_kubernetes_service_name
      target_label: service
    - source_labels:
        - __meta_kubernetes_pod_name
      target_label: pod
    - target_label: endpoint
      replacement: https-metrics
  metric_relabel_configs:
    - source_labels:
        - __name__
      regex: container_(cpu_cfs_periods_total|fs_inodes_total|fs_sector_reads_total|fs_sector_writes_total|last_seen|memory_mapped_file|memory_working_set_bytes|spec_memory_reservation_limit_bytes|spec_memory_swap_limit_bytes|tasks_state)
      action: drop
    - source_labels:
        - container
      regex: ""
      action: drop
    - action: labeldrop
      regex: (id|name|pod_name|image)
复制代码
```

上面通过下面这个配置从三个端口中筛选出我们所需要的 10250 端口，因为它的名称为 https-metrics：

```
- action: keep
  source_labels:
    - __meta_kubernetes_endpoint_port_name
  regex: https-metrics
复制代码
```

后面的 `metric_relabel_configs` 中，除了删除了一些我认为没用的监控指标外，还删除了所有 container 标签为空的监控指标，就像这些：

```
container_memory_cache{container="",container_name="",id="/",image="",name="",namespace="",pod="",pod_name=""} 2.144395264e+09 1565777155746
container_memory_cache{container="",container_name="",id="/kubepods.slice",image="",name="",namespace="",pod="",pod_name=""} 7.14027008e+08 1565777155757
container_memory_cache{container="",container_name="",id="/kubepods.slice/kubepods-besteffort.slice",image="",name="",namespace="",pod="",pod_name=""} 3.5053568e+08 1565777145294
container_memory_cache{container="",container_name="",id="/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-poda213e57700fd1325c59a70dd70f1a07d.slice",image="",name="",namespace="kube-system",pod="etcd-ops.k8s.master.01.sit",pod_name="etcd-ops.k8s.master.01.sit"} 3.09784576e+08 1565777149076
container_memory_cache{container="",container_name="",id="/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-podcfdbebf5_8211_11e9_8699_005056a7c0c5.slice",image="",name="",namespace="kube-system",pod="kube-proxy-xw8sw",pod_name="kube-proxy-xw8sw"} 4.0767488e+07 1565777147425
复制代码
```

实在不知道这些是干啥的，而且数量非常多，干脆直接删掉。同时还删除了一些标签，比如下面这种的：

```
container_fs_io_current{container="coredns",container_name="coredns",device="/dev/mapper/centos-root",endpoint="https-metrics",id="/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-poda9693624_85e3_11e9_80cb_005056a7c0c5.slice/docker-9b95ca870a0e7a8fde7ce116b07fc696bd91bea75f6bc61478f3ecc98f36f131.scope",image="sha256:eb516548c180f8a6e0235034ccee2428027896af16a509786da13022fe95fe8c",instance="10.99.100.2:10250",job="pods",name="k8s_coredns_coredns-fb8b8dccf-9dvh7_kube-system_a9693624-85e3-11e9-80cb-005056a7c0c5_2",namespace="kube-system",pod="coredns-fb8b8dccf-9dvh7",pod_name="coredns-fb8b8dccf-9dvh7",service="kubelet"}
复制代码
```

可以看到 id、name 和 image 这些标签的值非常长，不好看不说，还浪费内存和存储资源，我就都删了。它貌似会造成一个 pod 中如果有多个容器，你可能无法知道这个容器是监控指标中是哪一个，因为 image 这个标签被删了。

反正我删了，你看着办。删除之后就是这样：

```
container_fs_io_current{container="coredns",container_name="coredns",device="/dev/mapper/centos-root",endpoint="https-metrics",instance="10.99.100.2:10250",job="pods",namespace="kube-system",pod="coredns-fb8b8dccf-9dvh7",service="kubelet"}
复制代码
```

当然很多标签是我们自己创建，如果你不需要，可以在上面的 `relabel_configs` 中删除对应标签的配置。或许你还想删其他的标签，那你往上面的配置中加就行。

最后记得 reload。

![](data:image/svg+xml;utf8,<?xml%20version="1.0"?><svg%20xmlns="http://www.w3.org/2000/svg"%20version="1.1"%20width="1280"%20height="426"></svg>)

### 安装 kube-state-metrics

k8s 的其他组件我就不继续监控了，包括 kubelet、controller manager、coredns 等，它们监控的手段和之前的几个组件都差不多，相信你们自己弄起来也是轻轻松松。

下面我们会安装 kube-state-metrics，这个东西会连接 apiserver，然后将集群里的各种资源的指标都暴露出来，比如 configMap、ingress、secret、pod、deployment、statefulset 等，这是对 pod 指标的一大补充，非常有用。

#### RBAC 权限

因为它要访问集群内的所有资源，才能将它们的信息提供出去，因此部署它之前，先为它创建一些权限。这些权限都会绑定到一个 serviceAccount 上，然后我们用这个 sa 运行 kube-state-metrics 就行。

kube-state-metrics-clusterRole.yml：

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kube-state-metrics
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - secrets
      - nodes
      - pods
      - services
      - resourcequotas
      - replicationcontrollers
      - limitranges
      - persistentvolumeclaims
      - persistentvolumes
      - namespaces
      - endpoints
    verbs:
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - daemonsets
      - deployments
      - replicasets
      - ingresses
    verbs:
      - list
      - watch
  - apiGroups:
      - apps
    resources:
      - statefulsets
      - daemonsets
      - deployments
      - replicasets
    verbs:
      - list
      - watch
  - apiGroups:
      - batch
    resources:
      - cronjobs
      - jobs
    verbs:
      - list
      - watch
  - apiGroups:
      - autoscaling
    resources:
      - horizontalpodautoscalers
    verbs:
      - list
      - watch
  - apiGroups:
      - authentication.k8s.io
    resources:
      - tokenreviews
    verbs:
      - create
  - apiGroups:
      - authorization.k8s.io
    resources:
      - subjectaccessreviews
    verbs:
      - create
  - apiGroups:
      - policy
    resources:
      - poddisruptionbudgets
    verbs:
      - list
      - watch
  - apiGroups:
      - certificates.k8s.io
    resources:
      - certificatesigningrequests
    verbs:
      - list
      - watch
复制代码
```

kube-state-metrics-clusterRoleBinding.yml：

```
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kube-state-metrics
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kube-state-metrics
subjects:
  - kind: ServiceAccount
    name: kube-state-metrics
    namespace: monitoring
复制代码
```

kube-state-metrics-role.yml：

```
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: kube-state-metrics
  namespace: monitoring
rules:
  - apiGroups:
      - ""
    resources:
      - pods
    verbs:
      - get
  - apiGroups:
      - extensions
    resourceNames:
      - kube-state-metrics
    resources:
      - deployments
    verbs:
      - get
      - update
  - apiGroups:
      - apps
    resourceNames:
      - kube-state-metrics
    resources:
      - deployments
    verbs:
      - get
      - update
复制代码
```

kube-state-metrics-roleBinding.yml：

```
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kube-state-metrics
  namespace: monitoring
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: kube-state-metrics
subjects:
  - kind: ServiceAccount
    name: kube-state-metrics
复制代码
```

kube-state-metrics-serviceAccount.yml：

```
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kube-state-metrics
  namespace: monitoring
复制代码
```

#### deployment 和 service

kube-state-metrics 会提供两个指标页面，一个是暴露集群内资源的，另一个是它自身的，它自身的可以选择性的关注。

先创建 kube-state-metrics-deployment.yml：

```
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: kube-state-metrics
  name: kube-state-metrics
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kube-state-metrics
  template:
    metadata:
      labels:
        app: kube-state-metrics
    spec:
      containers:
        - args:
            - --port=10000
            - --telemetry-port=10001
          image: quay.io/coreos/kube-state-metrics:v1.6.0
          name: kube-state-metrics
          resources:
            limits:
              cpu: 100m
              memory: 150Mi
            requests:
              cpu: 100m
              memory: 150Mi
        - command:
            - /pod_nanny
            - --container=kube-state-metrics
            - --cpu=100m
            - --extra-cpu=2m
            - --memory=150Mi
            - --extra-memory=30Mi
            - --threshold=5
            - --deployment=kube-state-metrics
          env:
            - name: MY_POD_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.name
            - name: MY_POD_NAMESPACE
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
          image: k8s.gcr.io/addon-resizer:1.8.4
          name: addon-resizer
          resources:
            limits:
              cpu: 50m
              memory: 30Mi
            requests:
              cpu: 10m
              memory: 30Mi
      nodeSelector:
        kubernetes.io/os: linux
      securityContext:
        runAsNonRoot: true
        runAsUser: 65534
      serviceAccountName: kube-state-metrics
复制代码
```

指定了两个启动参数，也就是两个端口，其中 10000 是暴露集群资源指标的端口，10001 就是它自身了。除了 kube-state-metrics 之外，还启动了 addon-resizer 这个容器，我不知道它是干啥的，反正官方怎么搞我们怎么弄就是了。

最后是 service 文件 kube-state-metrics-service.yml：

```
apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: kube-state-metrics
  name: kube-state-metrics
  namespace: monitoring
spec:
  clusterIP: None
  ports:
    - name: http-main
      port: 10000
      targetPort: 10000
    - name: http-self
      port: 10001
      targetPort: 10001
  selector:
    app: kube-state-metrics
复制代码
```

两个端口都暴露出来，你可以都收集或者只收集 10000 端口。如果只收集 10000，你可以只暴露一个端口，也可以两个都暴露，然后在 Prometheus 配置中过滤掉一个端口即可。

#### 收集监控数据

将上面所有的文件都 apply 之后，就可以直接配置 Prometheus 进行收集了。在此之前，你可以使用 curl 命令访问它的指标页面，看看里面都有啥：

```
curl IP:10000/metrics
curl IP:10001/metrics
复制代码
```

这个 ip 地址不用我说怎么拿吧，然后你就可以看到集群资源的指标非常非常非常的多，我觉得你最好对其进行过滤，将不需要的统统拒绝掉，不然对 Prometheus 造成的压力很大。

然后下面就是 Prometheus 的配置：

```
- job_name: kube-state-metrics
  honor_labels: true
  kubernetes_sd_configs:
    - role: endpoints
      namespaces:
        names:
          - monitoring
  scrape_interval: 30s
  scrape_timeout: 30s
  tls_config:
    insecure_skip_verify: true
  bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
  relabel_configs:
    - action: keep
      source_labels:
        - __meta_kubernetes_service_label_k8s_app
      regex: kube-state-metrics
    - action: keep
      source_labels:
        - __meta_kubernetes_endpoint_port_name
      regex: http-main
  metric_relabel_configs:
    - source_labels:
        - __name__
      regex: (kube_daemonset_status_number_ready|kube_daemonset_status_number_unavailable|kube_deployment_status_replicas_unavailable|kube_deployment_spec_paused|kube_deployment_spec_strategy_rollingupdate_max_surge|kube_deployment_spec_strategy_rollingupdate_max_unavailable|kube_endpoint_address_available|kube_endpoint_address_not_ready|kube_node_info|kube_node_spec_unschedulable|kube_node_status_condition|kube_node_status_capacity|kube_node_status_capacity|kube_node_status_allocatable|kube_persistentvolumeclaim_info|kube_persistentvolumeclaim_status_phase|kube_persistentvolumeclaim_resource_requests_storage_bytes|kube_persistentvolume_status_phase|kube_persistentvolume_info|kube_persistentvolume_capacity_bytes|kube_pod_info|kube_pod_status_phase|kube_pod_status_ready|kube_pod_container_info|kube_pod_container_status_waiting|kube_pod_container_status_waiting_reason|kube_pod_container_status_running|kube_pod_container_status_terminated_reason|kube_pod_container_status_last_terminated_reason|kube_pod_container_status_restarts_total|kube_pod_container_resource_limits|kube_service_info|kube_statefulset_status_replicas_current|kube_statefulset_status_replicas_ready)
      action: keep
复制代码
```

配置的内容就无需我多提了，和前面监控的配置都差不多。

主要是这里删除了一大批我不关注的指标，注意我这里做的是白名单，只收集我指定的，因为不需要的实在太多，写不过来。虽然正则表达式这么长，但是由于指标名称短，且 regex 默认锚定了行首和行尾，所以匹配效率还是很高的。

最后记得 reload。

![](https://user-gold-cdn.xitu.io/2019/8/21/16cb4215cddb8d55?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)

ok，本文到此为止，下一篇会提到如何使用 grafana 和 alertmanager，谢谢！
