免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Kubernetes集群从零搭建:kubeadm初始化与排错实战

Kubernetes集群从零搭建:kubeadm初始化与排错实战 把一套 Kubernetes 集群从零搭到能跑业务说难不难说简单也真不简单。我见过太多人卡在 kubeadm init 那一步半天不动也见过节点状态一直是 NotReady 查了一整天才发现是 cgroup 驱动不统一。我自己从对着文档一步步抄命令到现在闭着眼睛十分钟能把环境初始化完中间踩的坑基本都集中在环境准备、版本匹配、镜像拉取和网络插件这几件事上。这篇东西就把我实际搭建 Kubernetes 集群环境与初始化时的完整步骤、参数选择和排错经验摊开来讲适合准备自己动手搭集群的运维、后端开发以及任何想搞懂容器编排底层逻辑的同学参考。1. 动手之前先把环境规划做扎实1.1 节点规划与目标场景先问自己要搭什么很多人上来就搜“kubeadm init 命令”结果环境不对、参数不对反复失败。我的习惯是动手前先花十分钟回答一个问题这套集群是给自己学 Kubernetes 概念用的还是用来跑开发测试环境或者干脆是要往生产方向靠不同目标对应完全不同的节点规划。如果是学习目的硬件要求其实不高。控制平面节点也就是 master建议至少 2 核 CPU、4G 内存工作节点同样 2 核 4G 起步磁盘空间别低于 40G。为什么这么强调磁盘因为容器镜像、日志、etcd 数据都会悄悄占地方我见过磁盘写满导致 etcd 直接只读的惨案所以宁可一开始给足空间。节点数量上有条件尽量搭 1 个控制平面加 2 个工作节点的三节点集群调度、跨节点访问、网络策略这些核心功能都能体验到。如果只有一台机器也可以单节点跑起来但需要给控制平面节点打上允许调度的 taint 标记很多集群行为会和真实环境不一致容易产生“我在单机上验证没问题啊”的错觉。操作系统我建议统一用 Ubuntu 22.04 LTS 或者 Debian 12内核版本比较新兼容性好。如果非要用 CentOS 系注意 7 的内核版本偏老跑最新版 Kubernetes 可能遇到奇怪问题。所有节点的主机名不能重复最好按 k8s-master01、k8s-node01 这种规则提前规划好IP 地址用静态配置避免 DHCP 分配变化导致 apiserver 地址失效。时间同步这一步也千万别省。Kubernetes 的证书机制、日志时间戳、etcd 选主都对时间敏感节点间时间偏差超过 100ms 就可能出现各种诡异问题。Ubuntu 上装 chrony或者直接 systemctl enable --now systemd-timesyncd 做基础同步就够了。1.2 容器运行时选型我为什么改用 containerd如果是老读者可能还记得 Docker 时代搭建 Kubernetes 的方式装 Docker然后用 kubeadm 初始化。但 Kubernetes 1.24 之后官方把内置的 Dockershim 移除了这意味着 kubelet 没法直接通过 Docker 来管理容器需要一个适配层或者换用符合 CRIContainer Runtime Interface标准的运行时。现在主流的 CRI 运行时有三类containerd、CRI-O、Docker需要额外装 cri-dockerd 适配器。containerd 是最省心的选择它本来就从 Docker 里剥离出来专门负责容器生命周期管理轻量又稳定官方也把它当作默认运行时推荐。CRI-O 是专门为 Kubernetes 设计的理念很好但生态和文档相对少一些。Docker 加 cri-dockerd 这种方案最大的问题是你得同时维护 Docker 和适配器两层多一个组件就多一个故障点。这里必须解释一个关键概念cgroup 驱动。Linux 系统上用 systemd 做 init 时容器运行时和 kubelet 都应该使用 systemd 作为 cgroup 驱动而不是默认的 cgroupfs。为什么因为如果两台马车各拉各的缰绳车的方向就不受控了。当 systemd 已经接管了资源管理时容器运行时再用 cgroupfs 去写控制文件会导致系统里出现两套资源控制逻辑节点状态会不稳定Pod 可能被莫名其妙杀掉。另一个绕不开的概念是 pause 容器也就是 sandbox_image。每个 Pod 创建时kubelet 会先拉取一个 pause 镜像它负责创建 Pod 的网络命名空间和 PID 命名空间业务容器再共享这个命名空间。所以这个镜像的地址是否正确、能不能拉取成功直接影响 Pod 能不能启动。默认情况下Kubernetes 会从 registry.k8s.io 拉取 pause 镜像但国内网络环境下这个地址有时候不太稳定我后面会说怎么处理。1.3 系统初始化内核模块、sysctl、swap 和 hosts正式安装 Kubernetes 之前每台节点都要做一遍系统初始化这一步偷懒的话后面排查到你怀疑人生。核心是内核模块、内核参数、swap 和主机解析四件事。内核模块方面必须加载 overlay 和 br_netfilter。overlay 是容器镜像分层存储用的文件系统驱动br_netfilter 让网桥流量也能经过 iptables/netfilter 处理Kubernetes 的 Service 转发依赖这个能力。把这两个模块写进 /etc/modules-load.d/k8s.conf然后 modprobe 加载。内核参数方面需要确保 net.bridge.bridge-nf-call-iptables 和 net.bridge.bridge-nf-call-ip6tables 设为 1同时开启 net.ipv4.ip_forward。这些参数写进 /etc/sysctl.d/k8s.conf 后执行 sysctl --system 生效。swap 必须关掉。Kubernetes 的 kubelet 从设计上就不希望在节点上看到 swap因为内存交换会导致 Pod 的资源配额失真影响调度判断。虽然新版本支持某些条件下的 swap 启用但默认情况下还是关掉最稳。用 swapoff -a 临时关闭然后注释掉 /etc/fstab 里的 swap 条目防止重启后回弹。hosts 文件里把集群内所有节点的主机名和 IP 对应关系写清楚这是最朴素的节点互访方式。同时用 hostnamectl set-hostname 设置好每台机器的主机名。最后确认 iptables 可用如果系统用的是 nftables 后端部分 CNI 插件可能不适应不过 Ubuntu 22.04 默认的 iptables 命令已经做了兼容一般不需要额外操作。这套初始化逻辑可以写成一个脚本在各节点统一执行保证环境一致性。我第一次搭集群时就是靠一个 init.sh 把所有节点的基础环境刷成一样的后面排查问题时省了很多力气。2. 安装核心组件版本锁定、镜像准备和 containerd 配置2.1 为什么用 kubeadm官方引导工具不是过家家Kubernetes 集群搭建方式主要有三种kubeadm、二进制部署、托管云服务。kubeadm 是官方提供的集群引导工具它做的事情很多人可能不清楚——它不只是帮你敲几条命令而是负责生成控制平面的核心配置签发证书、生成 kubeconfig 文件、配置 kubelet 启动参数、把 etcd 和 apiserver 等静态 Pod 定义写到指定目录。这些步骤手写非常容易出错kubeadm 把它们固化成了可重复的流程。有些人觉得 kubeadm 搭出来的集群是“玩具”这种观点早该过时了。很多生产环境的集群就是用 kubeadm 初始化的因为它的证书管理、升级路径都经过了充分验证而且支持高可用配置。二进制部署更适合需要深度定制或者研究内部原理的场景普通用户没有必要自己造轮子。这里要强调版本匹配的重要性。kubeadm、kubelet、kubectl 这三个组件必须使用相同的大版本和小版本比如都用 1.28.x。原因很简单kubelet 是跑在每个节点上的 agentkubeadm 是引导工具如果它们之间版本差距过大可能出现协议不兼容或者求稳策略差异导致的行为异常。官方支持一个版本的偏差但实际维护时保持一致最省心。2.2 containerd 安装与生产化配置先装 containerd。Ubuntu 上可以直接用 apt 安装但要注意版本可能偏老。我更推荐从 Docker 官方 apt 源装因为 containerd 和 Docker 同源维护版本更新及时。装完 containerd 后第一步要生成默认配置containerd config default /etc/containerd/config.toml。如果你第一次打开这个文件会发现它很长主要是一堆默认值的显式声明。我们需要修改其中三个关键位置。第一是 systemd_cgroup。找到 SystemdCgroup 这个字段把它从 false 改成 true。这就是前面说的 cgroup 驱动统一问题如果不改kubelet 会一直报 cgroup 相关的错误节点始终进不了 Ready 状态。第二是 sandbox_image。默认值是 registry.k8s.io/pause:3.9我一般改成 registry.aliyuncs.com/google_containers/pause:3.9这是国内可稳定访问的公共镜像仓库地址拉取速度快很多。第三是镜像加速配置。containerd 支持在配置里声明 registry 的 mirror也就是镜像加速器。我习惯在配置文件里给 docker.io 配置一个加速地址这样之后拉 nginx、busybox 这些公共镜像时速度会好很多。注意加速器地址格式不能带 http:// 前缀直接写域名和路径。改完配置后systemctl daemon-reload systemctl restart containerd。验证方法是用 crictl version 或者 ctr version 查看运行时版本。这里插一句crictl 是 Kubernetes 社区提供的 CRI 客户端工具它和 containerd 自带的 ctr 不一样前者能查 kubelet 视角的容器后者只能查 containerd 自己的命名空间。建议把 crictl 也装好排错时会经常用到。2.3 安装 kubeadm kubelet kubectl版本一致性是底线接下来安装 Kubernetes 核心组件。Ubuntu 上先添加 Kubernetes 官方软件源然后安装指定版本。安装命令里版本号要写全比如apt-get update apt-get install -y kubelet1.28.2-00 kubeadm1.28.2-00 kubectl1.28.2-00安装完成后立刻执行 apt-mark hold kubelet kubeadm kubectl把版本锁住。为什么一定要锁因为 kubelet 升级必须按照官方支持的路径走如果哪次 apt upgrade 顺带把 kubelet 升上去了而 kubeadm 和 kubectl 没跟着变版本不一致会导致 kubelet 起不来或者集群状态混乱。生产环境踩过这个坑的人不在少数。验证版本号kubeadm version 看 kubeadm 版本kubelet --version 看 kubelet 版本kubectl version --client 看客户端版本。三个版本要完全一致。另外注意这一步需要在所有节点上执行包括 worker 节点。worker 节点虽然不需要 kubeadm 做初始化但 join 时用的是 kubeadm 命令kubelet 更不用说了每台机器都得跑。2.4 预先拉取镜像避免 init 时“卡在原地”kubeadm init 的过程看起来像是命令执行到某个地方就卡住了其实很多时候不是真的卡住而是在后台拉取镜像。默认情况下kubeadm 会从 registry.k8s.io 拉取控制平面组件镜像包括 kube-apiserver、kube-controller-manager、kube-scheduler、etcd、coredns、pause。这个地址在国内网络环境下速度不稳定一旦拉取超时整个初始化流程就卡住了。正规做法是在初始化之前手动用 kubeadm 把镜像拉到本地。命令分两部分kubeadm config images list --image-repository registry.aliyuncs.com/google_containers kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers第一条命令是列出需要哪些镜像第二条才是真正拉取。这里 --image-repository 参数指定了公共镜像仓库地址阿里云的这个项目专门同步了 Kubernetes 官方镜像版本覆盖很全。拉取完成后可以用 crictl images 查看本地已有的镜像列表确认 pause 镜像和各个组件镜像都就位了。有个细节容易踩坑crictl 默认连接的 socket 路径可能不对导致它看不到 containerd 里的镜像。Ubuntu 上通常需要设置环境变量 export CONTAINER_RUNTIME_ENDPOINTunix:///run/containerd/containerd.sock或者在 /etc/crictl.yaml 里写好配置。否则 crictl images 显示的镜像和实际 containerd 里的对不上排错时会很困惑。3. 控制面初始化kubeadm init 实测全流程3.1 init 前最后检查清单别急着敲命令在控制平面节点上执行 kubeadm init 之前我把最后检查清单列出来每一项目标都非常明确。第一确认 swap 是关着的检查命令 swapon --show 输出为空。第二确认内核参数已经生效sysctl net.bridge.bridge-nf-call-iptables 输出 1。第三确认主机名和 hosts 解析无误getent hosts k8s-master01 能返回正确 IP。第四确认本机监听端口没有被占用控制平面需要 6443apiserver、2379-2380etcd、10250kubelet、10257kube-controller-manager、10259kube-scheduler这些端口。可以用 ss -lntp 逐个检查如果端口被占用多半是之前部署过其他东西需要先清理。第五确认 kubelet 和 containerd 的 cgroup 驱动一致。怎么确认看一眼 /etc/containerd/config.toml 里的 SystemdCgroup 是否为 true再看 kubelet 默认配置如果都是 systemd 就没问题。第六确认镜像已经拉取完毕用 crictl images 查看重点确认 pause 镜像存在。第七确认当前用户是 root或者有 sudo 权限因为 kubeadm init 涉及写入 /etc/kubernetes 和创建系统目录权限不足会直接失败。这七个检查项看着啰嗦但每次都能帮我提前发现问题。我搭环境遇到过最离谱的一次是节点上装了旧版 Docker导致 6443 端口被占用结果 kubeadm init 反复失败最后发现是旧组件的锅清理干净就好了。3.2 kubeadm init 完整命令和输出结果解读一切就绪后执行真正的初始化命令。以 Kubernetes 1.28 为例我常用的命令如下kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --kubernetes-versionv1.28.2 \ --pod-network-cidr192.168.0.0/16 \ --image-repository registry.aliyuncs.com/google_containers逐项解释参数含义。--apiserver-advertise-address 指定 apiserver 对外通告的 IP 地址必须是这台机器能够实际绑定的静态 IP不能是 127.0.0.1也不能是动态获取的临时 IP。--kubernetes-version 指定版本号v 前缀不能漏要和前面安装的 kubeadm 版本一致。--pod-network-cidr 是 Pod 网段这里我用了 192.168.0.0/16因为后面要用 Calico 作为 CNI 插件Calico 默认的 IP 池就是这个网段。如果你打算用 Flannel这里要改成 10.244.0.0/16。这个参数必须和 CNI 插件的配置对齐否则后面 Pod 之间没法通信。--image-repository 指定镜像仓库地址。如果不加kubeadm 会从默认地址 registry.k8s.io 拉取很可能因为网络问题卡住。执行后耐心等待正常情况下会看到一堆日志输出包括证书生成、etcd 启动、控制平面组件启动等步骤。最后几行会出现一个关键提示Your Kubernetes control-plane has initialized successfully!这段输出非常重要它会给出三样东西kubeconfig 文件的位置、kubeadm join 命令用于加入工作节点、以及一些初始化完成后需要做的操作提示。我强烈建议把 join 命令复制下来单独保存因为 token 默认 24 小时过期过期后虽然可以重新生成但没必要给自己找麻烦。3.3 配置 kubectl让 root 和普通用户都能操作集群初始化完成后kubectl 还不能直接使用因为它不知道集群的凭证信息。kubeadm 已经把管理凭证放在了 /etc/kubernetes/admin.conf 里我们需要把它配置成 kubectl 默认读取的 kubeconfig。root 用户可以这样配export KUBECONFIG/etc/kubernetes/admin.conf echo export KUBECONFIG/etc/kubernetes/admin.conf ~/.bashrc普通用户则建议复制一份到自己的家目录mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config这里解释一下 admin.conf 是什么它包含集群 CA 证书、admin 用户的客户端证书和私钥、以及 apiserver 的访问地址。Kubernetes 的访问控制模型里kubectl 只是拿着证书向 apiserver 请求apiserver 会校验证书合法性并决定权限。配置好之后先跑 kubectl get nodes你会看到控制平面节点已经注册但状态是 NotReady。再跑 kubectl get pods -n kube-system会发现 CoreDNS 一直处于 Pending 状态。这些都是正常现象因为网络插件还没装。很多人第一次搭到这里以为集群有问题其实只是 CNI 没就绪而已。4. 网络插件选型与工作节点加入4.1 CNI 选型Calico、Flannel、Cilium 怎么选CNIContainer Network Interface插件负责给每个 Pod 分配 IP 并打通网络。没有 CNIPod 也能创建但没有网络地址所有跨 Pod 通信都会失败所以这是集群初始化的必经环节。当前主流的 CNI 插件有三种。Flannel 是最简单的方案它用 VXLAN 之类的 overlay 技术在节点间建立虚拟网络配置量最小社区文档也最全适合学习和测试。但 Flannel 不提供网络策略功能也就是说你没法定义“哪些 Pod 可以互相访问”这类规则。Calico 则要丰富得多它默认用 BGP 协议在三层直接路由 Pod 流量性能比 VXLAN 封装更好同时内置了完整的 NetworkPolicy 支持。Cilium 是后起之秀基于 eBPF 技术实现性能非常强悍可观测性和安全能力也是一流的但它的概念和配置复杂度会让初学者头疼。我个人的选择逻辑是这样的如果目标是快速跑通一套环境验证 Kubernetes 基础功能Flannel 就够了如果要模拟生产环境或者需要用到网络策略直接上 Calico如果后面还想深入学习云原生网络和可观测性可以尝试 Cilium。这里要特别提醒一个关键点初始化时使用的 --pod-network-cidr 参数必须和 CNI 插件默认配置的网段一致。Calico 默认是 192.168.0.0/16Flannel 默认是 10.244.0.0/16。如果不匹配CNI 插件分配出来的 IP 不在 kube-apiserver 声明的网段范围内网络策略和路由都会出问题。4.2 安装 Calico一条命令后的等待与验证我以 Calico 为例演示安装过程。需要先下载 Calico 的 manifest 文件然后应用到集群curl -OL https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml kubectl apply -f calico.yaml如果 GitHub 的 raw 地址访问不太稳定也可以从国内能找到的镜像站点下载校验文件内容不变即可。这个 manifest 文件包含了 Calico 所需的全部资源DaemonSet每个节点跑一个 calico-node、Deploymentcalico-kube-controllers、RBAC、ConfigMap 等。应用完 manifest 后等一两分钟用 kubectl get pods -n kube-system 查看你会看到 calico-node 的 Pod 开始逐步变成 Running 状态。这个过程中涉及镜像拉取首次耗时取决于网络状况。验证 Calico 是否真正就绪有个关键指标kubectl get nodes 的输出里节点的 STATUS 应该从 NotReady 变成 Ready。这代表 kubelet 通过 CNI 插件成功拿到了节点网络的健康状况反馈。如果等了三五分钟节点还是 NotReady多半是 Calico 的 Pod 有问题按照后面第六节的方法排查。4.3 工作节点加入集群token 和证书哈希一个不能少工作节点的加入是 kubeadm 集群搭建的最后一块拼图。在 worker 节点上需要提前完成前面第二章讲的所有操作系统初始化、装 containerd 并配置、装 kubelet 和 kubeadm。然后执行控制平面节点初始化时输出的 join 命令kubeadm join 192.168.1.10:6443 --token token \ --discovery-token-ca-cert-hash sha256:hash这个命令做的事情是worker 节点向 apiserver 发起加入请求通过 token 证明自己有资格加入集群用 CA 证书哈希验证控制平面身份然后生成 kubelet 需要的证书和配置把节点注册到集群中。如果当初没有保存 join 命令可以在控制平面节点上重新生成输入kubeadm token create --print-join-command这条命令会生成一个新的 token 并直接打印出完整的 join 命令。如果集群里的 CA 证书哈希你也没有记录可以用 openssl 从 CA 证书计算但更简单的办法是查找 /etc/kubernetes/pki/ca.crt 并手动计算。不过我一般直接用 --print-join-command生成的命令是完整的方便复制。执行 join 命令后回到控制平面节点执行 kubectl get nodes你应该能看到新节点已经出现在列表里。一开始状态可能是 NotReady等 kubelet 启动、CNI 插件在新节点上运行就会变成 Ready。这个过程通常需要一两分钟。5. 集群验证与生产化补充5.1 健康检查清单从“安装完成”到“真正可用”节点全部 Ready 只是一个开始真正判断集群可用需要做一轮完整检查。我的检查清单如下kubectl get nodes所有节点 Ready版本号一致。kubectl get pods -A所有系统组件 Pod 处于 Running 状态特别是 CoreDNS 和 Calico。kubectl get events --sort-by.metadata.creationTimestamp查看最近的集群事件确认没有错误级别的事件。kubectl top nodes如果能输出 CPU 和内存数据说明 Metrics Server 已经正常工作了。这些检查都过集群最基础的链路才算是通的。然后我会跑一个测试应用验证调度和跨节点通信kubectl create deployment nginx --imagenginx kubectl expose deployment nginx --port80 --typeNodePort然后 kubectl get svc 查看 NodePort 端口通过任意节点的 IP 加这个端口访问如果页面能打开说明从用户请求到 Pod 的完整链路是通的。最后再用一个临时 Pod 验证集群内 DNSkubectl run busybox --imagebusybox --rm -it --restartNever -- nslookup kubernetes.default.svc.cluster.local能看到正常的解析结果说明 CoreDNS 没问题。很多人搭完集群kubectl get nodes 看到 Ready 就认为大功告成结果真正部署应用时发现跨节点 Pod 互相访问不了、DNS 解析超时回过头来排查才发现最基础的链路根本没验证。这套检查流程花不了几分钟能帮你提前暴露大部分问题。5.2 可选增强Metrics Server、Dashboard 和监控集群基础通了之后通常还会装几个常用组件。Metrics Server 是首选它采集节点和 Pod 的 CPU、内存数据是 kubectl top、HPA水平自动扩缩容的前提。安装方式kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml默认情况下 Metrics Server 会因为证书校验问题报错需要在 Deployment 里加一个启动参数 --kubelet-insecure-tls。原因在于 Metrics Server 通过 kubelet 的只读端口拉数据而 kubelet 的证书通常不是 Kubernetes 集群 CA 签发的校验会失败。Dashboard 可以装但不是必须。我倾向于在初期先不装 Dashboard强迫自己适应命令行工作流反而更容易理解 Kubernetes 的对象模型。如果确实需要可视化界面用 kubectl apply 部署官方 manifest然后通过 token 登录即可。最后的聚合层监控生产环境一般会上 Prometheus 加 Grafana这是另一个大话题这里就不展开了。学习阶段先把 Metrics Server 装好对理解集群资源状态已经很有帮助。5.3 高可用控制平面以后做生产的必要准备如果是打算长期使用或者往生产方向发展单一控制平面节点始终是个隐患。kubeadm 是原生支持高可用控制平面部署的思路是在 kubeadm init 时指定 --control-plane-endpoint 参数指向一个负载均衡地址比如 keepalived 的 VIP 或者 haproxy 后端后续再加入其他控制平面节点时kubeadm join 加上 --control-plane 参数即可。etcd 的部署模式也有两种选择一种是 stacked 高可用etcd 和 kube-apiserver 在同一组控制平面节点上部署简单另一种是 external etcdetcd 独立部署在专门的机器上职责分离更彻底。生产环境强烈建议至少做 stacked 高可用虽然会多消耗一些资源但能避免单点故障。控制平面证书默认有效期是一年。kubeadm certs check-expiration 可以查看证书过期时间到期前需要执行 kubeadm certs renew all 并重启相关组件。我建议大家把证书续期写进节假日的例行巡检清单里别等到访问 apiserver 报证书过期才手忙脚乱。6. 常见问题与排查实录我踩过的坑6.1 kubeadm init 卡在等待 kubelet 启动这个现象太常见了kubeadm init 执行到某个阶段后控制台一直打印类似 [kubelet-check] Waiting for kubelet to be ready... 的内容等多久都没反应。我遇到过的原因主要就三个。第一是 cgroup 驱动不一致。kubelet 默认用 systemd 驱动而 containerd 如果没改 SystemdCgroup两者对资源控制的理解就不一样kubelet 检查节点状态时发现异常迟迟不能通过。解决办法就是改 containerd 配置并重启。第二是必要的镜像没拉全。尤其是 pause 镜像如果它不存在kubelet 创建 Pod 时一定会失败而这个检查阶段又恰好需要临时创建一个 Pod 来验证节点状态所以会一直卡住。解决方案是回头执行 kubeadm config images pull。第三是 kubelet 服务本身有问题。用 journalctl -u kubelet -f 查看 kubelet 日志通常能直接看到报错信息。最常见的是缺少 CNI 插件、读取配置文件失败、或者引用了不存在的目录。遇到卡住先别急着敲 ctrlc等它彻底超时然后按上面三条逐项排查。如果确认是配置混乱导致的环境问题执行 kubeadm reset -f 清理删掉 /etc/kubernetes 和 /var/lib/kubelet 目录再重新初始化。我一开始老觉得 reset 会破坏什么其实它就是把集群写过的配置清掉属于标准操作。6.2 节点一直 NotReady八成是 CNI 没就绪所有节点加入后kubectl get nodes 显示状态是 NotReady且持续了五分钟以上我第一反应是 CNI 插件没有正常工作。查看方法kubectl get pods -n kube-system | grep -E calico|flannel看看网络插件 Pod 的状态。如果 Calico 的 Pod 卡在 ImagePullBackOff说明镜像拉取失败检查 containerd 的镜像加速配置。如果 Pod 一直 CrashLoopBackOff看日志kubectl logs -n kube-system 。Calico 常见报错包括 IP 池配置错误、BGP peer 连接失败、内核模块缺失等。如果是 Flannel 节点不 Ready优先看 /run/flannel/subnet.env 文件是否存在以及里面的网段和 kubeadm init 时声明的 --pod-network-cidr 是否一致。版本不匹配也会导致问题比如 Flannel 版本对内核的要求没满足。还要检查一个很容易被忽略的点IP 转发是否开启。CNI 插件创建 veth 对后需要宿主机做三层转发如果 net.ipv4.ip_forward 没设成 1跨节点的 Pod 通信一定失败节点就会一直显示 NotReady。6.3 CoreDNS 一直 CrashLoopBackOffCoreDNS 是集群内部的 DNS 服务它如果反复重启最典型的原因就是 DNS 循环解析。什么情况会产生循环当节点上的 /etc/resolv.conf 把 nameserver 指向了 127.0.0.53systemd-resolved 的默认做法时CoreDNS 的 Pod 内部再去解析外部域名请求就绕回了自己形成死循环。解决办法有两个方向。一个是在 kubelet 服务配置里指定一个干净的 resolv.conf 文件。Kubernetes 官方推荐的方式是在 /etc/systemd/system/kubelet.service.d/ 下创建一个 drop-in 配置文件加入 --resolv-conf/etc/resolv.conf 参数前提是 /etc/resolv.conf 本身要用一台真实有效的 DNS 服务器地址。另一个方向是修改 CoreDNS 的 Corefile把 forward 目标换成集群外部可用的 DNS 地址。判断是否是循环问题kubectl logs -n kube-system 查看日志会看到大量 SERVFAIL 或 connection refused 记录。这个问题在我刚接触 Kubernetes 时困扰了我一整天后来发现几十字的配置改动就能解决。6.4 Token 过期与证书更新kubeadm 生成的 token 默认有效期是 24 小时如果你搭集群时保存了 join 命令但隔天才去加 worker 节点就会遇到 token 过期报错。解决办法很简单在控制平面节点执行 kubeadm token create --print-join-command这会生成一个全新的 token并打印出完整的 join 命令。证书方面控制平面组件的证书默认有效期是一年其中 etcd 的证书也是一年。可以用 kubeadm certs check-expiration 查看所有证书的过期时间和剩余天数。到期前执行kubeadm certs renew all systemctl restart kubelet这条命令会续期所有由 kubeadm 管理的证书。注意它不会自动重启 apiserver 等静态 Pod因为静态 Pod 由 kubelet 监管kubelet 重启后会自动拉起新证书的组件。如果证书已经过期导致 apiserver 无法访问那恢复起来就更麻烦需要先离线更新证书再启动组件所以定时检查证书有效期非常重要。6.5 镜像拉取慢或失败的应对策略镜像拉取问题在 Kubernetes 环境搭建中太常见了尤其是控制平面组件默认镜像地址在国内网络下访问不稳定的情况。我的应对策略有三个层次。第一初始化时直接用 --image-repository 指定国内公共镜像仓库地址这是最彻底的办法所有控制平面组件镜像都会从这个仓库拉取。第二给 containerd 配置 docker.io 的加速器这样后续手动运行业务容器时镜像拉取速度快很多。第三提前拉取镜像也就是先运行 kubeadm config images pull把可能用到的镜像提前备好避免 init 或 join 过程中因为网络波动而中断。还有一个容易忽略的点不要在 Pod 的 image 字段里手动写死某个镜像的完整地址导致绕过了 containerd 的加速配置。正确做法是保持镜像名简洁比如 nginx、busybox让 containerd 根据 registry mirror 配置自动决定从哪拉取。这样既统一又好管理。我在实际操作中最深的体会是Kubernetes 集群搭建这个事照着教程抄命令很快但真正把每个参数、每个状态背后发生了什么搞清楚才能在出问题时快速定位。到现在我每次搭新环境都会顺手把初始化命令整理成一个模板脚本存起来下次直接改 IP 和版本号就能复用。如果你也准备动手搭一套建议别复制完命令就跑拿到 join 输出和拿到 NotReady 后的排查过程才是这套技术真正值钱的部分。
返回列表