ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kubernetes高可用集群:kubeadm init初始化控制面全解析

Kubernetes高可用集群:kubeadm init初始化控制面全解析 手头这套 Kubernetes 高可用集群从硬件规划到把 Keepalived HAProxy 这份“虚拟入口”搭好已经写到了系列第九篇。第十篇轮到整个流程里最让人紧张的一步在第一个控制平面节点上执行kubeadm init把集群的控制面真正拉起来。这篇会把初始化日志从 preflight 到生成 join 命令的每一段拆开讲再补上初始化后的三个必做验证动作最后用一份 Nginx 应用验证整个集群的调度和访问链路。读完你不仅能复现同样的初始化过程还能在日志报错时快速定位问题不用再对着满屏 ERROR 干瞪眼。在动手之前先明确这篇的定位前面九篇我们已经完成了节点基础环境、containerd 容器运行时、kubeadm kubelet kubectl 安装以及 Keepalived HAProxy 的高可用入口配置。也就是说所有“前置条件”都已经就位这一篇就是从执行kubeadm init开始到集群第一个节点 Ready 为止的完整现场记录附带使用 Nginx 验证集群可用性的实测步骤。适合已经照着系列前几篇搭好环境、准备初始化控制面节点的小伙伴参考也适合遇到初始化报错时直接翻这篇做排查。1. 初始化前把每块拼图对到位网络、内核与镜像源1.1 前九篇铺垫了什么这一篇解决什么高可用集群和单机集群最大的区别就是控制面不能只绑在一台机器上。我们前面搭的 HAProxy Keepalived 方案本质上是给 kube-apiserver 做了一个虚拟 IP 入口客户端包括 kubelet、kubectl、kube-proxy都通过10.10.1.100:6443这个 VIP 访问 API Server而不是直连某一台节点的真实 IP。这样一来任意一台控制平面节点宕机VIP 会自动漂移到另一台请求不会中断。这一篇要做的就是把第一个控制平面节点“变成”集群的初始控制面。kubeadm init会完成一系列看起来很繁琐、但实则环环相扣的工作检查环境、拉取镜像、生成证书、写 kubeconfig、生成静态 Pod 清单、启动控制面组件、最后生成其他节点的 join 命令。这个过程在日志里就是一串带方括号的阶段标记搞清楚每个阶段在干什么遇到报错才能精准定位。动手前建议再核对一遍三件事hostname是否已经改成规划好的名称比如 master01/master02/master03、/etc/hosts里是否写好了所有节点和 VIP 的解析、swapoff -a是否已执行并写入/etc/fstab注释掉 swap 行。这些前置检查很多新手会忽略结果 preflight 阶段直接红一大片后面排查半天发现只是个小配置问题特别浪费时间。1.2 kubeadm init 的核心参数如何选择kubeadm init不是一条固定命令参数选错会导致后续联调出现问题尤其是网络插件和 API Server 地址这两个点。我这边 1.32 版本实测使用的命令如下你可以根据自己网络规划做调整kubeadm init \ --apiserver-advertise-address10.10.1.11 \ --control-plane-endpoint10.10.1.100:6443 \ --image-repositoryregistry.aliyuncs.com/google_containers \ --kubernetes-versionv1.32.0 \ --pod-network-cidr10.244.0.0/16 \ --service-cidr10.96.0.0/12 \ --cri-socketunix:///run/containerd/containerd.sock逐个拆解一下这些参数的含义--apiserver-advertise-address是本机 IP也就是 API Server 监听和对外通告的地址我这里写的是第一台控制面节点 master01 的地址10.10.1.11。--control-plane-endpoint必须写成 VIP 加端口10.10.1.100:6443它会被写进生成的证书和 kubeconfig 里后续所有节点、客户端都通过这个地址访问 API Server。这里有个关键点广告地址和端点地址是两个不同的东西前者告诉本节点我是谁后者告诉所有组件你该访问谁。--pod-network-cidr决定了 Pod 网段这里用10.244.0.0/16是 Flannel 的默认习惯。--service-cidr是 Service 网段10.96.0.0/12是 kubeadm 的默认值一般不轻易改。这两个网段必须和后面的网络插件CNI配置保持一致否则插件启动后 Pod 网段冲突节点会一直 NotReady。镜像仓库我用的是registry.aliyuncs.com/google_containers这在国内环境下几乎是必须的直接拉 k8s.gcr.io 大概率超时。1.32 版本对应的各类组件镜像kubeadm会自动拼接仓库地址拉取不需要手动一个个处理。--cri-socket指定 containerd 的 socket 路径如果前面装的是其他 CRI 实现这里要对应修改。提示kubeadm init执行前可以用kubeadm config images list --image-repositoryregistry.aliyuncs.com/google_containers --kubernetes-versionv1.32.0查看需要拉取的镜像清单先kubeadm config images pull预拉一遍避免 init 过程中卡在漫长的拉镜像阶段。2. kubeadm init 日志从 preflight 到 join 命令逐段拆解2.1 preflight启动前那些硬检查fail 了不要慌kubeadm init执行后不会立刻开始搭建而是先做一整轮 preflight 检查。这就是日志里[preflight] Running pre-flight checks那段也是很多人第一次初始化时最容易看到红色 ERROR 的位置。下面是我在现场截取的一段日志结构1.32 的输出和早期版本高度一致[init] using kubernetes version: v1.32.0 [preflight] running pre-flight checks [preflight] pulling images required for setting up a kubernetes cluster [preflight] this might take a minute or two, depending on the speed of your internet connectionpreflight 检查的项目非常多包括但不限于是否以 root 执行、内核版本是否满足、ip_forward是否开启、swap 是否关闭、/proc/sys/net/bridge/bridge-nf-call-iptables是否生效、6443/10250 等端口是否被占用、containerd 是否正常运行、kubelet是否已安装。每一项检查不过都会输出对应的[ERROR ...]行。我见过最多的失败原因有三个swap 没关干净、端口被上次测试残留的进程占用、ip_forward内核参数没生效。前两个直接检查配置即可第三个需要在/etc/sysctl.d/下写配置并执行sysctl --system确保net.ipv4.ip_forward1已生效。preflight 阶段报错不用慌它已经把问题定位到具体检查项了按提示修完重新执行即可。preflight 通过后紧跟着是拉镜像阶段。1.32 需要拉取的镜像包括 kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd、coredns、pause 等一堆数量在十个左右。如果网络状况不好这里会卡很久。有个小技巧看到[preflight] pulling images后可以另开一个终端用crictl images实时查看镜像拉取进度比干等日志输出要直观得多。2.2 证书、kubeconfig 与控制面组件看见“视频架构”如何由静态 Pod 拉起镜像就绪后日志会依次出现证书生成、kubeconfig 生成、静态 Pod 清单写入等阶段[certs] generating certificate ca [certs] generating certificate apiserver [certs] generating certificate etcd/healthcheck-client ... [kubeconfig] generating kubeconfig files [kubelet-start] writing kubelet environment file [control-plane] using manifest folder /etc/kubernetes/manifests [etcd] creating static pod manifest for local etcd[certs]这一大段负责生成整套 PKI 证书体系。kubeadm会生成 ca、apiserver、apiserver-kubelet-client、front-proxy-ca、etcd/ca、etcd/server、etcd/peer 等一堆证书。这些证书分别服务不同的组件间认证比如 kube-apiserver 要验证 kubelet 的身份、etcd 集群内部节点之间要互信。证书有效期默认是十年CA 是一年如果以后要续期用kubeadm certs renew处理即可。[control-plane] using manifest folder这行非常值得留意。kubeadm 启动控制面组件并不是直接跑进程而是把每个组件的 Pod 清单 YAML 写到/etc/kubernetes/manifests/目录下。kubelet 会持续监听这个目录发现新清单就把它变成静态 Pod 启动起来。所以你去ls /etc/kubernetes/manifests/会看到四个文件etcd.yaml、kube-apiserver.yaml、kube-controller-manager.yaml、kube-scheduler.yaml控制面的本质就是这四组静态 Pod。这里有个容易困惑的点为什么 etcd 也在这个目录里因为每个控制平面节点上跑的 etcd 是独立静态 Pod三个控制面节点各跑一个 etcd它们通过 peer 地址互相通信组成 etcd 集群这份清单就是本地 etcd 的启动配置。控制面静态 Pod 启动后日志进入等待阶段[wait-control-plane] waiting for the kubelet to boot the control plane as static pods [wait-control-plane] waiting for the kubelet to boot the control plane as static pods from /etc/kubernetes/manifests这时日志会短暂“卡住”最长可能要等一两分钟因为 kubelet 要把四个静态 Pod 全部拉起来并且 API Server 要能正常响应/healthz才算通过。实际观察推荐用crictl ps -a或crictl logs看容器状态如果某个组件容器反复重启大概率是镜像没拉全或者配置参数写错后面第三节会讲具体排查。2.3 join 命令生成其他节点的入场券控制面组件全部就绪后日志会继续走到 token 生成和控制面标记阶段[mark-control-plane] marking the node as control-plane by adding the label: node-role.kubernetes.io/control-plane [mark-control-plane] marking the node as control-plane by adding the taints: node-role.kubernetes.io/control-plane [bootstrap-token] configuring the bootstrap tokens ... [addons] installing coreDNS [addons] installing kube-proxy这一段有两个信息量很大的动作。一个是给当前节点打上node-role.kubernetes.io/control-plane标签和污点这个污点决定了常规 Pod 不会被调度到控制平面节点上除非显式容忍所以你的业务 Pod 默认只跑在 worker 节点这个设计是合理的。另一个是安装 CoreDNS 和 kube-proxy这俩是集群的基础插件前者提供集群内 DNS 解析后者维护 Service 的转发规则。最后输出的那段kubeadm join ...命令是给后续第二个、第三个控制面节点以及 worker 节点用的入场券包含了 VIP 地址、token 和 CA 证书的 SHA256 指纹。这条命令一定要完整保存如果弄丢了也别急可以在任意控制面节点上用kubeadm token create --print-join-command重新生成一条新的。加入控制面节点时命令末尾会多一个--control-plane参数加入 worker 节点时则去掉该参数具体区别后面系列篇章会展开讲。3. 初始化后的三个必做动作与失败现场排查实录3.1 配置 kubectl 并验证集群状态初始化结束后第一件事不是急着kubectl get nodes而是要先把 kubectl 的管理员凭据配好。kubeadm init会在本机生成/etc/kubernetes/admin.conf里面包含了管理员账号的 kubeconfig。如果你是 root 用户直接执行mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config如果是普通用户还要把文件属主改成当前用户或者用sudo chown $(id -u):$(id -g) $HOME/.kube/config处理。配好后执行kubectl get nodes应该能看到 master01 处于 NotReady 状态这是预期的因为还没装网络插件。验证集群组件是否健康用kubectl get pods -n kube-system -o wide查看所有控制面 Pod 的状态。正常情况下 kube-apiserver、kube-controller-manager、kube-scheduler、etcd 几个容器都是 Runningcoredns 可能处于 Pending原因同样是缺少网络插件。此时用crictl ps查看容器列表能看到控制面组件的容器都在运行只是 Pod 层因 CNI 缺失无法 Ready。装网络插件这一步我用的是 Flannel直接应用官方清单即可kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml这里有几个容易踩的坑。第一Flannel 默认使用的网段是10.244.0.0/16如果 init 时--pod-network-cidr不是这个网段需要下载 yaml 后修改network字段再应用。第二Flannel 要用宿主机的eth0网卡作为默认出口如果你的机器有多网卡需要在 yaml 的args里加--ifaceeth0指定实际网卡。第三Flannel 的 Pod 需要能够访问 API Server而访问地址正是 VIP所以前面 HAProxy 没配好的话Flannel 会一直 CrashLoopBackOff这算是高可用集群部署中特有的联动坑。装完网络插件后再次kubectl get nodes节点从 NotReady 变为 Ready需要等一小段时间让 CNI 在节点上完成初始化一般一分钟内能看到变化。3.2 高频失败场景排查口诀与速查表整个初始化过程中如果遇到报错第一原则是看两个日志源kubelet 日志和容器运行时日志。kubelet 的实时日志用journalctl -u kubelet -f查看它几乎会告诉你所有卡住的真实原因容器本身的输出则用crictl logs container-id查看。下面把我在多个环境里遇到的高频问题整理成速查表方便你直接对照现象可能原因处理方式preflight 报[ERROR FileContent--proc-sys-net-ipv4-ip_forward]内核转发未开启写入/etc/sysctl.d/98-k8s.conf执行sysctl --systempreflight 报端口被占用上次部署残留或其它服务占用ss -lntp检查 6443/10250 等端口杀掉进程或换节点镜像拉取卡住或超时默认仓库访问慢换registry.aliyuncs.com/google_containers仓库提前kubeadm config images pull多个报错同时出现且都指向 kubelet 未运行kubelet 服务没起来systemctl status kubelet查看先启动并设为开机自启初始化卡在[wait-control-plane]的 40s 超时kubelet 没把静态 Pod 拉起来journalctl -u kubelet -f查看具体原因常见的是镜像缺失或证书配置异常节点长时间 NotReadyCNI 网络插件未安装或网段不匹配检查 Flannel 等插件是否 Running确认--pod-network-cidr与插件配置一致coredns 一直 Pending节点存在未容忍的污点确认网络插件已 Ready等待调度即可若是单节点集群需要去除污点还有一条很实用的经验kubeadm init失败后想重新初始化不能直接再跑一遍需要先kubeadm reset清理现场。这个命令会重置 kubelet 配置、清空 etcd 数据、删除/etc/kubernetes下的证书和清单。如果清理不彻底第二次 init 时会被 preflight 的目录占用检查拦住报[ERROR DirAvailable--var-lib-etcd]。必要时还要手动清理/var/lib/etcd、/var/lib/kubelet、/etc/cni/net.d等目录确保环境干净。注意kubeadm reset只能清理 kubeadm 管理的内容像 flannel 这类 CNI 插件留下的网络网卡和路由规则建议用ip link delete cni0之类的命令手动清理否则下次部署会出现 IP 分配异常。4. 用 Nginx 压测集群可用性部署、调度与访问链路4.1 部署 Nginx 应用并观察调度结果控制面节点 Ready 以后集群从“搭起来”变成了“活着”但“活着”和“能用”之间还差一个完整的验证链路。我最常用的验证方式就是部署一份 Nginx 应用它足够简单能快速覆盖镜像拉取、Pod 调度、控制器创建、Service 暴露等关键链路。先创建一个 Deployment指定镜像和副本数我这里用 3 个副本kubectl create deployment nginx-test --imagenginx:1.25 --replicas3很快kubectl get pods -o wide就能看到 3 个 Pod 分别调度到不同的 worker 节点上。这一步本身就在隐式验证kube-scheduler 是否正常工作、每个节点上的 kubelet 能否成功拉起容器、containerd 能否从仓库拉取镜像、Flannel 能否给 Pod 分配 IP。如果这一环出了问题十有八九是网络插件没安装好或者节点上镜像拉取受限。Deployment 创建后我习惯再用kubectl get deployment nginx-test确认 DESIRED、CURRENT、READY 三列都对齐然后看kubectl rollout status deployment/nginx-test的输出确认滚动发布已完成。这里有个小技巧如果想先看看最终生成的 YAML 长什么样再决定是否创建可以用kubectl create deployment nginx-test --imagenginx:1.25 --replicas3 --dry-runclient -o yaml nginx-test.yaml先导出清单再修改后 apply这样对想要微调副本数、添加资源限制的场景特别方便。4.2 通过 NodePort 走通客户端访问链路Pod 创建成功只验证了集群内部的调度和容器运行对外访问链路还需要通过 Service 暴露。执行kubectl expose deployment nginx-test --typeNodePort --port80 --target-port80这条命令会创建一个 Service将 80 端口映射到每个节点上的一个随机高位端口默认范围 30000-32767。查看 Service 信息kubectl get svc nginx-test假设输出中PORT(S)列显示80:32678/TCP那么任意节点包括 worker 和控制面节点的32678端口都能访问到这个 Nginx 服务。用curl http://任意节点IP:32678测试能返回 Nginx 默认欢迎页就说明整条链路是通的DNS/Service 发现 → kube-proxy 规则 → 后端 Pod → 业务容器。这里我想提醒一个 NodePort 的细节NodePort 实际上是 kube-proxy 在每个节点上创建的 iptables 或 IPVS 规则它把访问节点端口的数据包转发到 Service 的 ClusterIP再由 Service 转发到后端 Pod。所以访问任意节点的 NodePort 都能到达服务前提是该节点的防火墙放行了对应端口生产环境尤其要注意安全组策略。如果curl不通排查顺序建议是先确认 Pod 处于 Running再确认 Service 的 Endpoints 列表不为空kubectl get endpoints nginx-test然后用iptables -t nat -L -n | grep 32678检查转发规则是否存在。绝大多数 NodePort 不通的问题都出在这三步里的第二步——Service 没有关联到任何健康 Pod。验证完 NodePort 后我还习惯再测试一遍集群 DNS 能力进入任意 Pod用curl访问 Service 名称。kubectl exec -it nginx-pod-name -- curl http://nginx-test.default.svc.cluster.local能通的话说明 CoreDNS 解析、集群内服务发现链路也正常这套高可用集群从控制面到数据面才算真正完整可用。在我实际操作中初始化成功那一刻其实只是万里长征第一步真正让我安心的永远是后面这条完整的验证链路节点 Ready、控制面 Pod Running、业务应用正常调度、外部访问链路畅通。这一整套流程跑通后后续再往集群里加控制面节点、加 worker 节点或者部署有状态应用心里就有底了。还有一个小习惯想分享给你每次初始化完我都会把kubeadm join命令、证书有效期、当前版本信息、网络插件版本都记录到一个部署文档里后续排查和升级时翻起来会节省大量时间这个成本投入远比你想象中划算。
返回列表