K8s api server 无法连接 metrics server 问题解决方案

api server 无法连接 metrics server 问题解决方案


⚠️ 阅读本文前请注意:

本文仅针对以二进制包部署集群的场景,如果使用 kubeadm 的方式部署集群的话并不问题出现本文的这个问题。


一、引言

metrics-server 是 Kubernetes 生态中的一个重要组件,其主要的作用在于监测 Kubernetes的(node、pod)资源指标并提供监控系统做采集。Kubernetes 的许多特性都会依赖 metrics server,比如 kubectl top nodes/pods 指令;比如 HPA 需要根据其获取资源利用率;再比如 Istio 的服务组件等。

所以当 metrics-server 出现异常时,相关的组件都会受到影响。比如,如下这种典型的问题:

执行 kubectl top nodes 指令失败

报错信息如下:

Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io

其根本原因在于 Kubernetes 的 ApiServer 无法访问到 metrics-server,要验证这种问题,我们可以执行如下命令:

kubectl describe apiservice v1beta1.metrics.k8s.io

返回值如下:

image-20210709170913395.png

可以看到访问 https://10.244.1.11:4443/apis/metrics.k8s.io/v1beta1 这个 API 异常,其访问的IP是一个典型的 clusterIP。

kubectl get svc -n kube-system metrics-server
image-20210709171522584.png

我们知道,通常情况下,Kubernetes 的 master 节点是访问不到 clusterIP 的。

image-20210709171725553.png

而 Kubernetes 的 node 节点则可以访问的到,其主要的原因在于 kube-proxy,那么如果想要 master 节点可以访问到 Service 的 clusterIP,就要在 master 节点上也部署 kube-proxy ,确切的说是将 master 节点同时也作为 node 节点加入集群中去。并且为了避免调度对 master 节点造成影响,还需要对 master 节点打污点处理,这就是这次问题解决方案的主要思路。



二、环境参数

1、相关组件信息

参数 版本
OS CentOS Linux release 7.9.2009 (Core)
Docker v20.10.7
Kubernetes v1.18.18
kubectl v1.18.18
etcd v3.4.14

2、Kuernetes 集群信息

角色 主机名 IP
master 节点(kubectl) t-k8sM-001 192.168.3.171
master 节点 t-k8sM-002 192.168.3.172
master 节点 t-k8sM-003 192.168.3.173
node 节点 t-k8sN-001 192.168.3.174
node 节点 t-k8sN-002 192.168.3.175
Nginx Proxy t-k8s-nginx 192.168.3.201


三、部署Docker

这里提供一个一键化部署脚本(也可以以其他方式自行安装,建议 docker 版本同其他节点 docker 版本一致。

脚本内容如下:

#!/bin/bash

echo "Docker 一键安装开始!"

# 清理残留版本
yum remove -y docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-engine && \

# 设置存储库
yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2 && \

# 添加 yum 源
wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo && \

# 修改国内镜像源
sed -i 's#download.docker.com#mirrors.tuna.tsinghua.edu.cn/docker-ce#g' /etc/yum.repos.d/docker-ce.repo && \

# 安装最新版本(也可以指定版本,如 docker-ce-18.09.5-3.el7)
yum install -y docker-ce && \

# 添加开机自启
systemctl enable docker 

# 生成 docker 镜像加速地址以及日志清理的配置文件。
if [ ! -d "/etc/docker" ]; then
mkdir /etc/docker
fi

cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://5a8zducs.mirror.aliyuncs.com"],
  "log-driver":"json-file",
  "log-opts": {"max-size":"500m", "max-file":"3"}
}
EOF

# 启动 docker
systemctl start docker

echo "Docker 安装已完成!"

docker version


四、部署 kubelet

1、环境准备

⚠️ 注意:

对于三个 master 节点 进行管理操作时推荐使用 ansible 进行批量处理,以下内容是以 ansible 操作进行展开的。

终端机对于所有节点进行ssh免密通信,然后在 ansible 主机清单添加对应标签,内容如下:

[t-k8sM-1.18]
192.168.3.171 
192.168.3.172 
192.168.3.173 

[t-k8sN-1.18]
192.168.3.174 
192.168.3.175 

[t-k8s-1.18:children]
t-k8sM-1.18
t-k8sN-1.18

1)创建相关目录

1.创建 Kubernetes 程序目录(可选)
# 终端机操作
ansible t-k8s-1.18 -m file -a "path=/data/application/kubernetes state=directory owner=root group=root"

目录结构说明:

/data/application/kubernetes/
├── bin         # 命令程序目录
├── cfg         # 配置文件目录
├── logs        # 日志目录
└── ssl         # 证书目录
2. 创建 kubelet 数据目录

Node 节点已部署 kubelet,故只针对 Master 节点进行批量处理

# 终端机操作
ansible t-k8sM-1.18 -m file -a "path=/var/lib/kubelet state=directory owner=root group=root"

2)分发配置文件

1. 分发 bootstrap 认证配置文件
1⃣️ 分发文件: bootstrap.kubeconfig
# 终端机操作
ansible t-k8sM-1.18 -m copy -a "src=/data/softwares/k8s1.18/bootstrap.kubeconfig dest=/data/application/kubernetes/cfg/ owner=root group=root"

文件内容:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR2akNDQXFhZ0F3SUJBZ0lVRmIwRWRtMjhRWWx2T2t2Uk1FRWJ1THpBNjh3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pURUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURBT0JnTlZCQWNUQjBKbAphV3BwYm1jeEREQUtCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByCmRXSmxjbTVsZEdWek1CNFhEVEl4TURVeE1qQTRNRFF3TUZvWERUSTJNRFV4TVRBNE1EUXdNRm93WlRFTE1Ba0cKQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURBT0JnTlZCQWNUQjBKbGFXcHBibWN4RERBSwpCZ05WQkFvVEEyczRjekVQTUEwR0ExVUVDeE1HVTNsemRHVnRNUk13RVFZRFZRUURFd3ByZFdKbGNtNWxkR1Z6Ck1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBcXBManJTT0Z1U3NvRjVaYlh0dnoKNFZmRFAzOUFtdmtRdWkyZEhVOEpXOFVKT2xhaGlqWDQwaTNNWml5OFQ0K3dVS2Q4M0xRL1ZVOThOOGR6aEJ4awowdlhFY0RMNkh0aDRnWkcwQXZUaU5uNTVhbUdsZHBTOUxYTXNuTFV4amtidzk3UWtlbnNqZVQ0MGdMaW9BUkhzCjkwSGphbGtLS0dBV1hyVjZmT2RLVW9oamxUNXlDdGc3RXNLOG5tOWJVR2NXa09PNWtXSGREZEhqcEc2U3E1RFoKeE5kOU50aHlkZTBiRFlrdUZyUExhSW9tVGNjRk10VGoyOVgzR2Y0OHl1d1Z3b1piaU1ZaU44SDlXSnlzMHdoagorT05BTURmMEN1TFBWSk9HZ0VqTlpvYm1wb2JJazlQbHdGa0RUZFBja0xTN3c4bjNmaGcrb0ZHSXZTWkhkK1E0ClFRSURBUUFCbzJZd1pEQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFqQWQKQmdOVkhRNEVGZ1FVeWdvWDdlWUhzWmQ2TDZrT3BIcit2ZlhlNDdJd0h3WURWUjBqQkJnd0ZvQVV5Z29YN2VZSApzWmQ2TDZrT3BIcit2ZlhlNDdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFFSitGSmZ6b1NuMDRWcGhVNE9KCmFIYUoxNWY0bVdaL2JmTllRdDZLRmVZWEVzV2dlN1ZZMEtWVFBlaVp4WFY2YlZYZFM1TUd5K1RuOUxMSGZpVVUKTy9qdnRGOWJkcHRZQU5HSUtuRFEwWW9zZ24rQVNyb3JHck42RlhqTFlOUDl1aGUxdGlxWmJUOHhVbkRmcXJRZQpoN202TGdpTDNXa0VKRDhPRG5kMSszRjFYZGRubThadWZrWXBxYnNibWpFaGMwalhFNlNoWEg0RGF3TzFzeU51ClVnZi81b1dFSHJTdUJhSlVibjEwWVcrOHI3L1ZQU05GQmI1THR0WktnNzVUMTJtYnZVWFgzejg3RlN3bElwdUwKTDZZUW9xWnc2UWVIZ3I3dG9WeFQ5VkNGYkRTelQ4RDBUUGlQU25abFl1SmhHeEpzeGxUYUgzWGJvUGdINk8rLwpIUmM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    server: https://192.168.3.201:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubelet-bootstrap
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: kubelet-bootstrap
  user:
    token: c47ffb939f5ca36231d9e3121a252940
2⃣️ 修改 bootstrap.kubeconfig

由于我们在正式情况下,并不会将 master 节点作为 node 节点来使用,所以对于 master 节点的 kubelet 来说就没有必要使用 nginx proxy 的 apiserver 地址了,直接配置本地的 apiserver 地址的 6443端口就可以了,因为我们 apiserver 监听的是内网网卡IP,故做以下修改,如果同时也监听本地回环地址的话,使用 127.0.0.1:6443 也可以。

# 终端机操作
ansible t-k8sM-1.18 -m shell -a 'sed -i "s/192.168.3.201/$( hostname -I|cut -d " " -f 1)/g" /data/application/kubernetes/cfg/bootstrap.kubeconfig'
2. 分发 kubelet 配置文件
1⃣️ 分发文件: kubelet.conf
# 终端机操作
ansible t-k8sM-1.18 -m copy -a "src=/data/softwares/k8s1.18/kubelet.conf dest=/data/application/kubernetes/cfg/ owner=root group=root"

文件内容:

KUBELET_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/data/application/kubernetes/logs \
--hostname-override=t-k8sN-001 \
--network-plugin=cni \
--kubeconfig=/data/application/kubernetes/cfg/kubelet.kubeconfig \
--bootstrap-kubeconfig=/data/application/kubernetes/cfg/bootstrap.kubeconfig \
--config=/data/application/kubernetes/cfg/kubelet-config.yml \
--cert-dir=/data/application/kubernetes/ssl \
--pod-infra-container-image=lizhenliang/pause-amd64:3.0 \
--node-labels=node.kubernetes.io/k8s-node=true"
文件: kubelet-config.yml
# 终端机操作
ansible t-k8sM-1.18 -m copy -a "src=/data/softwares/k8s1.18/kubelet-config.yml dest=/data/application/kubernetes/cfg/ owner=root group=root"

文件内容:

kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 0.0.0.0
port: 10250
readOnlyPort: 10255
cgroupDriver: cgroupfs
clusterDNS:
- 10.0.0.2
clusterDomain: cluster.local 
failSwapOn: false
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 2m0s
    enabled: true
  x509:
    clientCAFile: /data/application/kubernetes/ssl/ca.pem 
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 5m0s
    cacheUnauthorizedTTL: 30s
evictionHard:
  imagefs.available: 15%
  memory.available: 100Mi
  nodefs.available: 10%
  nodefs.inodesFree: 5%
maxOpenFiles: 1000000
maxPods: 110
2⃣️ 修改 kubelet.conf

修改内容如下:

  • **--hostname-override **

修改对应的主机名

  • 监听IP地址(可选)

如果监听地址使用的是 0.0.0.0,可以不用修改

  • **--node-labels **

修改为 node.kubernetes.io/k8s-master=true


--node-labels 选项的作用:

这个选项的作用在声明节点角色,对于 master 节点来说,需要设置为 node.kubernetes.io/k8s-master=true,(kubeadm的配置参数为 node-role.kubernetes.io/master),配置后 label 就会告诉 k8s 调度器当前节点为 master 节点


2、启动服务

1)分发启动文件

1. 分发文件: kubelet.service
# 终端机操作
ansible t-k8sM-1.18 -m copy -a "src=/data/softwares/k8s1.18/kubelet.service dest=/usr/lib/systemd/system/ owner=root group=root mode=0644"

文件内容:

[Unit]
Description=Kubernetes Kubelet
After=docker.service

[Service]
EnvironmentFile=/data/application/kubernetes/cfg/kubelet.conf
ExecStart=/data/application/kubernetes/bin/kubelet $KUBELET_OPTS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

2)启动服务

1. 重载自定义配置
# 终端机操作
ansible t-k8sM-1.18 -m shell -a "systemctl daemon-reload"
2. 加入开机自启
# 终端机操作
ansible t-k8sM-1.18 -m shell -a "systemctl enable kubelet.service"
3. 启动服务
# 终端机操作
ansible t-k8sM-1.18 -m shell -a "systemctl start kubelet.service"

3)证书授权

1. 查看未授权请求
# 任意 master 节点操作
kubectl get csr

执行成功,返回值类似下方:

NAME                                                   AGE   SIGNERNAME                                    REQUESTOR           CONDITION
node-csr-8nETD46VxpINtCH1gBT1mCNEJGbyHCpSzvVZaUqJxlU   18m   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Pending
2. 授权 TLS
# 任意 master 节点操作
kubectl get csr|grep "Pending"|awk 'NR>0{print $1}'|xargs kubectl certificate approve

执行成功,返回值类似下面:

certificatesigningrequest.certificates.k8s.io/node-csr-8nETD46VxpINtCH1gBT1mCNEJGbyHCpSzvVZaUqJxlU approved
3. 检查就绪状态
1⃣️ 检查 Kubernetes 集群状态
# 任意 master 节点操作
kubectl get nodes

返回如下:

image-20210712165616108.png
2⃣️ 检查 etcd集群状态
# 任意 master 节点操作
ETCDCTL_API=3 ./bin/etcdctl  --cacert="/data/application/etcd/ssl/ca.pem" --cert="/data/application/etcd/ssl/server.pem" --key="/data/application/etcd/ssl/server-key.pem" --endpoints="https://192.168.3.171:2379,https://192.168.3.172:2379,https://192.168.3.173:2379" endpoint health

正常情况,返回类似下面:

https://192.168.3.172:2379 is healthy: successfully committed proposal: took = 22.91948ms
https://192.168.3.171:2379 is healthy: successfully committed proposal: took = 26.761636ms
https://192.168.3.173:2379 is healthy: successfully committed proposal: took = 32.129748ms

4)master 节点设置污点

上面我们说到,设置选项 --node-labels 后,节点可以声明自己的角色,但是并不是说这样就不会被调度。而正式环境为了避免应用程序对 master 节点的干扰,特别是 etcd 集群,个人建议 master 节点要独立。


污点标签:

K8s 节点可以通过打污点(Taint)的形式来设置节点的调度倾向,污点的组成形式如 key=value:effect,每一个污点有一个 key:values 作为污点标签,而 effect 则用来描述污点的作用。有以下三种 effect 选项可以使用:

  • NoSchedule

    K8s 不会将 pod 调度到具有该污点的 Node 上

  • PreferNoSchedule

    K8s 将尽量避免将 Pod 调度到具有该污点的 Node 上

  • NoExecute

    K8s 不会将 pod 调度到具有该污点的 Node 上,并且会将该 Node 上已经存在的 Pod 进行驱逐

具体使用方法如下:

kubectl taint nodes node1 key1=value1:effect
对三个 master 节点设置 taint:
# 任意 master 节点操作
kubectl taint nodes t-k8sm-001 node-role.kubernetes.io/k8s-master=:NoSchedule
kubectl taint nodes t-k8sm-002 node-role.kubernetes.io/k8s-master=:NoSchedule
kubectl taint nodes t-k8sm-003 node-role.kubernetes.io/k8s-master=:NoSchedule


五、部署 kube-proxy

1、环境准备

1)分发配置文件

1. 分发文件: kube-proxy.conf
# 终端机操作
ansible t-k8sM-1.18 -m copy -a "src=/data/softwares/k8s1.18/kube-proxy.conf dest=/data/application/kubernetes/cfg/ owner=root group=root"

文件内容:

KUBE_PROXY_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/data/application/kubernetes/logs \
--config=/data/application/kubernetes/cfg/kube-proxy-config.yml"
2. 分发文件: kube-proxy-config.yml
# 终端机操作
ansible t-k8sM-1.18 -m copy -a "src=/data/softwares/k8s1.18/kube-proxy-config.yml dest=/data/application/kubernetes/cfg/ owner=root group=root"

文件内容:

kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
metricsBindAddress: 0.0.0.0:10249
clientConnection:
  kubeconfig: /data/application/kubernetes/cfg/kube-proxy.kubeconfig
hostnameOverride: t-k8sN-001
clusterCIDR: 10.0.0.0/16
mode: ipvs
ipvs:
  scheduler: "rr"
iptables:
  masqueradeAll: true
3. 修改 kube-proxy-config.yml

修改内容如下:

  • hostnameOverride:

    修改对应的主机名


2、启动服务

1)安装环境依赖

conntrack:

conntrack 是 Linux 下的一个连接追踪命令,K8s 需要通过它来追踪 cni 网络连接,如果没有安装会有类似如下报错:

7月 12 17:29:42 t-k8sM-001 systemd[1]: Started Kubernetes Proxy.
7月 12 17:29:42 t-k8sM-001 kube-proxy[12358]: E0712 17:29:42.476980   12358 proxier.go:1622] Failed to delete stale service IP 10.0.0.2 connections, error: error deleting connection tracking state for UDP service IP: 10.0.0.2, error: error looking for path of conntrack: exec: "conntrack": executable file not found in $PATH
# 终端机操作
ansible t-k8sM-1.18 -m shell -a "yum -y install conntrack"

2)分发启动文件

1. 分发文件: kube-proxy.service
# 终端机操作
ansible t-k8sM-1.18 -m copy -a "src=/data/softwares/k8s1.18/kube-proxy.service dest=/usr/lib/systemd/system/ owner=root group=root mode=0644"

文件内容:

[Unit]
Description=Kubernetes Proxy
After=network.target

[Service]
EnvironmentFile=/data/application/kubernetes/cfg/kube-proxy.conf
ExecStart=/data/application/kubernetes/bin/kube-proxy $KUBE_PROXY_OPTS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

3)启动服务

1. 重载自定义配置
# 终端机操作
ansible t-k8sM-1.18 -m shell -a "systemctl daemon-reload"
2. 加入开机自启
# 终端机操作
ansible t-k8sM-1.18 -m shell -a "systemctl enable kube-proxy.service"
3. 启动服务
# 终端机操作
ansible t-k8sM-1.18 -m shell -a "systemctl start kube-proxy.service"


六、最终验证

**关键点总结: **

  • 对于新部署的集群(kubeadm部署除外),建议在部署集群时就对 master 节点做此处理,而不要等集群部署完成后再二次处理
  • master 节点的 kubelet 需要设置 --node-labels=node.kubernetes.io/k8s-master=true,以此对调度器声明身份
  • 为确保 master 节点的稳定性,建议通过污点 node-role.kubernetes.io/k8s-master=:NoSchedule,避免 Pod 的调度
  • master 节点的 kubelet 和 kube-proxy 直接与本地的 kube-apiserver 通信即可,无需使用 nginx proxy

1、验证

1)验证 apiservice

# 任意 master 节点执行
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml
1. 未调整前的异常情况

在未进行调整之前,返回值会包含类似一下的内容,很明显的可以看出连接异常:

image-20210712175722083.png
2. 调整 kubelet 和 kube-proxy 后

在进行上文的调整后,正常情况下的返回值应该如下,这种情况下 apiserver 可以正常的访问到 metrics-server:

image-20210712175813905.png

2)验证 kubectl top

1. 查看 node 指标
# 任意 master 节点执行
kubectl top nodes

返回值类似下面:

image-20210712180433815.png
2. 查看 pod 指标
# 任意 master 节点执行
kubectl top pod -A

返回值类似下面:

image-20210712180416527.png

你可能感兴趣的:(K8s api server 无法连接 metrics server 问题解决方案)