最近在维护一套基于Docker和cri-dockerd的Kubernetes生产环境时,遇到了一个棘手的问题:每当服务器执行计划内重启或意外断电后,kube-apiserver、kube-controller-manager和kube-scheduler这三个核心控制平面组件总是无法自动恢复。这直接导致集群处于不可用状态,需要人工介入才能恢复服务。
这种情况在传统使用systemd直接管理静态Pod的部署方式中很少出现。问题的特殊性在于我们采用了Docker作为容器运行时,并通过cri-dockerd这个适配器来满足Kubernetes的CRI接口要求。这种架构组合在生产环境中越来越常见,但相关的故障处理经验却相对缺乏。
在标准的Kubernetes部署中,控制平面组件通常以静态Pod的形式运行。这些Pod由kubelet直接管理,其定义文件存放在/etc/kubernetes/manifests目录下。kubelet会持续监控这个目录,确保其中定义的Pod始终处于运行状态。
这种设计的一个重要特性就是自愈能力——即使节点重启,kubelet也会在启动后自动重新创建这些静态Pod。但在我们的场景中,这套机制似乎失效了。
当使用Docker作为容器运行时,整个工作流程会多出几个关键环节:
这种架构下,任何一环出现问题都可能导致组件无法正常恢复。特别是在系统重启场景下,各组件的启动顺序和依赖关系变得尤为关键。
通过分析多台节点的系统日志,我们发现了一个关键现象:Docker服务启动完成后,cri-dockerd服务有时会启动失败,或者虽然显示启动成功,但实际无法正常处理CRI请求。
进一步检查发现,这是由于Docker服务完全就绪所需的时间比systemd预设的超时时间更长。在某些硬件配置较低的节点上,Docker可能需要30秒以上才能完全初始化,而cri-dockerd的systemd单元默认可能在20秒后就尝试启动。
在故障节点上,以下几个日志片段特别值得关注:
code复制# cri-dockerd日志
timeout waiting for Docker to be ready
failed to connect to Docker daemon
# kubelet日志
Container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready
这些日志明确指出了组件间的依赖问题:cri-dockerd需要Docker完全就绪后才能正常工作,而kubelet又依赖于cri-dockerd。
执行以下命令验证服务状态和依赖关系:
bash复制# 查看服务启动时间线
journalctl -u docker -u cri-docker -u kubelet --no-pager
# 检查服务依赖定义
systemctl show cri-docker | grep Requires
结果显示cri-dockerd服务虽然声明了Requires=docker.service,但并没有配置足够的启动延迟或重试机制。
针对cri-dockerd服务,我们需要修改其systemd单元文件,增加对Docker服务的强依赖和启动延迟:
bash复制sudo tee /etc/systemd/system/cri-docker.service.d/10-wait-docker.conf > /dev/null <<EOF
[Unit]
After=docker.service
Requires=docker.service
StartLimitIntervalSec=600
StartLimitBurst=5
[Service]
Restart=always
RestartSec=5s
ExecStartPre=/bin/sleep 30
EOF
关键配置说明:
After=docker.service确保Docker先启动StartLimit*设置防止服务频繁重启ExecStartPre添加30秒延迟,确保Docker完全就绪Restart策略确保服务异常退出后自动恢复同时调整kubelet的配置,增加对容器运行时就绪的等待时间:
bash复制sudo tee /etc/systemd/system/kubelet.service.d/10-wait-cri.conf > /dev/null <<EOF
[Unit]
After=cri-docker.service
Requires=cri-docker.service
[Service]
Environment="KUBELET_EXTRA_ARGS=--runtime-request-timeout=10m"
EOF
应用配置更改并验证:
bash复制# 重新加载systemd配置
sudo systemctl daemon-reload
# 重启服务验证
sudo systemctl restart docker cri-docker kubelet
# 检查服务状态
systemctl status docker cri-docker kubelet -l
建议部署以下监控指标,提前发现问题:
yaml复制# Prometheus监控规则示例
groups:
- name: k8s-control-plane
rules:
- alert: ControlPlaneComponentDown
expr: absent(up{job="apiserver"}) or absent(up{job="kube-controller-manager"}) or absent(up{job="kube-scheduler"})
for: 5m
labels:
severity: critical
annotations:
summary: "Kubernetes control plane component down"
description: "{{ $labels.instance }}: {{ $labels.job }} is down for more than 5 minutes"
对于关键生产环境,考虑以下架构优化:
建立每月一次的预防性维护检查:
当发现控制平面组件未自动恢复时,按以下步骤操作:
bash复制# 1. 检查各服务状态
sudo systemctl status docker cri-docker kubelet -l
# 2. 查看容器运行状态
sudo docker ps -a | grep -E "kube-apiserver|kube-controller-manager|kube-scheduler"
# 3. 尝试手动重启服务
sudo systemctl restart docker cri-docker kubelet
# 4. 如果仍不恢复,检查静态Pod定义
ls -l /etc/kubernetes/manifests/
cat /etc/kubernetes/manifests/kube-apiserver.yaml
收集排错所需的完整日志:
bash复制# 收集系统服务日志
journalctl -u docker -u cri-docker -u kubelet --no-pager > k8s_services.log
# 收集Docker容器日志
sudo docker logs $(sudo docker ps -aqf "name=k8s_kube-apiserver") > apiserver.log
sudo docker logs $(sudo docker ps -aqf "name=k8s_kube-controller-manager") > controller.log
sudo docker logs $(sudo docker ps -aqf "name=k8s_kube-scheduler") > scheduler.log
# 收集Kubernetes事件
kubectl get events --sort-by='.metadata.creationTimestamp' > k8s_events.log
虽然Docker+cri-dockerd方案可以工作,但从长期维护角度考虑,建议:
采用Terraform+Ansible等工具实现:
针对Kubernetes工作负载优化内核参数:
bash复制# /etc/sysctl.d/10-k8s.conf
net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-iptables=1
fs.inotify.max_user_watches=524288
vm.swappiness=10
应用配置后执行sysctl -p生效。