快乐的水煮肉 · 在卢森堡度周末疫情下也要释放心情-浪迹天涯的 ...· 3 月前 · |
闷骚的咖啡 · 民族管弦乐团这些乐器你都认得吗?_中国· 1 年前 · |
温柔的打火机 · 前iG辅助Southwind退役:很早就想过 ...· 1 年前 · |
苦恼的啄木鸟 · 二级建造师证书到底有什么用? - 知乎· 1 年前 · |
买醉的手链 · 为什么说汉字和其他文字走上了不同的路|汉字| ...· 1 年前 · |
Pod
状态显示
CrashLoopBackOff
$ kubectl get pods |
查看
Pod
详细信息
$ kubectl describe pod test-centos7-7cc5dc6987-jz486 |
结果显示,
Reason
为
BackOff
,
Message
显示
Back-off restarting failed container
可能原因 :
Back-off restarting failed container
的原因,通常是因为,容器内 PID 为 1 的进程退出导致(通常用户在构建镜像执行
CMD
时,启动的程序,均是 PID 为1)
[1]
容器进程退出(命令执行结束或者进程异常结束),则容器生命周期结束。kubernetes 控制器检查到容器退出,会持续重启容器。针对此种情况,需要检查镜像,是否不存在常驻进程,或者常驻进程异常。
针对此种情况,可以单独使用
docker
客户端部署镜像,查看镜像的运行情况,如果部署后,容器中的进程立马结束或退出,则容器也会随之结束。
定位中也可以使用
kubectl describe pod
命令检查 Pod 的退出状态码。Kubernetes 中的 Pod ExitCode 状态码是容器退出时返回的退出状态码,这个状态码通常用来指示容器的执行结果,以便 Kubernetes 和相关工具可以根据它来采取后续的操作。以下是一些常见的 ExitCode 状态码说明:
ExitCode 0
: 这表示容器正常退出,没有错误。这通常是期望的结果。
ExitCode 1
: 通常表示容器以非正常方式退出,可能是由于应用程序内部错误或异常导致的。通常是容器中 pid 为 1 的进程错误而失败
ExitCode 非零
: 任何非零的状态码都表示容器退出时发生了错误。ExitCode 的具体值通常是自定义的,容器内的应用程序可以根据需要返回不同的状态码来表示不同的错误情况。你需要查看容器内应用程序的文档或日志来了解具体的含义。
ExitCode 137
: 通常表示容器因为被操作系统终止(例如,
OOM-killer
)而非正常退出。这可能是由于内存不足等资源问题导致的。
ExitCode 139
: 通常表示容器因为接收到了一个信号而非正常退出。这个信号通常是
SIGSEGV
(段错误),表示应用程序试图访问无效的内存。
ExitCode 143
: 通常表示容器因为接收到了
SIGTERM
信号而正常退出。这是 Kubernetes 在删除 Pod 时发送的信号,容器应该在接收到该信号后做一些清理工作然后退出。
ExitCode 130
: 通常表示容器因为接收到了
SIGINT
信号而正常退出。这是当用户在命令行中按下
Ctrl+C
时发送的信号。
ExitCode 255
:通常表示未知错误,或者容器无法启动。这个状态码通常是容器运行时的问题,比如容器镜像不存在或者启动命令有问题。
Harbor 证书过期后,更新了证书,
更新证书后相关问题参考
,Kubernetes 中更新 Pod 失败,
节点上使用了
containerd
做为 CRI
,具体报错信息如下
# kubectl get pods -n ops |
在节点
k8s-worker1
上使用
docker
及
curl
测试访问 Harbor 域名均正常。因此判断问题出现在
containerd
未识别到证书导致。
对于 Kubernetes 使用
containerd
作为容器运行时,如果需要配置额外的证书(如信任自签名的 Harbor 仓库证书),可能需要修改或创建
containerd
的配置文件,通常为
/etc/containerd/config.toml
。在配置文件中添加证书配置,
在这个示例中,
ca_file
应该指向 Harbor 证书。确保该路径正确且证书格式为 PEM
。
[plugins."io.containerd.grpc.v1.cri".registry] |
重启
containerd
服务,然后重新部署 Pod
systemctl restart containerd |
Pod
状态显示
InvalidImageName
kubectl get pods -n cs |
可能原因 :
镜像的 url 地址中,以
http://
或
https://
开头。配置中镜像的 url 地址中无需指定协议(
http://
或
https://
)
查看 Pod 状态,显示 Error
$ kubectl get pods |
检查 Pod 的具体信息
$ kubectl describe pod front-7df8ccc4c7-xhp6s |
其中包含异常的关键信息:
Status: Failed
,
Reason: Evicted
,具体原因为
The node was low on resource: ephemeral-storage
检查节点上的 Kuberlet 日志,搜索关键字
evicte
或者
disk
,也可以看到系统上文件系统空间使用率超过了阈值
$ journalctl -u kubelet | grep -i -e disk -e evict |
可能原因 :
根据以上信息,可知 Pod 异常是因为
The node was low on resource: ephemeral-storage
,表示
临时存储资源
不足导致节点处于
Tainted
,其上的 Pod 被驱逐(
Evicted
)
针对此种情况,如果某 Pod 的临时存储用量超出了你所允许的范围,kubelet 会向其发出逐出(
eviction
)信号,触发该 Pod 被逐出所在节点。
如果用于可写入容器镜像层、节点层面日志或者
emptyDir
卷的文件系统中可用空间太少, 节点会为自身设置本地存储不足的污点(
Tainted
)标签。 这一污点会触发对那些无法容忍该污点的 Pod 的逐出操作。
解决方法 :
增加磁盘空间
调整
kubelet
的
nodefs.available
的 threshold 值
修改节点上的
kubelet
的启动配置文件
/usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
,添加以下启动参数,主要为定义环境变量
KUBELET_EVICT_NODEFS_THRESHOLD_ARGS
,并将其添加到启动参数中
Environment="KUBELET_EVICT_NODEFS_THRESHOLD_ARGS=--eviction-hard=nodefs.available<5%" |
修改之后重启
kubelet
服务,并通过日志查看
nodefs.available
的新值是否生效
$ systemctl daemon-reload |
日志中看到
Signal:nodefs.available Operator:LessThan Value:{Quantity:<nil> Percentage:0.05
,表明更改生效。
[2]
$ kubectl get pods |
查看 Pod 的详细描述信息
$ kubectl describe pod admin-cbb479556-j9qg2 |
根据
Events
中输出的信息,
MountVolume.SetUp failed for volume "uat-nfs-pv" : mount failed: exit status 32
,显示挂载卷失败,输出中包含了挂载卷时使用的命令和参数(
mount -t nfs 34.230.1.1:/data/NFSDataHome /var/lib/kubelet/pods/9d9a4807-706c-4369-b8be-b5727ee6aa8f/volumes/kubernetes.io~nfs/uat-nfs-pv
)及命令失败后的返回结果(
mount.nfs: Connection timed out
)
根据
Events
中的信息,查看配置,发现此卷为 NFS 类型的 PV,根据报错排查,此例原因为 NFS 的服务器地址填写错误,更新 PV 配置中的 NFS Server 的地址后,Pod 正常启动。
更新
DaemonSet
类型的
node_exporter
,其中一个节点上的 Pod 未创建成功,状态一直保持在
ContainerCreating
,检查 Pod 的详细描述信息
$ kubectl get pods -n prometheus -o wide |
错误信息显示为
unable to ensure pod container exists: failed to create container for [kubepods besteffort pode526f19a-57d6-417c-ba5a-fb0f232d31c6] : dbus: connection closed by user
查看
kubelet
日志,显示同样的日志
$ journalctl -u kubelet |
根据以上日志信息,问题原因为
kubelet
和系统服务
dbus
通信异常,可以
通过重启
kubelet
服务
的方法解决此问题。
集群中创建的 POD 状态一直处于
ContainerCreating
,检查 Pod 详细信息
# kubectl describe pod -n cattle-system cattle-cluster-agent-7d766b5476-hsq45 |
关键信息
failed to set bridge addr: "cni0" already has an IP address different from 10.244.2.1/24
。
检查节点上的 IP 信息,发现
flannel.1
网段和
cni0
网段不一致。可能因为
flannel
读取的配置错误,
重启节点后恢复
。
# ip add |
新部署的 Pod 状态一直处于
PodInitializing
# kubectl get pods |
登陆到 Pod 所在节点,检查
kubelet
服务日志
# journalctl -f -u kubelet |
从日志中可以看到关键错误日志信息:
start failed in pod ops-admin-5656d7bb64-kvvmx_ops
,
exited with 137: : Exec lifecycle hook ([/bin/sh -c php /home/www/artisan command:apollo.sync >> apollo.log;php /home/www/artisan queue:restart]) for Container "php" in Pod "ops-admin-5656d7bb64-kvvmx_ops(a44af28c-3a39-439b-97c1-7e78b03ccd91)" failed - error: command '/bin/sh -c php /home/www/artisan command:apollo.sync >> apollo.log;php /home/www/artisan queue:restart' exited with 137
由此可以判断 Pod 无法正常启动的原因为 Pod 中的容器中的进程执行错误导致。
集群中某个节点上面的 Pod 状态显示为
Init
或者
PodInitializing
,其他节点正常,登陆异常节点,检查
kubelet
服务日志
# journalctl -f -u kubelet |
关键日志
"Failed to update QoS cgroup configuration" err="dbus: connection closed by user"
,根据此信息,可能是因为要与 DBus 服务通信更新容器的 QoS cgroup 配置失败。具体来说,
kubelet
在尝试更新容器的 QoS cgroup 配置时遇到了
dbus: connection closed by user
错误,并且无法正确创建容器。
这种情况可能是由于系统上的 DBus 服务异常导致的
。本示例中在先后重启了
dbus
服务和
kubelet
服务后问题恢复。
systemctl restart dbus |
# kubectl get nodes |
集群中 coredns 状态处于 Pending。通常, Pod 处于 Pending 状态意味着 Kubernetes 调度程序未能将 Pod 分配给任何节点 。查询 Pod 状态如下
# kubectl get pods -n kube-system -o wide |
看到除了
kube-controller-manager
、
kube-scheduler
、
etcd
、
kube-apiserver
外,其他 Pod 状态都为
Pending
,并且
Node
列显示为
<none>
,说明集群未将新创建的 Pod 调度到某个节点上。以
coredns-6d4b75cb6d-7np4c
为例查看其描述信息,
Events
中未包含任何事件信息。
# kubectl describe pod -n kube-system coredns-6d4b75cb6d-7np4c |
鉴于以上信息,怀疑这可能是集群级别的问题。Pod 调度主要由
kube-scheduler
进行,因此首先查看
kube-scheduler
组件日志
# kubectl logs -n kube-system kube-scheduler-fm-k8s-c1-master1 | tail -n 20 |
根据日志内容,显示
kube-scheduler
无法获取到集群资源,原因为
Unauthorized
。
一般
Unauthorized
常见原因可能是因为 RBAC 或者证书。先检查 RBAC,
kube-scheduler
默认使用用户
system:kube-scheduler
,下面查看用户绑定的 Role 及权限
# kubectl describe clusterrole system:kube-scheduler |
查看 RBAC 权限,并无异常。检查集群证书,集群证书已经更新过,显示正常。
# kubeadm certs check-expiration |
查看下
kube-apiserver
日志信息,从日志中看到连接
etcd
(
127.0.0.1:2379
)异常,主要为证书问题,并且
kube-apiserver
日志中显示证书并未更新。
clientconn.go:1331] [core] grpc: addrConn.createTransport failed to connect to {127.0.0.1:2379 127.0.0.1 <nil> 0 <nil>}. Err: connection error: desc = "transport: authentication handshake failed: remote error: tls: internal error". Reconnecting... |
检查
etcd
日志,日志中显示找不到证书:
open /etc/kubernetes/pki/etcd/peer.crt: no such file or directory
{"level":"warn","ts":"2023-12-08T07:54:23.780Z","caller":"embed/config_logging.go:169","msg":"rejected connection","remote-addr":"172.31.21.3:30426","server-name":"","error":"open /etc/kubernetes/pki/etcd/peer.crt: no such file or directory"} |
堆叠(Stack)高可用模式下
etcd
组件启动时会挂载 Master 节点的
/etc/kubernetes/pki/etcd/
目录作为自己的证书文件,具体配置可以查看静态 Pod 的配置
/etc/kubernetes/manifests/
apiVersion: v1 |
登陆到
etcd
的容器中,检查目录
/etc/kubernetes/pki/etcd
,发现下面为空,没有文件。原因未找到,重启系统后挂载正常。
同一个节点上的
Pod
之间网络不通
排查思路 :
$ sysctl -a | grep net.ipv4.ip_forward |
iptables
是否禁止转发,
iptables
防火墙配置参考
iptables
影响,开关闭
iptables
再进行测试,如果关闭防火墙后可以通信,可以确定是防火墙规则导致,需要检查防火墙规则。
netshoot
容器
进行抓包定位,
查看 Pod 抓包结果
可以看到源 IP 为 Pod 地址,目标为服务 IP 的
6603/tcp
的请求发送后,未收到 TCP 连接建立的响应。查看 节点
cni0
网卡 的抓包
可以看到源 IP 为 Pod 地址,目标为服务 IP 的
6603/tcp
的请求发送后,未收到 TCP 连接建立的响应。查看节点出口网卡
eth0
的抓包。
此处看到的源 IP 依然是 Pod 的 IP 地址,此处存在问题 。在云主机的场景中,如果数据包以这种结构发送出去,数据包到了 Internet 网关将拒绝它,因为网关 NAT(将 VM 的 IP 转换为公网 IP) 只了解连接到 VM 的 IP 地址。
正常情况下,Pod 的流量到节点的出口网卡之前,是应该经过
iptables
执行源 NAT -
更改数据包源,使数据包看起来来自 VM 而不是 Pod
。有了正确的源 IP,数据包才可以离开 VM 进入 Internet
此种情况下,数据包可以从节点的出口网卡发送出去,但是到了 Internet 网关将会被丢弃,因此目标服务无法接收到请求,查看目标服务器上的抓包,确实未收到来自此 Pod 的请求。
此处的
源 NAT
是由
iptables
负责执行,流入节点出口网卡的数据包未被正确的
源 NAT
,有可能是因为
kube-proxy
维护的网络规则错误,或者因为
iptables
规则配置错误。可以通过重启
kube-proxy
(由服务
kubelet
管理)和
iptables
服务尝试恢复。
systemctl restart kubelet |
本示例中,重启这 2 个服务后,Pod 恢复正常。
集群中的 Pod 出现连接集群之外的数据库服务超时,且出现频率较高
集群中有 1 个 master 节点, 2 个 work 节点,节点状态均正常,master 无法 ping worker1 上面的 Pod,可以 ping 通 worker2 节点上面的 Pod
$ kubectl get nodes -A -o wide |
由此可判断问题大概率出现在 worker1 节点,首先检查 worker1 节点上的
flannel
容器是否正常
$ kubectl get pods -A -o wide |
看到 worker1 节点上的 flannel 容器运行正常。在 worker1 节点上检查
flannel
进程及端口信息
$ netstat -anutp |
检查发现
flanneld
进程存在,但是端口未启动,检查
flannel
容器日志输出
$ docker ps -a | grep flannel | grep -v "Exited" |
关键错误信息
failed to add vxlanRoute
,
network is down
,
参考案例
,重启服务器。恢复正常。
集群相关信息如下
$ kubectl get svc -A |
在容器中测试 dns 相关信息,访问外部 IP 和 Kubernetes API Server 的 Service 地址均正常
$ ping 8.8.8.8 |
容器中的 dns 配置为
kube-dns
的 Service 的 IP,测试其端口,显示
Connection refused
。测试解析集群内部域名,结果无法解析。
$ cat /etc/resolv.conf |
通过以上步骤,大概可以确定,Pod 的网络正常,应该是
kube-dns
出问题,导致 Pod 无法解析域名。
Service 是通过 Endpoint 和后端的具体的 Pod 关联起来向外提供服务,首先检查
kube-dns
的 Service 对应的 Endpoint,看是否正常。
$ kubectl get ep -A |
检查发现,
kube-dns
对应的 ENDPOINTS 列表为空。删除
coredns
容器,重新创建。再次检查后,发现
kube-dns
的 Service 对应的 Endpoint 恢复正常。
$ kubectl delete pod -n kube-system coredns-565d847f94-bzr62 coredns-565d847f94-vmddh |
在 Pod 中重新测试解析,结果正常
$ curl -v 10.96.0.10:53 |
故障排查:Kubernetes 中 Pod 无法正常解析域名
$ kubectl get nodes |
查看节点详细信息
$ kubectl describe node k8s-work1 |
集群因为此原因(
PLEG is not healthy: pleg was last seen active ***h**m***s ago;
)状态变为
NotReady
,通常是因为节点超负载。
检查集群中的 Pod 分布情况时,发现某一节点上几乎所有的 Pod 都被调度去了其他节点,当前检查时此节点的状态已经是
Ready
,针对此情况进行分析。
确定问题发生的大概时间段
根据 Pod 在其他节点上面被启动的时间,可以大概确定节点异常的时间,根据此时间段可以缩小排查的时间范围。此示例中问题发生的时间大概在
Nov 25 04:49:00
前后。
检查
kubelet
日志
根据已经推断出的时间段,在
问题节点
上,检查
kubelet
日志
$ journalctl -u kubelet --since "2022-11-25 4:40" | grep -v -e "failed to get fsstats" -e "invalid bearer token" | more |
从以上日志中,可以看到关键的日志信息:
"Skipping pod synchronization" err="container runtime is down"
setters.go:532] "Node became not ready"
,
Reason:KubeletNotReady Message:[container runtime is down, container runtime not ready: RuntimeReady=false reason:DockerDaemonNotReady message:docker: failed to get docker version: operation timeout: context deadline exceeded]}
从以上日志信息可以看出,节点状态变为了
not ready
,原因为
container runtime is down, container runtime not ready
,本示例中
container runtime
为
docker
检查 docker 服务日志
根据上面的日志时间,检查 docker 服务的日志
journalctl -u docker --since "2022-11-25 04:0" | more |
根据日志可以看到关键日志
write unix /var/run/docker.sock->@: write: broken pipe
检查 messages 日志
查看对应时间段的系统日志
Nov 25 04:49:00 k8s-work2 kubelet: E1125 04:49:00.153089 17604 remote_runtime.go:356] "ListPodSandbox with filter from runtime service failed" err="rpc error: code = Unknown desc = operation timeout: context deadline exceeded" filter="nil" |
根据
kubelet
服务日志,节点
Not Ready
的原因为
docker down
,根据 docker 服务日志,docker 存在异常,但是此时执行
docker
相关命令,未发现异常。此问题多次出现,
docker engine
版本为
19.03.15-3
,之后尝试将
docker engine
版本升级为最新版本
20.10.9
,问题未在出现。
docker engine
升级参考
新增节点后,节点状态为
NotReady
$ kubectl get nodes |
Master 上查看节点的描述信息
$ kubectl describe node k8s-api-work5 |
看到异常原因为
container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized
在
work5
节点上查看
kubelet
日志
$ journalctl -u kubelet -f |
在 Master 节点上查看异常节点上的
kube-flannel
POD 状态正常
$ kubectl get pods -o wide -n kube-system |
接着检查新增节点上提供
flannel
组件的安装包,及相关目录中的文件是否存在异常
$ rpm -qa | grep kubernetes |
比对其他已存在的正常节点上的
kubernetes-cni
信息,发现其他节点上的
kubernetes-cni
版本为
kubernetes-cni-0.8.7-0
,怀疑为版本问题导致,卸载问题节点上的
kubernetes-cni-1.2.0-0
,重新安装
kubernetes-cni-0.8.7-0
。卸载
kubernetes-cni
会导致之前安装的
kubeadm
和
kubelet
被卸载,也需要重新安装。
$ yum remove kubernetes-cni-1.2.0-0 |
安装
kubernetes-cni-0.8.7-0
版本后,再次查看节点状态,变为
Ready
$ kubectl get nodes |
$ kubectl get nodes -o wide |
检查发现节点上没有 CNI 配置文件
/etc/cni/net.d/10-flannel.conflist
,拷贝正常节点上的配置到异常节点后,状态恢复正常。
api server 启动失败,执行
kubectl
命令输出:
$ kubectl get nodes |
检查 Api Server 监听的端口 6443 ,显示端口未启动。
检查 Api Server 对应的容器状态
$ docker ps -a | grep api |
容器状态为
Exited
,检查容器日志
$ docker logs 81688b9cbe45 |
日志显示
err="open /etc/kubernetes/pki/apiserver.crt: no such file or directory"
,检查文件
/etc/kubernetes/pki/apiserver.crt
$ ls /etc/kubernetes/pki/apiserver.crt |
解决方法参考
cp /k8s/backup/pki/apiserver.key /etc/kubernetes/pki/ |
kubelet
后检查 Api Server,发现服务正常启动
systemctl restart kubelet |
apiserver.key
,
apiserver.crt
证书文件,可通过以下命令重新生成证书文件,
生成原理参考
$ kubeadm init phase certs apiserver \ |
kube-apiserver
无法正常启动,检查
kube-apiserver
相关容器日志
# kubectl get nodes |
查看到日志中的关键错误信息:
"command failed" err="context deadline exceeded"
,
[core] grpc: addrConn.createTransport failed to connect to {127.0.0.1:2379 127.0.0.1 <nil> 0 <nil>}. Err: connection error: desc = "transport: authentication handshake failed: context deadline exceeded". Reconnecting...
,由此可知,主要问题在于连接
etcd
组件异常,因
transport: authentication handshake failed
无法和
etcd
建立连接,
kube-apiserver
连接
etcd
的认证依赖于证书,因此去检查集群证书,发现证书过期,相关操作参考以下命令及输出
# cd /etc/kubernetes/ |
参考以下命令,备份及更新集群证书,并重启
kubelet
后,
kube-apiserver
恢复正常
# tar -cf /etc/kubernetes/kubernetes.20231115.tar /etc/kubernetes/kubernetes |
$ journalctl -f -u kubelet |
问题原因
为
版本不匹配
。集群版本为
1.21.2
,检查问题节点上的组件版本信息,
kubelet
变为了
kubelet-1.27.3
,可能是升级了
kubelet
软件包版本导致。
$ rpm -qa | grep kube |
查看正常节点上的组件版本
$ rpm -qa | grep kube |
解决方法 为恢复问题节点上的组件版本和集群版本一致。
yumdownloader kubernetes-cni-0.8.7-0 kubectl-1.21.2-0 kubeadm-1.21.2-0 kubelet-1.21.2-0 |
yum remove kubelet-1.27.3-0 kubectl-1.25.2-0.x86_64 kubernetes-cni-1.2.0-0.x86_64 |
systemctl daemon-reload |
$ systemctl status kubelet |
检查
kubelet
服务日志
$ journalctl -f -u kubelet |
根据日志信息可知,
kubelet
服务启动失败是因为
kubelet
使用的
cgroup
驱动是
systemd
,而 docker 服务使用的是
cgroupfs
。二者不一致导致
kubelet
无法启动。
修改
docker
服务使用的
cgroup
驱动为
systemd
$ cat <<EOF | sudo tee /etc/docker/daemon.json |
重启
docker
服务后,
kubelet
服务运行正常。
systemctl restart docker |
# journalctl -f -u kubelet |
从日志可知,是因为
kubelet
和
docker
使用的 cgroup 驱动不一致导致。
kubelet
使用了
cgroupfs
,而
docker
使用了
systemd
参考以下步骤修改
kubelet
使用
systemd
驱动
kubelet
配置
/var/lib/kubelet/config.yaml
中的
cgroupDriver
cgroupDriver: systemd |
kubelet
服务
172.31.26.116 k8s-master1 kube-api-svr-c1.mydomain.com |
集群中的所有节点都写入了以上内容,实现了节点通过
/etc/hosts
中的配置解析
kube-apiserver
的域名(
kube-api-svr-c1.mydomain.com
)。
正常情况下,所有节点都会将
kube-apiserver
解析为
172.31.26.116 kube-api-svr-c1.mydomain.com
。
本次故障中,
k8s-master1
节点因为 CPU 和内存满载,
k8s-master1
异常,无法提供
kube-apiserver
服务。同时,集群中所有的应用都响应异常 :
503 Service Temporarily Unavailable
理论上,本环境为
高可用
的Kubernetes 集群,一个 master 节点异常,不会影响集群提供正常的功能,但是此次只是因为
k8s-master1
这一个 master 节点异常,就导致了整个集群无法提供正常的功能。
复现问题分析,发现
k8s-master1
异常后,在其他主节点上使用
kubectl
命令,无法使用,原因为
根据节点
/etc/hosts
配置,
kube-apiserver
解析到了异常的
k8s-master1
节点
,修改
k8s-master2
上面的
/etc/hosts
配置,将
172.31.19.164 k8s-master2 kube-api-svr-c1.mydomain.com
放在第一行,以使
kube-apiserver
域名解析到
k8s-master2
,
kubectl
命令连接到
k8s-master2
即可正常查看集群状态
$ kubectl get nodes |
可以看到,集群中除了
k8s-master2
,其他所有节点都异常,原因为
除了
k8s-master2
其他节点都将
apiserver
解析到了异常的
k8s-master1
节点
。修改所有节点的
/etc/hosts
文件,将
kube-api-svr-c1.mydomain.com
解析到
k8s-master1
之外的 master 节点,集群恢复正常。
本次故障的根本原因为
本环境中未实现 Kubernetes 的高可用,虽然已经部署了高可用环境,但是因为
apiserver
的域名未实现高可用(负载均衡),导致
apiserver
请求全部到了异常节点
。要从根本上解决此问题,需要为
apiserver
的请求域名部署负载均衡实现真正的高可用。
使用
/etc/hosts
将域名解析到多个 IP 不能实现高可用
。
deployment 部署成功后,检查对应的 Pod,发现未创建对应的 Pod。查看
deployment
描述信息如下,
Events
内容为空。
# kubectl describe deployment ops-admin -n ops |
检查
replicaset
信息,未发现任何的相关
replicaset
资源
# kubectl describe replicaset -l env=prod,project=ops-admin -n ops |
检查事件列表,内容为空
# kubectl get events -n ops |
继续检查控制平面组件的状态,看到
controller-manager
和
scheduler
状态异常(此异常可能是因为配置原因,具体见
controller-manager 和 scheduler 组件状态 Unhealthy
)
# kubectl get componentstatuses |
接着查看
controller-manager
和
scheduler
对应的 Pod 的日志
# kubectl logs -n kube-system -l component=kube-controller-manager |
日志显示存在鉴权问题(
Unauthorized
),本示例中此问题主要是由集群证书过期导致。
检查集群组件状态,显示
controller-manager
和
scheduler
组件状态
Unhealthy
。集群功能正常
# kubectl get componentstatuses |
出现此情况是因为
/etc/kubernetes/manifests/kube-controller-manager.yaml
和
/etc/kubernetes/manifests/kube-scheduler.yaml
设置的默认端口是
0
,修改此配置文件,注释
- --port=0
即可(修改此配置文件后,系统会自动加载配置,无需重启),等待大概一分钟后重新查看,状态正常
# kubectl get componentstatuses |
Kubernetes 集群中使用了多种证书来保证各种组件间的安全通信。以下是 Kubernetes 中使用的主要证书:
证书默认存放路径
/etc/kubernetes/pki
,以下文件相对路径基于此默认存放路径
kubernetes CA 证书
Kubernetes CA 证书用于签发集群中的其他证书,例如
kube-apiserver
服务器证书、
kubelet
证书、
controller-manager
证书等。
ca.crt
CA 的公钥证书。
ca.key
CA 的私钥。
在使用
kubeadm
初始化 Kubernetes 集群时,CA 证书通常会自动生成。
kube-apiserver 组件证书
加密 Kubernetes API 服务器的 HTTPS 接口。
apiserver.crt
apiserver.key
证书的 SANs 中包含了 Kubernetes API 服务器的 IP 地址、DNS 名称等。要更新 SANs 的值,使用命令
kubeadm init phase certs apiserver
,
参考示例
要单独更新
kube-apiserver
的证书,参考命令
kubeadm certs renew
Kube Proxy、Controller Manager 和 Scheduler 默认不使用 TLS 和 API Server 进行身份验证,而是使用 Kubernetes Service Account。
Kubelet 组件证书
加密 Kubelet 与 API 服务器的通信。kubelet 使用这些证书来安全地与 Kubernetes API 服务器通信。这包括节点状态的报告、Pod 的创建和管理等。当 kubelet 首次加入集群时,它会使用这些证书进行 TLS 认证。API 服务器通过这些证书验证节点的身份,并基于此授予相应的权限。
Kubernetes 支持自动证书轮换,这意味着 kubelet 会在证书接近过期时自动请求新的证书。这个过程是自动的,确保了长期运行的集群保持安全。
通常为节点的
/var/lib/kubelet/pki/kubelet-client-current.pem
和对应的 Key 文件。
Service Account Keys
在初始化 Kubernetes 集群时,这些密钥通常由
kubeadm
或相似的工具自动生成。
sa.key
用于签发新的 Service Account JWT Tokens。通常由集群管理员或自动化工具(如
kubeadm
)安全地生成和管理。
sa.pub
(
非 TLS 证书,而是密钥对
)。 由 Kubernetes API 服务器用来验证 Token 的合法性。与私钥配对使用。
在 Kubernetes 中,Service Account Keys 是一对公钥和私钥,用于验证和签发 Service Account Tokens。这些令牌允许 Pod 以特定的 Service Account 身份与 Kubernetes API 服务器进行认证和授权。
apiserver-kubelet-client
这对证书和密钥用于 API 服务器请求集群中每个节点上的
kubelet
时的安全通信。API 服务器使用这些证书与
kubelet
通信,进行诸如启动 Pod、获取节点状态等操作。
apiserver-kubelet-client.crt
apiserver-kubelet-client.key
etcd CA 证书
Etcd CA 证书用于签发 etcd 集群相关的证书,如
etcd
服务器证书、
etcd
客户端证书、Peer 实体证书等。
etcd/ca.crt
etcd/ca.key
apiserver-etcd-client
这对证书和密钥用于 Kubernetes API 服务器请求
etcd
数据库时的加密通信
apiserver-etcd-client.crt
apiserver-etcd-client.key
# sudo kubeadm init phase certs all |
原因为系统上存在多个可用的 CRI,需要手动配置使用哪一个。这通常是在
kubeadm
配置文件中设置
criSocket
字段来完成。如果集群正在使用
containerd
作为容器运行时,参考以下配置解决
kubeadm
配置文件。创建一个
kubeadm
配置文件(如果还没有的话,如
kubeadm-config.yaml
),并在其中指定
criSocket
字段。例如,如果使用
containerd
,则文件内容如下:
apiVersion: kubeadm.k8s.io/v1beta2 |
kubeadm
命令
# sudo kubeadm init phase certs all --config=./kubeadm-config.yaml |
使用以下方式更新
kube-apiserver
的证书时报错
# kubeadm init phase certs apiserver --apiserver-advertise-address --apiserver-cert-extra-sans kubernetes --apiserver-cert-extra-sans kubernetes.default --apiserver-cert-extra-sans kubernetes.default.svc --config=/home/username/kubeadm-config.yaml |
根据报错提示,不能同时使用
--config
和
apiserver-advertise-address apiserver-cert-extra-sans
。
本示例中使用
--config
是用来解决
Found multiple CRI endpoints on the host
。要简单解决,只需要确保主机上只有
一个可用的 CRI 即可
。
如果在
kubeadm init
命令中使用了
--config
选项指定配置文件,那么所有的选项配置都要写入此配置中。
相关配置项参考
不同的 Kubernetes 版本对此配置版本要求不一样,具体要参考官网
etcd
堆叠(Stack)架构的 Kubernetes 集群无法访问,获取不到集群状态
# kubectl get nodes |
通过 CRI(以
docker
为例)查看
kube-apiserver
容器状态,显示为
Exited
。
# docker ps -a |
查看
kube-apiserver
容器日志信息。错误日志中有关键错误信息
failed to connect to {127.0.0.1:2379 127.0.0.1 <nil> 0 <nil>}. Err: connection error: desc = "transport: authentication handshake failed: remote error: tls: internal error". Reconnecting...
,这显示了
kube-apiserver
因为 TLS 证书原因无法连接到
etcd
。
# docker logs b0aa1a2e24ee |
根据日志指向,首先来确定集群中
etcd
的状态。
etcd
集群暴露了健康状态检查的 Endpoint,可以通过请求此接口检查
etcd
集群的状态。检查结果显示集群状态不正常,原因为未选举出 Leader
# curl 127.0.0.1:2381/health |
根据当前的排查结果,可以确定
etcd
集群异常,
etcd
集群是 Kubernetes 集群的配置核心,其异常会导致整个 Kubernetes 集群不可用。为了确定
etcd
集群异常原因,检查
etcd
集群中节点的日志,以查找有用信息
# docker logs etcd |
检查
etcd
节点容器日志,日志中有报错显示证书认证问题:
x509: certificate signed by unknown authority (possibly because of \"crypto/rsa: verification error\" while trying to verify candidate authority certificate \"etcd-ca\")"
,根据错误提示,
可能是
etcd
集群使用的证书存在问题,具体错误说明
etcd
节点之间在尝试相互验证对方证书时遇到了问题。这通常发生在节点使用不同的 CA 证书,或者配置不正确时。
。
通常情况下,在启用了
--client-cert-auth=true
和
--peer-client-cert-auth=true
的
etcd
集群中,
etcd
对等节点间需要使用 HTTPS 证书进行加密通信,对等实体间需要验证客户端证书,这个过程中需要相同的 CA 证书进行 CA 机构的签名校验。根据日志提示,问题应集中在
etcd
集群中的证书上。
根据以上日志及思路引导,首先检查
etcd
各个节点的 CA 证书是否一致,结果发现 3 个
etcd
节点上面的 CA 证书不同
[root@k8s-master1 ~]# md5sum /etc/kubernetes/pki/etcd/ca.crt |
在一个正常配置的
etcd
集群中,所有节点应该使用由同一个根 CA 签发的证书,以便它们能够相互验证和信任。这是因为节点之间的通信(包括 Raft 协议的通信和快照传输)使用 TLS 加密,而且节点需要能够验证彼此的证书。如果每个节点上的 CA 证书不同,它们将无法验证其他节点的证书,从而导致通信失败。
为了解决这个
etcd
集群中各个节点使用的 CA 证书不一致的问题,参考以下步骤
/etc/kubernetes/pki/
。
在所有的 Master 节点上操作
tar -czf /ops/kubernetes_backup/pki.tar /etc/kubernetes/pki/ |
etcd
节点上的所有证书。
在所有的 Master 节点上操作
rm -rf /etc/kubernetes/pki/etcd/* |
kubeadm
重新生成
etcd-ca
证书。
只需在一个 Master 节点上操作
kubeadm init phase certs etcd-ca |
etcd-ca
证书对(证书
/etc/kubernetes/pki/etcd/ca.crt
和私钥
/etc/kubernetes/pki/etcd/ca.key
) 拷贝到另外 2 台
etcd
节点的相同路径下(
/etc/kubernetes/pki/etcd/
)
etcd
集群使用的证书文件。
在所有的 Master 节点上操作
。执行以下操作,基于以上步骤中重新生成的 CA 证书,签发
etcd
集群所需的其他证书
kubeadm init phase certs etcd-healthcheck-client |
etcd
集群及 Kubernetes 集群的状态
# curl 127.0.0.1:2381/health |
安装以上步骤操作后,检查 Kubernetes 集群中管理节点 Pod 状态发现
master3
节点上面的
kube-apiserver
、
kube-scheduler
和
kube-controller-manager
处于
CrashLoopBackOff
# kubectl get pods -n kube-system |
检查
kube-apiserver-k8s-master3
日志发现是因为证书问题无法连接到
etcd
。
原因可能是因为 这些组件连接
etcd
的证书导致
# kubectl logs -n kube-system kube-apiserver-k8s-master3 |
在
master3
节点上
执行以下命令更新
kube-apiserver
连接
etcd
时使用的客户端证书
/etc/kubernetes/pki/apiserver-etcd-client.crt
,需要先删除
/etc/kubernetes/pki/apiserver-etcd-client.crt
和
/etc/kubernetes/pki/apiserver-etcd-client.key
,否则更新时会报错:
error execution phase certs/apiserver-etcd-client: [certs] certificate apiserver-etcd-client not signed by CA certificate etcd/ca: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "etcd-ca")
# kubeadm init phase certs apiserver-etcd-client --config=/root/kubeadm-config.yaml |
检查
kube-controller-manager-k8s-master3
日志和
kube-scheduler-k8s-master3
日志,显示相关端口已经绑定,kill 掉被占用端口的进程后重试
# kubectl logs -n kube-system kube-controller-manager-k8s-master3 |
在更新集群证书后,
kube-apiserver
异常,检查日志,有证书过期的错误
authentication.go:63] "Unable to authenticate the request" err="[x509: certificate has expired or is not yet valid: current time 2023-12-11T07:35:22Z is after 2023-12-06T09:50:35Z, verifying certificate SN=2750116196247444292, SKID=, AKID=08:39:2B:D0:14:00:F4:7F:3F:58:26:36:32:BA:F8:0E:0E:B4:D4:83 failed: x509: certificate has expired or is not yet valid: current time 2023-12-11T07:35:22Z is after 2023-12-06T09:50:35Z]" |
这个可能是因为更新证书后,其他节点依旧使用旧的证书在请求
kube-apiserver
,此时可以重启所有节点上的
kubelet
服务。
Deployment
,
Service
,
Ingress
部署后,通过
Ingress
配置的域名访问,显示
503 Service Temporarily Unavailable
检查
Ingress-Nginx
Pod 的日志,检索对应域名日志,显示返回码为 503
52.77.198.154 - - [15/Dec/2022:02:10:59 +0000] "GET /graph HTTP/1.1" 503 592 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" 507 0.000 [prometheus-prometheus-service-8080] [] - - - - 00b07fe234401054153fdbd0ffafb158 |
查看 Ingress 对应的
Service
,从以下输出中可以看到对应的
Service
为
prometheus-service
,端口为 8080
$ kubectl get ingress -n prometheus -o wide |
查看
Service
信息
$ kubectl get services -n prometheus -o wide |
从以上信息可以看到,服务的端口为
Port: prometheus-port 8090/TCP
,而 Ingress 中配置的服务端口为
8080
,修改 Ingress 配置,将服务端口修改正确。修改后访问正常。
# kubectl exec -it -n kube-system coredns-6d4b75cb6d-kdlqg -- sh |
此错误主要是因为 Kubernetes 和
cri-docker
版本问题导致
[3]