尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Kubernetes控制面重启后无法自恢复?根因与修复指南
从 kubeadm 部署一个 Docker cri-dockerd 组合的集群开始控制面组件是以静态 Pod 方式运行的。你只要经历过一次服务器重启后 kubectl 连不上 apiserver的现场就会明白我为什么对这个故障印象这么深不是某个组件真的坏了而是把 docker、cri-dockerd、kubelet 这三层的启动顺序和自启动状态全部赌在了系统默认配置上。这篇文章就把这个问题的定位思路、根因和完整修复过程拆开讲顺带记录一些我在实际运维中踩过的坑。1. 故障表现与定位思路1.1 控制面组件到底是怎么跑起来的很多人会把kube-apiserver、kube-controller-manager、kube-scheduler理解成和 nginx、mysql 一样的系统服务想着服务挂了就systemctl restart kube-apiserver。但 kubeadm 集群里根本没有 kube-apiserver.service 这个 unit它们都是静态 Pod。静态 Pod 的 YAML 文件放置在/etc/kubernetes/manifests目录下由 kubelet 进程负责监听并创建。kubelet 健康时会周期性扫描这个目录把 YAML 提交给容器运行时。这意味着控制面的生命周期完全绑在 kubelet 上kubelet 起不来apiserver 就根本不存在。而 kubelet 要创建容器又依赖容器运行时。当你使用 Docker 作为运行时且集群版本在 1.24 及以上时kubelet 不能直接连 Docker 的 unix socket 调用容器接口必须经过 cri-dockerd 这个适配层。cri-dockerd 负责把 kubelet 发出的 CRI 调用转换成 Docker Engine API。于是启动链路就变成了systemd 启动 docker → systemd 启动 cri-dockerd → systemd 启动 kubelet → kubelet 连接 /var/run/cri-dockerd.sock → kubelet 读取 /etc/kubernetes/manifests → 拉起 etcd、apiserver、controller-manager、scheduler这条链路上只要有一环在开机时没有就绪后面所有环节都会卡住。1.2 重启后的两条关键排查线索服务器重启后问题表现通常有两种形态。第一种是kubectl get nodes直接报连接超时或拒绝说明 apiserver 端口没有监听。第二种是能连上大致集群但 master 节点显示 NotReady说明某个控制面容器处于 CrashLoopBackOff 或 ContainerCreating。我一般不会先去翻 kubectl而是先按顺序检查三个核心服务systemctl status docker --no-pager systemctl status cri-dockerd --no-pager systemctl status kubelet --no-pager然后确认两个 socket 文件是否真实存在ls -l /var/run/docker.sock /var/run/cri-dockerd.sock如果cri-dockerd.sock不存在那 kubelet 再怎么重试也连不上运行时apiserver 必然起不来。这一步能帮你快速区分问题出在运行时这一层还是出在 kubelet 与静态 Pod 这一层。2. 控制面不能自恢复的三个根因2.1 服务自启没配全docker、cri-dockerd、kubelet第一个根因看起来简单但我在很多环境里都见过。kubeadm 安装文档通常只提醒你systemctl enable kubeletdocker 是发行版包管理器装的默认大概率已经 enable但 cri-dockerd 如果是从 GitHub release 下载 tar 包手动部署的service 文件不一定存在更不一定 enable。自启检查三条命令systemctl is-enabled docker systemctl is-enabled cri-dockerd systemctl is-enabled kubelet输出只要不是enabled就补上systemctl enable docker systemctl enable cri-dockerd systemctl enable kubelet systemctl daemon-reload还有一个容易被忽略的环节docker 的 socket 激活。某些发行版上 docker.service 依赖 docker.socket如果 socket 没有 enableDocker daemon 可能不会被唤醒。建议一起检查systemctl is-enabled docker.socket2.2 systemd 依赖缺失启动顺序整个乱掉即使三个服务全部 enablesystemd 在开机时也是并行拉起服务。kubelet 完全有可能比 cri-dockerd 先启动cri-dockerd 也可能比 docker 先启动。kubeadm 生成的 kubelet.service 文件默认内容非常干净[Unit] Descriptionkubelet: The Kubernetes Node Agent Documentationhttps://kubernetes.io/docs/ Wantsnetwork-online.target Afternetwork-online.target里面没有Aftercri-dockerd.service也没有Requirescri-dockerd.service。cri-dockerd 的 service 文件如果是从某份文档里复制来的依赖配置也可能不完整。结果就是开机时容器运行时的 unix socket 还没有创建kubelet 就试图连接不断报 connection refused。我排查时喜欢看开机以来的日志把时间顺序拉出来journalctl -b -u kubelet --no-pager | head -50 journalctl -b -u cri-dockerd --no-pager | head -50如果 kubelet 的第一条连接错误发生在 cri-dockerd 的 Started CRI Interface 之前基本可以断定就是启动顺序问题。2.3 静态 Pod 机制把命脉全绑在 kubelet 上第三个根因是架构层面的。apiserver、controller-manager、scheduler 没有独立的守护进程所以你不能像修普通服务那样单独拉起它们。它们被创建的前提是 kubelet 已经成功连接容器运行时并且/etc/kubernetes/manifests目录下有完整的 YAML 文件。如果异常关机导致 etcd 的 YAML 文件损坏或者/etc/kubernetes目录权限被改乱kubelet 即使活着也无法正确创建静态 Pod。这里有一个我自己的检查顺序ls -l /etc/kubernetes/manifests/ cat /etc/kubernetes/manifests/kube-apiserver.yaml | head -20确认文件存在、权限为 644 或至少可读并且 YAML 结构没有被截断。静态 Pod 的 YAML 丢失或损坏时kubelet 不会报特别醒目的错误只会静默地跳过创建这比显式报错更容易误导人。3. 完整修复过程让控制面重启后自动恢复3.1 第一刀把三件套全部设为开机自启修复的第一步是把 docker、cri-dockerd、kubelet 全部设为开机自启。这一步能解决什么都没启动的问题。systemctl enable docker systemctl enable cri-dockerd systemctl enable kubelet systemctl daemon-reload确认结果systemctl is-enabled docker cri-dockerd kubelet如果某台机器之前被人为关闭过服务自启这里会一并还原。我还会顺手确认network-online.target的状态因为 kubelet 的 service 依赖它而某些系统上这个 target 可能没有 enabledsystemctl is-enabled network-online.target3.2 第二刀给 cri-dockerd 和 kubelet 补齐依赖自启解决要不要启动依赖解决谁先谁后。找到 cri-dockerd 的 service 文件路径systemctl cat cri-dockerd通常位于/etc/systemd/system/cri-dockerd.service或/usr/lib/systemd/system/cri-dockerd.service修改后内容如下[Unit] DescriptionCRI Interface for Docker Application Container Engine Afternetwork-online.target docker.service Wantsnetwork-online.target Requiresdocker.service [Service] Typenotify ExecStart/usr/local/bin/cri-dockerd --container-runtime-endpoint fd:// Restartalways RestartSec10 StartLimitInterval0 [Install] WantedBymulti-user.target关键配置是Afterdocker.service和Requiresdocker.service。After控制顺序Requires控制硬依赖如果 docker 启动失败cri-dockerd 也不会启动。StartLimitInterval0是为了避免反复重启触发 systemd 失败上限。再看 kubelet.service同样补充 cri-dockerd 依赖[Unit] Descriptionkubelet: The Kubernetes Node Agent Documentationhttps://kubernetes.io/docs/ Wantsnetwork-online.target Afternetwork-online.target cri-dockerd.service Requirescri-dockerd.service StartLimitInterval0 [Service] ExecStart/usr/bin/kubelet Restartalways RestartSec10 StartLimitInterval0 [Install] WantedBymulti-user.target修改完以后执行systemctl daemon-reload这里提醒一句Requires搭配After才是完整表达。只写After不写Requires只能保证顺序不能保证 docker 失败时阻止 cri-dockerd 启动只写Requires不写Afterdocker 可能还在启动中 cri-dockerd 就开始连接了。如果你用的是容器化部署的 kubelet操作方式会不同但 kubeadm 场景下这套写法是稳妥的。3.3 第三刀手动拉起一轮做全局验证如果当前集群已经处于起不来的状态改完配置后不建议直接systemctl restart kubelet。要按依赖顺序从底层开始拉起systemctl start docker systemctl start cri-dockerd systemctl start kubelet每启动一个环节停一下确认状态systemctl status docker --no-pager | head -20 systemctl status cri-dockerd --no-pager | head -20 systemctl status kubelet --no-pager | head -20kubelet 起来后静态 Pod 创建需要一段时间。等待 30 秒到 2 分钟然后查看容器crictl pods --namespace kube-system如果你没装 crictl也可以用 docker CLI 过滤docker ps --filter labelio.kubernetes.pod.name --format table {{.Names}}\t{{.Status}}正常情况下应该依次看到 etcd、kube-apiserver、kube-controller-manager、kube-scheduler 的容器。如果 etcd 未就绪apiserver 会一直等待连接 2379/tcp如果 apiserver 未就绪controller-manager 和 scheduler 即使被创建也会不断重试连接。3.4 验证下次重启不会再跪手动恢复之后我习惯做一次演练式验证。这是我认为整套修复中最重要的一步reboot等两三分钟重新登录依次检查systemctl is-active docker cri-dockerd kubelet docker ps --filter labelio.kubernetes.pod.name | head kubectl get nodes这一次要确认在没有人工干预的情况下控制面自动恢复到 Ready 状态。如果还不行再用journalctl -b看本次开机日志比上次多了什么、少了什么。4. 实操中容易忽视的坑etcd、网络插件、时序4.1 镜像缺失和数据盘没有挂载控制面组件的容器镜像通常在初始化时已经拉到本地但离线环境、镜像仓库地址变更、或者清理脚本误删镜像都可能导致重启后 kubelet 无法创建容器。检查方式docker images | grep kube-apiserver docker images | grep etcd如果镜像缺失需要手动拉取对应版本。这一步不起眼但我在排查apiserver 一直 ContainerCreating时遇到过不止一次。另一个坑是 etcd 数据目录。kubeadm 默认把数据放在/var/lib/etcd但有人会把数据迁到独立数据盘。重启后挂载盘没有自动挂载etcd 容器创建失败apiserver 也起不来。检查方式mount | grep /var/lib/etcd如果挂载点不对修复/etc/fstab后重新挂载再重启 kubelet。这类问题表面上是 apiserver 起不来根源却在存储层不挂数据盘之前怎么重启都是白费。4.2 kubelet 日志里的几类典型错误排查控制面无法恢复时我最常用的是这条命令journalctl -u kubelet --since 10 minutes ago --no-pager | grep -i error | tail -30常见错误可以归成几类我用一个速查表说明日志中的关键词大概率原因优先排查动作connection refused to /var/run/cri-dockerd.sockcri-dockerd 没就绪或没启动检查 docker 与 cri-dockerd 状态Container runtime is downkubelet 与运行时连接断开按顺序重启 docker 与 cri-dockerdFailed to create pod sandboxcri-dockerd 与 Docker 通信异常查看 cri-dockerd 日志connection refused to 2379etcd 未就绪检查 etcd 容器和 /var/lib/etcdfailed to ensure lease existskubelet 与 apiserver 通信异常确认 apiserver 容器、证书、网络如果错误集中在连接拒绝先按依赖顺序重启如果错误集中在镜像拉取赶紧去查镜像如果错误集中在权限重点检查/etc/kubernetes目录和 kubelet 运行用户。4.3 网络插件不恢复node 永远 NotReady控制面三件套都恢复后如果kubectl get nodes显示 NotReady问题往往不在控制面而在网络插件。自建集群常用的 flannel 或 calico 以 DaemonSet 方式运行控制面挂掉期间这些 Pod 会被反复重建恢复周期可能很长。排查网络插件 Pod 的日志kubectl logs -n kube-flannel ds/kube-flannel --tail20如果插件配置里的 apiserver 地址、token、或节点网络接口名有问题会出现频繁 CrashLoopBackOff。网络插件不恢复就算 apiserver 活着node 的 condition 依然是 False业务 Pod 也创建不出来。所以我把这个排查放进控制面自动恢复的检查单里。4.4 时间偏移与会话过期的隐性坑虚拟机上恢复过快照的节点开机后系统时间可能偏差很大。etcd 对时钟漂移非常敏感apiserver 与 etcd、kubelet 与 apiserver 之间的证书校验也依赖时间窗口。时间偏移会导致 TLS 证书验证失败、令牌失效之类让人摸不着头脑的报错。排查方法date timedatectl status如果时间偏差大先配置 chronyd 或 systemd-timesyncd 同步时间再重启 kubelet 和相关容器。这个问题在物理机上少见但我在虚拟化环境里踩到过而且症状和apiserver 无法自动恢复完全一致。5. 一套可复用的自愈检查脚本与其每次重启后手动敲命令不如把检查逻辑写成一个脚本放在 master 节点上开机自动执行。下面是我在实际环境里使用的简化版本#!/bin/bash # 作用重启后检查 docker/cri-dockerd/kubelet 和控制面静态Pod状态 set -e for svc in docker cri-dockerd kubelet; do if systemctl is-enabled $svc /dev/null 21; then echo [OK] $svc enabled else echo [WARN] $svc NOT enabled, enable it now systemctl enable $svc fi if systemctl is-active $svc /dev/null 21; then echo [OK] $svc active else echo [WARN] $svc inactive, starting systemctl start $svc fi sleep 2 done for sock in /var/run/docker.sock /var/run/cri-dockerd.sock; do if [ -S $sock ]; then echo [OK] socket $sock exists else echo [ERR] socket $sock missing fi done echo static pod yaml files: ls -l /etc/kubernetes/manifests/这个脚本解决不了所有问题但能把重启后最常见的三件事一次确认清楚服务是否自启、服务是否存活、socket 是否就位。如果这三样都正常控制面组件绝大多数情况下能自己拉起来如果还不正常就去查 etcd 和网络插件。最后分享一个我踩过多次坑后换来的习惯每次对 master 节点做内核升级、docker 升级、挂载盘调整之前先对/etc/kubernetes/manifests目录做一次备份再对/var/lib/etcd做快照备份。控制面恢复这件事七分靠 systemd 依赖三分靠关键时刻的手动介入而如果你连静态 Pod 的 YAML 都丢了再完美的启动顺序也无济于事。
RELATED

相关推荐

Point-LIO逐点更新:高动态LiDAR惯性里程计的鲁棒性突破

Point-LIO逐点更新:高动态LiDAR惯性里程计的鲁棒性突破

Point-LIO这篇论文在LiDAR惯性里程计圈子里传得很广,不只是因为它开源、跑分好看,而是它的处理思路跟主流方案有一个根本性的不同:别人都在"攒一帧点云再处理",它偏要"来一个点处理一个点"。就这个看似简单的…

📅 2026/10/19 10:12:26
基于大数据与Hadoop的LSTM股票预测系统设计全流程

基于大数据与Hadoop的LSTM股票预测系统设计全流程

做股票预测系统这个选题,十个里至少有八个是冲着“大数据Hadoop深度学习”这个组合去的。这三个词放一起,分量确实够——大数据是底座,Hadoop解决分布式存储与分布式计算的骨架问题,深度学习作为预测引擎,一套组合下来…

📅 2026/10/19 10:12:26
时间统计工具的核心设计:状态机、SQLite与跨平台实践

时间统计工具的核心设计:状态机、SQLite与跨平台实践

1. 项目概述1.1 核心需求解析“时间工具类”这个标题看似普通,但凡是做过具体功能的人都知道,越是这种泛化名称,背后越是藏着一堆需要落地的细节。我这次踩的坑和沉淀下来的方案,核心围绕一个需求:做一个跨平台的任务计…

📅 2026/10/19 10:07:23
MORE NEWS

更多资讯

📰

MySQL性能调优实战:执行计划、锁等待与主从延迟排查

MySQL用久了,你迟早会遇到这么一天:明明按规范加了索引,查询还是慢得像爬;并发量没多大,后台却天天告警锁等待;主从架构下从库延迟十几秒,业务方反复质疑数据对不对。这些问题的答案&#xff0c…

📰

水下机器人ROV全解析:发展简史、核心技术、选型与实操避坑

我这些年摸爬滚手在水下设备这块,跟ROV打交道的次数多得数不清。ROV三个字母是Remote Operated Vehicle的缩写,中文叫遥控潜水器,说白了就是一台靠脐带缆拴在水面母船上、由人在甲板上遥控操作的水下机器人。很多人第一次看到ROV下潜前的联调…

📰

TIPC透明进程间通信:集群高可用场景下的服务寻址与故障转移机制解析

如果你维护过超过几十台节点的集群,一定对“进程之间跨节点通信到底该走什么协议”这个问题头疼过。TCP/IP 用得好好的,为什么还要去了解一个叫 TIPC 的协议?我最初也是这么想的,直到在一个需要快速故障切换的分布式系统里被连接重…

📰

分数规划专题:二分答案与最优比率问题的完整实战指南

做分数规划这类题目,很多人第一次看到题解里“二分答案 check”那一套,总觉得像是凭空冒出来的思路。我自己最开始刷“算法提高16.01分数规划”这个专题时也是一脸懵:一个求最优比率的问题,怎么就变成二分判定问题了?…

📰

机器学习驱动的恶意流量识别:特征设计、模型调参与评估实战

搞毕设选了“基于机器学习的恶意流量识别检测”这个方向,说实话一开始心里是有点没底的。这个题目听起来就带着一股“硬核”味道,真正动手之后才发现,它牵扯到网络攻防、流量分析、数据挖掘、模型训练与评估这一整条长链条,任何一…

📰

MediaPipe人体姿态识别实战:关键点检测与动作计数

简介:面向计算机相关专业学生与实战学习者,这份基于MediaPipe实现的人体姿态识别Python源码及模型包,适用于毕业设计、课程设计、期末大作业或答辩演示等场景,适合有一定Python基础、希望接触姿态识别的中级学习者。资源共138个文…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬