Kubernetes 监控

环境信息

  • Centos 7
  • Kubernetes 1.24

Kubernetes Metrics Server

Kubernetes Metrics Server 从 kubelet 收集资源使用指标(情况)并通过 Metrics API 将其暴露给 Kubernetes API Server,以供 HPA(Horizontal Pod Autoscaler) 和 VPA(Vertical Pod Autoscaler) 使用。kubectl top 也使用 Metrics API。[1]

安装 Kubernetes Metrics Server

Kubernetes Metrics Server 安装之前必须要开启 kube api-server 的聚合层功能以及认证鉴权功能 [3]

检查 Kubernetes API Server 是否启用了聚合层功能及认证鉴权功能

$ ps -ef | grep apiserver | grep -E "authorization-mode|enable-aggregator-routing"
root 390 369 7 13:55 ? 00:00:37 kube-apiserver --advertise-address=172.31.26.116 \
--allow-privileged=true \
--authorization-mode=Node,RBAC \
--client-ca-file=/etc/kubernetes/pki/ca.crt \
--enable-admission-plugins=NodeRestriction \
--enable-bootstrap-token-auth=true \
--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt \
--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt \
--etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key \
--etcd-servers=https://127.0.0.1:2379 \
--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key \
--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname \
--proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt \
--proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key \
--requestheader-allowed-names=front-proxy-client \
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt \
--requestheader-extra-headers-prefix=X-Remote-Extra- \
--requestheader-group-headers=X-Remote-Group \
--requestheader-username-headers=X-Remote-User \
--secure-port=6443 --service-account-issuer=https://kubernetes.default.svc.cluster.local \
--service-account-key-file=/etc/kubernetes/pki/sa.pub \
--service-account-signing-key-file=/etc/kubernetes/pki/sa.key \
--service-cluster-ip-range=10.96.0.0/12 \
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt --tls-private-key-file=/etc/kubernetes/pki/apiserver.key \
--enable-aggregator-routing=true

输出中包含了 --authorization-mode=Node,RBAC--enable-aggregator-routing=true,表示已开启对应功能。如若未开启,修改 api-server 的 manifest 文件 /etc/kubernetes/manifests/kube-apiserver.yaml,在 kube-apiserver 的启动参数中添加 --enable-aggregator-routing=true--authorization-mode=Node,RBAC

以下命令使用 yaml 文件安装最新版本 Metrics Server [2]

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

Kubernetes Metrics Server 部署配置常见错误

Failed to scrape node

参考部署步骤 部署后,Metrics Server 的 Pod 一直处于未就绪状态

$ kubectl get pods -A | grep metrics
kube-system metrics-server-5cdf47479d-rwtd6 0/1 Running 0 5m53s

检查 Pod 日志

$ kubectl logs -n kube-system metrics-server-5cdf47479d-rwtd6
1 scraper.go:140] "Failed to scrape node" err="Get \"https://172.31.21.3:10250/metrics/resource\": x509: cannot validate certificate for 172.31.21.3 because it doesn't contain any IP SANs" node="k8smaster3"
1 scraper.go:140] "Failed to scrape node" err="Get \"https://172.31.26.116:10250/metrics/resource\": x509: cannot validate certificate for 172.31.26.116 because it doesn't contain any IP SANs" node="k8smaster1"
1 scraper.go:140] "Failed to scrape node" err="Get \"https://172.31.19.164:10250/metrics/resource\": x509: cannot validate certificate for 172.31.19.164 because it doesn't contain any IP SANs" node="k8smaster2"

根据日志提示,由于 metrics-server 未获得TLS Bootstrap 签发证书的导致访问各节点资源时报错。

  1. 参考解决办法
  2. 添加选项 --kubelet-insecure-tls 不验证 Kubelet 的 CA 证书
    spec:
    containers:
    - args:
    - --cert-dir=/tmp
    - --secure-port=4443
    - --kubelet-preferred-address-types=InternalIP
    - --kubelet-use-node-status-port
    - --metric-resolution=15s
    - --kubelet-insecure-tls

no such host

参考部署步骤 部署后,Metrics Server 的 Pod 一直处于未就绪状态,检查 Pod 日志

$ kubectl logs -n kube-system metrics-server-5cdf47479d-rwtd6
"Failed to scrape node" err="Get \"https://k8smaster1:10250/metrics/resource\": dial tcp: lookup k8smaster1 on 10.96.0.10:53: no such host" node="k8smaster1"
"Failed probe" probe="metric-storage-ready" err="no metrics to serve"
"Failed to scrape node" err="Get \"https://k8smaster1:10250/metrics/resource\": dial tcp: lookup k8smaster1 on 10.96.0.10:53: no such host" node="k8smaster1"
"Failed to scrape node" err="Get \"https://k8smaster3:10250/metrics/resource\": dial tcp: lookup k8smaster3 on 10.96.0.10:53: no such host" node="k8smaster3"
"Failed to scrape node" err="Get \"https://k8sworker1:10250/metrics/resource\": dial tcp: lookup k8sworker1 on 10.96.0.10:53: no such host" node="k8sworker1"
"Failed to scrape node" err="Get \"https://k8sworker2:10250/metrics/resource\": dial tcp: lookup k8sworker2 on 10.96.0.10:53: no such host" node="k8sworker2"
"Failed to scrape node" err="Get \"https://k8smaster2:10250/metrics/resource\": dial tcp: lookup k8smaster2 on 10.96.0.10:53: no such host" node="k8smaster2"
"Failed probe" probe="metric-storage-ready" err="no metrics to serve"
I0524 09:32:43.033797 1 server.go:187] "Failed probe" probe="metric-storage-ready" err="no metrics to serve"

根据日志显示,API Server 的主机名解析存在问题。这是因为节点主机名在集群的 DNS 中无法解析导致,可以通过在 Metrics Server 的 Pod 中手动添加解析解决

metadata:
labels:
k8s-app: metrics-server
spec:
hostAliases:
- ip: '172.31.26.116'
hostnames:
- 'fm-k8s-c1-master1'
- ip: '172.31.19.164'
hostnames:
- 'fm-k8s-c1-master2'
- ip: '172.31.21.3'
hostnames:
- 'fm-k8s-c1-master3'
- ip: '172.31.16.124'
hostnames:
- 'fm-k8s-c1-worker1'
- ip: '172.31.22.159'
hostnames:
- 'fm-k8s-c1-worker2'
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
image: registry.k8s.io/metrics-server/metrics-server:v0.6.3

Metrics Server 无法获取所在节点的主机的监控信息

Metrics Server 部署成功后,无法获取所在节点的主机的监控信息,获取其他主机的监控信息正常,具体信息如下

$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-master1 Ready control-plane 169d v1.24.7 172.31.26.116 <none> CentOS Linux 7 (Core) 5.4.225-1.el7.elrepo.x86_64 docker://20.10.9
k8s-master2 Ready control-plane 169d v1.24.7 172.31.19.164 <none> CentOS Linux 7 (Core) 5.4.225-1.el7.elrepo.x86_64 docker://20.10.9
k8s-master3 Ready control-plane 169d v1.24.7 172.31.21.3 <none> CentOS Linux 7 (Core) 5.4.225-1.el7.elrepo.x86_64 docker://20.10.9
k8s-worker1 Ready <none> 169d v1.24.7 172.31.16.124 <none> CentOS Linux 7 (Core) 5.4.225-1.el7.elrepo.x86_64 docker://20.10.9
k8s-worker2 Ready <none> 169d v1.24.7 172.31.22.159 <none> CentOS Linux 7 (Core) 3.10.0-1160.80.1.el7.x86_64 docker://20.10.9

$ kubectl get pods -A | grep met
metrics-server-c48655c66-jxwpt 1/1 Running 0 12m 10.244.4.138 k8s-worker1 <none> <none>

$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k8s-master1 381m 9% 3263Mi 43%
k8s-master2 149m 3% 3432Mi 45%
k8s-master3 166m 4% 3163Mi 41%
k8s-worker2 2093m 13% 21933Mi 71%
k8s-worker1 <unknown> <unknown> <unknown> <unknown>

查看 Metrics Server Pod 日志,显示

$ kubectl logs -n kube-system metrics-server-c48655c66-jxwpt
E0525 05:19:42.085468 1 scraper.go:140] "Failed to scrape node" err="Get \"https://172.31.16.124:10250/metrics/resource\": dial tcp 172.31.16.124:10250: connect: no route to host" node="fm-k8s-c1-worker1"
E0525 05:19:57.125457 1 scraper.go:140] "Failed to scrape node" err="Get \"https://172.31.16.124:10250/metrics/resource\": dial tcp 172.31.16.124:10250: connect: no route to host" node="fm-k8s-c1-worker1"
E0525 05:20:12.101468 1 scraper.go:140] "Failed to scrape node" err="Get \"https://172.31.16.124:10250/metrics/resource\": dial tcp 172.31.16.124:10250: connect: no route to host" node="fm-k8s-c1-worker1"
E0525 05:20:27.077495 1 scraper.go:140] "Failed to scrape node" err="Get \"https://172.31.16.124:10250/metrics/resource\": dial tcp 172.31.16.124:10250: connect: no route to host" node="fm-k8s-c1-worker1"

根据日志显示,是因为无法连接节点的 kubelet (端口 10250) 导致。为了定位问题,修改 Metrics Server 的部署 Yaml 文件,在其中加入一个容器,来方便使用工具测试问题。参考以下内容,在 Pod 中加入容器 centos:centos7.9.2009

metrics-server.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: metrics-server
strategy:
rollingUpdate:
maxUnavailable: 0
template:
metadata:
labels:
k8s-app: metrics-server
spec:
containers:
- image: centos:centos7.9.2009
command:
- ping
- 127.0.0.1
name: centos7
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls
image: registry.k8s.io/metrics-server/metrics-server:v0.6.3
imagePullPolicy: IfNotPresent
...

重新部署后,登陆容器 centos7,安装所需工具进行测试。根据日志信息,首先测试 Pod 是否能连接到节点的 kubelet

$ curl -v 172.31.16.124:10250
* About to connect() to 172.31.16.124 port 10250 (#0)
* Trying 172.31.16.124...
* No route to host
* Failed connect to 172.31.16.124:10250; No route to host
* Closing connection 0
curl: (7) Failed connect to 172.31.16.124:10250; No route to host

$ curl -v 172.31.22.159:10250
* About to connect() to 172.31.22.159 port 10250 (#0)
* Trying 172.31.22.159...
* Connected to 172.31.22.159 (172.31.22.159) port 10250 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 172.31.22.159:10250
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 400 Bad Request
<
Client sent an HTTP request to an HTTPS server.
* Closing connection 0

根据以上测试,Metrics Server 的 Pod 无法连接所在节点 k8s-worker1(172.31.16.124)的 kubelet (172.31.16.124:10250),可以正常连接其他节点的 kubelet。由此可以确定问题原因。

考虑到 Metrics Server 的 Pod 只是访问不到宿主节点所在的 kubelet,可以访问其他节点的 kubelet,梳理其中的网络连同流程发现,在访问其他节点的 kubelet 时,Metrics Server Pod 的报文在流出宿主节点前,会被 SNAT 为宿主节点的出口 IP,报文源 IP 为 宿主节点的 IP。而访问宿主节点的 kubelet 的报文,其源 IP 为 Metrics Server Pod 的 IP,目的 IP 为宿主节点的 IP。怀疑可能因为集群节点上的 iptables 允许集群节点的 IP 访问 kubelet,而 Pod 的 IP 未被允许访问 kubelet。为验证此猜想,在节点 k8s-worker1 的 iptables 添加允许 Metrics Server Pod 的 IP 访问的规则进行测试

iptables -I INPUT 7 -s 10.244.4.138 -j ACCEPT

再次测试和 kubelet 的连通性,发现可以正常连通,再次检查 kubectl top node,可以查到所有节点的监控数据

$ curl -v 172.31.16.124:10250
* About to connect() to 172.31.16.124 port 10250 (#0)
* Trying 172.31.16.124...
* Connected to 172.31.16.124 (172.31.16.124) port 10250 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 172.31.16.124:10250
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 400 Bad Request
<
Client sent an HTTP request to an HTTPS server.
* Closing connection 0

$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
fm-k8s-c1-master1 286m 7% 3242Mi 42%
fm-k8s-c1-master2 150m 3% 3262Mi 43%
fm-k8s-c1-master3 251m 6% 3247Mi 42%
fm-k8s-c1-worker1 166m 1% 4317Mi 13%
fm-k8s-c1-worker2 2013m 12% 21684Mi 70%

脚注