Kubernetes集群搭建实战:kubeadm init到Calico网络配置全解析
做K8s集群搭建与初始化很多教程一上来就是让你跑几条命令然后莫名其妙就拿到了一个Ready的集群。但真正落到生产环境或者自己认真学一遍时会发现网络不通、节点NotReady、Certificates过期、cgroup driver不匹配……各种问题接踵而至。这篇文章我不想再写那种照着抄就能成功的流水账而是想从环境规划、运行时选择、控制平面初始化、CNI网络、节点接入、验证这条完整链路出发把每个环节背后为什么这么做讲清楚同时把我踩过的坑和后续运维会遇到的问题一并交代。适合正在准备考CKA、需要在自己电脑或机房搭一套可靠测试环境的人阅读也适合公司内部需要快速交付一套开发/测试集群的同学参考。内容以Kubernetes 1.29.x containerd 1.7.x Calico为主兼容当前主流生态。1. 环境规划版本选型与资源分配里的那些坑很多人搭集群失败不是命令敲错而是环境和版本组合压根不匹配。Kubernetes对操作系统、内核、容器运行时的版本依赖比一般软件敏感得多我见过太多人拿着一套旧的初始化脚本配新版本结果kubeadm init直接报一串看不懂的证书或ServiceAccount错误。1.1 版本组合怎么选最省心我个人的建议是Kubernetes小版本不要追最新选当前稳定线里已经发布半年以上的版本比如1.29.x、1.30.x。这样能保证kubeadm、kubelet、kubectl这三件套互相兼容同时containerd和CNI插件生态也已经跟上。你要是非要用K8s 1.31配一个很老的containerd版本大概率会遇到CRI接口兼容问题报错信息还特别误导人。一个具体可行的版本组合参考如下组件推荐版本说明操作系统Rocky Linux 9.x / Ubuntu 22.04内核版本满足overlay、iptables模块需求别再用CentOS 7这种内核过老的系统Kubernetes1.29.x当前生态兼容性最好的一条线containerd1.7.x已内置CRI插件无需额外装cri-dockerdCalico3.27.x 或 3.28.x支持K8s 1.29/1.30内核≥5.10检查uname -r太老内核会导致网络插件诡异问题这里单独说明一下如果你一定要用Docker作为运行时需要额外安装cri-dockerd适配器因为Kubernetes从1.24开始就彻底移除了dockershim。我不推荐走这条路除非你公司项目有历史包袱。直接上containerd是现代集群的标准做法。1.2 节点规划不要盲目追求高可用实验环境通常选一主二从生产测试环境如果资源有限一主一从、甚至单节点把control-plane的taint去掉也能先跑起来。高可用不是必须的你要先想清楚这套集群的用途。我遇到过不少朋友在网上看到生产环境要求三主三从于是跑到云厂商买了一堆按量付费实例建完发现master负载极低钱全浪费了。真实情况是个人实验/备考1个master 1~2个worker即可公司开发环境1个master 2~3个worker或者直接单控制平面多worker生产环境才需要考虑至少3个master与外部负载均衡器节点资源方面master节点建议至少2核4Gworker节点至少2核2G。如果你跑的是机器学习训练或数据类负载worker节点再往上加CPU和内存不要把GPU等异构资源奢望在初始化阶段解决。1.3 网络规划要提前定死集群初始化阶段有一个参数很多人不重视--pod-network-cidr。这个CIDR决定了集群内部每个Pod拿到的IP段一旦网络插件装上之后这个段不能随意改动否则CNI插件和kube-controller-manager分配的IP对不上整个集群的Pod网络基本废掉。Flannel默认用的10.244.0.0/16Calico默认用的192.168.0.0/16。我建议不管后续选哪个插件初始化时统一指定一个明确且不大于/16的专用网段比如10.244.0.0/16同时预留一个10.96.0.0/12给Service ClusterIP。这两个段都要选私有地址段不能和你物理机/云VPC的网段冲突否则Pod访问外部服务会出各种奇怪的路由问题。提示VPC网段如果是10.0.0.0/16那么Pod网段和Service网段就必须换掉比如Pod用10.244.0.0/16Service用10.99.0.0/16。1.4 主机名与hosts解析Kubernetes的所有节点之间通过主机名互相识别kubeadm生成的证书里也包含主机名信息。所以主机名一定不要用默认的localhost更不要出现重复。我规范的做法是master节点k8s-master01每个worker节点k8s-worker01、k8s-worker02然后在每台机器的/etc/hosts里写好全部节点的解析192.168.10.10 k8s-master01 192.168.10.11 k8s-worker01 192.168.10.12 k8s-worker02如果你后续要做高可用这个hosts还要加入VIP虚拟IP与域名映射比如k8s-master-vip。现在先养成好习惯后边省事很多。生产环境建议用内网DNS实验环境直接hosts搞定。2. 系统层面初始化把内核参数、模块、limits一次配到位K8s集群对操作系统的要求不是能装软件就行它依赖一些Linux内核模块与网络桥接特性。很多Node进程起不来、Pod网络跨节点不通追根溯源都是这一层没排查干净。所以我在搭建步骤里把系统初始化单独拎出来讲这些操作每台节点都要执行。2.1 关闭swap与防火墙Kubernetes要求所有节点关闭swap这跟kubelet的cgroup资源管理强相关。如果你不关闭swapkubelet启动时会直接报错或者反复重启。执行swapoff -a sed -ri s/.*swap.*/#/ /etc/fstab然后确认free -h输出中Swap行为0。防火墙方面如果你用的是firewalld直接停掉最省事systemctl disable --now firewalld有朋友会担心关防火墙是不是不安全其实K8s集群的网络策略是由CNI插件比如Calico的NetworkPolicy来做的节点系统防火墙在集群内部反而是干扰项。如果你所在环境强制要求开启系统防火墙那必须放行6443/tcp、10250/tcp、2379/tcp、8472/udp等端口但我会明确告诉你实验和开发环境关闭系统防火墙是减少排查成本的最佳选择。2.2 加载br_netfilter与overlay模块Kubernetes的Pod网络依赖Linux的bridge和iptables能力需要提前加载两个核心模块cat EOF | tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter这两个模块分别负责容器镜像分层挂载与bridge网络下的iptables规则转发。不加的话即使集群能初始化后续跨节点Pod访问也可能直接超时。然后设置系统网络参数让iptables能够处理桥接流量cat EOF | tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sysctl --system这里说明bridge-nf-call-iptables必须为1否则Calico等基于iptables的CNI插件无法过滤bridge流量Service的ClusterIP转发也会出问题。ip_forward是Pod到外部网络通信的基础设置为1后让核心支持路由转发。2.3 配置ipvs或ip_tables模式kube-proxy会用到kube-proxy支持iptables和IPVS两种模式。默认是iptables但在大规模集群中IPTABLES规则数量多了以后性能下降明显IPVS模式是更好的选择。如果要启用IPVS模式需要提前加载相应内核模块cat EOF | tee /etc/modules-load.d/ipvs.conf ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack EOF modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_wrr modprobe ip_vs_sh modprobe nf_conntrack在kubeadm初始化的配置文件kubeadm-config.yaml中将kubeProxy.configuration.mode设置为ipvs即可。实验环境用iptables也没问题生产环境我强烈建议用IPVS因为Service数量超过1000之后iptables规则链的延迟会越来越明显。2.4 时间同步与文件句柄限制Kubernetes集群节点之间对时间偏差很敏感尤其kubelet和kube-apiserver通信时如果时间差太大x509证书校验可能会失败。实验环境可以简单用chrony同步也可以直接用timedatectl set-ntp true开启NTP。集群规模较大时还需要调高文件句柄与进程数限制ulimit -n 65535 echo ulimit -n 65535 /etc/profile个人实验环境也许感觉不到差异但在工作节点上跑大量Pod时too many open files这个错误很常见。提前调整就能避免一次生产事故。3. 容器运行时containerd的生成配置与调试技巧既然已经决定不用Docker了那么containerd就是重头戏。它的安装本身不难但有一个配置细节如果不处理kubelet会和containerd无法对话——这就是SystemdCgroup参数。我在这一步卡过整整一晚必须拿出来单独说。3.1 安装containerd在Rocky Linux或CentOS系使用yum在Ubuntu使用apt# RHEL系列 yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y containerd.io也可以直接从GitHub上下载containerd二进制包按release页面说明解压安装。官方yum源的好处是会自动带上runC和相关依赖省心一点。安装完成后先不急着启动因为默认配置文件需要调整。3.2 生成并修改config.tomlcontainerd的默认配置路径是/etc/containerd/config.toml。先用这条命令生成一份默认配置mkdir -p /etc/containerd containerd config default | tee /etc/containerd/config.toml然后重点修改两个地方。第一个将SystemdCgroup从false改成true[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true为什么必须改Kubernetes的kubelet默认使用systemd作为cgroup drivercontainerd的runc也需要与kubelet保持一致否则Node状态会显示kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs这类错误。这个错是我见过最多的初始化失败原因之一绝对值得提前规避。第二个推荐修改sandbox_image的地址。containerd默认拉取的pause镜像在registry.k8s.io如果你所在网络环境访问这个源不稳定Pod启动时会产生大量ImagePullBackOff。可以改成国内可访问的镜像源[plugins.io.containerd.grpc.v1.cri] sandbox_image registry.aliyuncs.com/google_containers/pause:3.9这里pause:3.9要和你的K8s版本配套。一般K8s 1.29对应pause 3.9K8s 1.30对应pause 3.10左右实际以kubeadm config images list输出为准。修改完后启动并设置开机自启systemctl daemon-reload systemctl enable --now containerd3.3 用crictl验证运行时containerd自带CRI插件但crictl命令需要单独安装或配置。推荐在安装完containerd后顺手装一个crictl它比ctr命令更贴合K8s的CRI接口排错时非常有用。先下载crictlVERSIONv1.29.0 wget https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz tar zxvf crictl-$VERSION-linux-amd64.tar.gz -C /usr/local/bin然后配置/etc/crictl.yamlruntime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false这时执行crictl images或crictl ps都可以正常访问containerd也算是打包验证了一遍运行时是否可用。3.4 检查镜像拉取网络初始化时kubeadm需要从远端拉取控制平面组件镜像。如果直接拉registry.k8s.io不顺畅kubeadm init会卡在[cri-images]很久然后报超时。你先执行kubeadm config images list看看当前版本需要哪些镜像然后手动crictl pull一个测试下网络和运行时。在初始化之前我强烈建议预先用crictl把控制平面所需镜像全部拉到本机。这么做的好处是即使后续在线拉取中断你本机已有镜像kubeadm init时会快很多也更容易定位问题。如果你确定网络环境不佳可以用阿里云镜像仓库提前拉取并重新打tag。基本思路是把registry.k8s.io/kube-apiserver:v1.29.x等镜像先crictl pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.29.x再用ctr -n k8s.io i tag重新标记为registry.k8s.io/kube-apiserver:v1.29.x让kubeadm检查时认为自己拉过了。4. 控制平面初始化kubeadm init的完整参数解析与风险点控制平面是整个集群的大脑kubeadm init的每个参数都对应一个集群核心机制。很多人在这里只会套用网上的一行命令遇到问题之后完全没有排查头绪。所以我建议不要直接执行长命令而是先写一个kubeadm配置文件。好处有两个一是配置项可读、可版本化管理二是方便调整apiserver参数、kube-proxy模式、证书有效期等。4.1 安装kubeadm、kubelet、kubectl参照Kubernetes官方文档设置软件源。以RHEL系列为例cat EOF | tee /etc/yum.repos.d/kubernetes.repo [kubernetes] nameKubernetes baseurlhttps://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled1 gpgcheck0 EOF yum install -y kubelet-1.29.x kubeadm-1.29.x kubectl-1.29.x systemctl enable --now kubelet这里允许kubelet在初始化前没有配置文件而处于失败状态这是正常的不必惊慌。kubeadm init会自动生成kubelet所需的配置文件并重启它。Ubuntu/Debian系统则使用apt源curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/k8s.gpg然后用apt安装。具体请以官方文档为准。4.2 编写kubeadm-config.yaml这份配置我建议放在/etc/kubernetes/kubeadm-config.yamlapiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.10.10 bindPort: 6443 nodeRegistration: criSocket: unix:///run/containerd/containerd.sock --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: 1.29.5 controlPlaneEndpoint: 192.168.10.10:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 imageRepository: registry.aliyuncs.com/google_containers --- apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: ipvs如果你是在单节点上测试controlPlaneEndpoint可以写成与advertiseAddress相同的地址。如果后续做高可用这里要换成VIP或域名。imageRepository这项很关键。写成阿里镜像仓库后kubeadm会自动去registry.aliyuncs.com/google_containers拉镜像能绕开大部分网络问题。不过也请注意如果它内部拼接的镜像路径与你预先手动拉取的tag不一致会重新尝试拉取。所以上面第三节我才强调尽量用crictl提前把依赖镜像拉完并且tag要与当前kubeadm版本严格匹配。4.3 执行init并处理证书有效期一切就绪后在master节点执行kubeadm init --config /etc/kubernetes/kubeadm-config.yaml --upload-certs如果看到Your Kubernetes control-plane has initialized successfully!说明初始化成功。如果卡在拉取镜像阶段先排查网络与镜像tag如果卡在某一步certs先kubeadm reset -f rm -rf /etc/kubernetes/manifests再重来。这里有一个被大家反复提到的问题kubeadm默认签发的证书有效期只有一年。在生产上一年一签需要纳入运维计划在个人实验环境我通常会临时把证书延长到十年省得集群放几个月后突然所有组件报证书过期。方法是在ClusterConfiguration里加入apiServer: extraArgs: service-account-issuer: kubernetes.default.svc service-account-signing-key-file: /etc/kubernetes/pki/sa.key certificateValidityPeriod: 87600h但注意certificateValidityPeriod并不是v1beta3配置的公开字段不同版本支持程度不一样。我实测中更稳妥的方式是初始化完成后立刻用kubeadm certs renew all --config/etc/kubernetes/kubeadm-config.yaml并把新证书分发到各节点或者采用官方推荐的固定期自动续签流程。不要为了图省事去改一个后续升级时可能失效的隐式字段。4.4 配置kubectl与保存join信息初始化成功后按提示执行mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config然后kubectl get nodes大概率会看到master节点的状态是NotReady。这非常正常原因是还没有安装CNI网络插件。不要急着对节点做任何处理先进入网络插件环节。初始化输出末尾会有一段kubeadm join ... --token ... --discovery-token-ca-cert-hash ...一定要保存下来之后worker节点要用。如果弄丢了可以用kubeadm token create --print-join-command在新版本中如果希望worker节点能拿到控制平面相关证书还需要--control-plane参数和--certificate-key这个只有在--upload-certs时才生成且证书key默认24小时有效。所以生产上直接从文件系统拷贝证书或部署独立的PKI体系更可靠实验环境把join命令保存好就够了。5. CNI网络插件选型与部署为什么我对Calico更放心控制平面起来了但节点尚未Ready原因就是缺少CNI插件。K8s本身不实现Pod网络它只定义了CNI接口让Flannel、Calico、Weave、Cilium这些插件来具体执行。网络插件的选择影响整个集群的转发模型、网络策略能力以及故障排查复杂度。5.1 Flannel还是Calico从实验环境最简单上手来说Flannel的确领先。它用VXLAN或者host-gw封装配置极简一条kubectl apply -f kube-flannel.yml就完事。但它的网络策略能力很弱不支持NetworkPolicy也不擅长复杂的跨网段多租户隔离。Calico则走BGP或IPIP模式天然支持NetworkPolicy性能比Flannel的VXLAN更优适合生产环境。缺点是部署参数多一点BGP模式下要求节点二层能通如果你的机器跨VPCIPIP模式更合适。我给出的建议是场景选择个人快速实验Flannel足够公司开发/测试集群Calico生产集群Calico或Cilium对强制网络策略有需求Calico本文默认选择Calico 3.27。5.2 安装Calico的完整过程从Calico官方仓库下载manifestcurl -O https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml然后需要把你初始化时设定的podSubnet填进去。默认Calico使用的IP池是192.168.0.0/16如果你的podSubnet不是这个段就必须修改。在calico.yaml中定位到CALICO_IPV4POOL_CIDR环境变量改成- name: CALICO_IPV4POOL_CIDR value: 10.244.0.0/16然后执行kubectl apply -f calico.yaml接下来观察所有Pod启动状态kubectl get pods -n kube-system -w等calico-node和calico-kube-controllers都进入Running后再查看节点kubectl get nodes此时master节点应该变为Ready。5.3 IPIP与BGP模式选择Calico默认安装在IPIP模式下适合跨网段环境。如果所有节点都在同一个二层网络内可以改成BGP模式减少IPIP封装带来的额外开销。修改方法是将calico.yaml中IPPool相关的IPIP字段从Always改为Never或者直接通过Calico API调整IPPool配置。我在一个公司项目里就遇到过这样的场景所有机器都在同一VPC网络延迟要求极高用IPIP模式能跑但吞吐有损耗改成BGP模式后跨节点Pod间的Ping延迟降低了一截。需要注意的是BGP模式对数据中心或云网络上不同子网的跨界访问不友好确认好网络拓扑再切换。5.4 NetworkPolicy尝鲜Calico装好了以后一个隐形福利就是天然支持NetworkPolicy。K8s的NetworkPolicy对象可以定义Pod之间、Pod与外部之间的访问规则。比如想限制某组Pod只能被特定来源访问apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-from-frontend spec: podSelector: matchLabels: app: backend ingress: - from: - podSelector: matchLabels: app: frontend这个对象在Flannel下通常会被忽略或由插件提供兼容性支持但在Calico下是严格执行的。文章后面可以用它作为验证集群是否健康的测试项之一。6. 工作节点接入token、ca-cert-hash与节点标签管理控制平面Ready之后剩下就是把worker节点加入集群。这一步的坑主要集中在token过期、证书hash不匹配、以及节点名称冲突三个方向。6.1 使用kubeadm join加入在worker节点完成第2、3节所述的系统初始化与containerd安装后直接执行之前保存的join命令kubeadm join 192.168.10.10:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxx如果提示缺少--cri-socket则加上kubeadm join 192.168.10.10:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxx \ --cri-socket unix:///run/containerd/containerd.sock在老的架构里还要装Docker现在不需要了。只要worker上的kubelet能够连上master的apiserver它就会自动开始注册节点并运行必要的Pod。如果你使用高可用架构join命令中的地址不是具体某台master的IP而是VIP或域名例如https://k8s-master-vip:6443。这样控制平面内部发生故障转移时worker节点不需要任何改动。6.2 Token失效与重新生成正常情况下kubeadm init生成的bootstrap token默认有效期是24小时。如果你过了一两天才去添加worker节点token早就失效了join时直接报invalid token。这时候可以在master上重新生成kubeadm token create --print-join-command --ttl 2h如果你连CA证书hash都丢了也可以拿到openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2/dev/null | openssl dgst -sha256 -hex | sed s/^.* //把这段输出拼到--discovery-token-ca-cert-hash sha256:后面即可。需要注意不要复制到换行符或多余空格否则hash校验会失败。6.3 节点标签与污点管理节点加入成功后会处于Ready状态但它目前没有任何标签。实际部署时你肯定希望某些业务Pod只运行在worker节点上而不是master节点。虽然默认情况下控制平面节点存在node-role.kubernetes.io/control-plane:NoSchedule污点业务Pod不会调度上去但明确打标签仍然是良好的管理习惯kubectl label node k8s-worker01 node-role.kubernetes.io/worker如果你就是想在master节点上跑测试Pod比如资源有限可以移除污点kubectl taint nodes k8s-master01 node-role.kubernetes.io/control-plane:NoSchedule-但这种做法只适合实验环境。生产上master节点不应该运行业务负载否则apiserver性能受干扰整个集群的稳定性都会下降。我遇到过有些网上教程在初始化时直接让你去掉master污点这是非常糟糕的生产习惯。6.4 验证节点与组件健康节点全部加入后快速检查集群健康状况kubectl get nodes -o wide kubectl get pods -A kubectl get cs正常情况下所有节点应该是Readykube-system命名空间下的CoreDNS、calico-node、kube-proxy等都处于Running。kubectl get cs可能会显示一些组件Unhealthy这是新版kubeadm的已知现象因为kubectl get cs已不再直接反映集群健康状态以kubectl get componentstatuses和实际功能为准即可。7. 第一个应用部署与集群功能验证初始化完成不是终点你还需要用一个真实的Deployment验证集群的网络、调度、DNS、服务发现等机制是否正常。这一步对出问题后的排查至关重要。7.1 部署测试应用创建nginx命名空间并部署一个nginx应用kubectl create namespace test kubectl create deployment nginx-test --imagenginx:1.25 --replicas2 -n test kubectl expose deployment nginx-test --port80 --typeClusterIP -n test接着看一眼Pod分布kubectl get pods -n test -o wide正常情况下两个Pod会分散调度到不同节点上。如果你只有1个worker节点它们也会尽量平均分配。这一步验证了集群调度器是否正常分配资源。7.2 验证Service DNS与集群IP转发在集群内任意一个Pod中通过Service名称访问nginxkubectl run curl-test --imagecurlimages/curl --rm -it --restartNever -- sh然后执行curl http://nginx-test.test.svc.cluster.local能够返回nginx欢迎页说明CoreDNS解析、Service代理、Pod网络这些都通了。如果这一步失败优先检查CoreDNS Pod是否Runningkube-proxy是否正常Calico的BGP邻居或VXLAN隧道是否建立有时候直接在节点上ping ServiceIP不通反而是正常的因为ClusterIP在设计上不依赖ICMP响应要用curl或wget访问真实端口。7.3 验证跨节点Pod通信在两台不同的节点上分别启动测试Pod互ping或互curl。如果通说明CNI网络完整。不通时先看calicoctl node status再看路由表ip route最后抓包看VXLAN或IPIP封包是否正常。常见的一个坑是节点上存在多个网卡Calico选错了默认网卡。这样会导致节点之间的BGP邻居建立不了或者数据包来回路径不一致。解决方法是在calico.yaml中指定- name: IP_AUTODETECTION_METHOD value: interfaceeth.*把eth.*替换成你实际内网网卡的模式比如interfaceenp.*或interfaceens.*。7.4 验证网络策略与调度最后可以测试一下NetworkPolicy是否生效。在test命名空间里创建一个只允许特定来源Pod访问后端的策略然后从其他Pod访问看出是否被拒绝。Calico部署好后这个验证非常直接也能让你后续写真正业务网络策略时更有底气。这些全部通过之后这套集群已经可以放心使用了。接下来就是如何做监控、日志、持久化存储、以及后续的应用发布流程。8. 日常维护与常见故障速查避免初始化后继续踩坑集群上线只是一个开始。我在实际运维过程中最常见的几类问题分别集中在证书、磁盘空间、网络插件重置、节点NotReady这几个方向。这里集中梳理一下当作一种速查手册。8.1 节点NotReady的排查链路当节点状态变为NotReady时不要急着重启机器。先看kubelet状态systemctl status kubelet journalctl -u kubelet -f重点排查三个因素kubelet是否还在运行节点磁盘空间是否已满df -h/var/lib/kubelet目录权限是否正常磁盘满导致的NotReady是我见过最多的场景。容器镜像、日志、临时文件堆积一旦撑爆根分区或/var分区kubelet就开始报Failed to update node status节点很快标记为NotReady。生产环境务必给/var/lib/containerd和/var/lib/kubelet独立分区并配置日志轮转。8.2 证书快过期怎么办如果你不去处理证书一年后会出现大量诡异问题kubectl访问apiserver有时通有时不通kubelet无法连接apiserver或节点状态异常。处理方法如下# 检查证书有效期 kubeadm certs check-expiration # 续期全部证书 kubeadm certs renew all # 重启控制平面相关Pod在master节点 ls /etc/kubernetes/manifests/ | grep -E kube-apiserver|kube-controller|kube-scheduler | while read file; do file/etc/kubernetes/manifests/$file if [ -f $file ]; then mv $file ${file}.tmp sleep 2 mv ${file}.tmp $file fi done再把更新后的/etc/kubernetes/admin.conf重新拷贝到本机的~/.kube/config否则当前kubectl仍会使用旧证书。如果是高可用集群还要把pki目录下新生成的证书同步到其他master节点。需要注意的是kubeadm certs renew all不会自动处理kubelet.conf里的证书kubelet的轮转机制一般会在客户端自动续期但如果没配置好还是要重新拷贝或执行kubeadm kubeconfig regen --kubeconfig/etc/kubernetes/kubelet.conf。8.3 集群初始化失败怎么干净地重置初始化失败后很多人会反复执行kubeadm init结果报错越来越奇怪。这是因为上一次初始化的残留文件还在。完整重置流程kubeadm reset -f rm -rf /etc/kubernetes/manifests /etc/kubernetes/pki /var/lib/kubelet /etc/cni/net.d rm -rf $HOME/.kube iptables -F iptables -t nat -F iptables -t mangle -F ipvsadm -C systemctl restart containerd如果网络规则没清干净即使重新初始化成功旧网络规则也会干扰新Pod通信。重置时务必把iptables、IPVS规则一并清掉。8.4 控制平面证书与网络插件目录的备份维护集群时好的习惯是定期备份/etc/kubernetes/pki目录与kubeadm-config.yaml。控制平面证书是集群所有通信信任的根一旦丢失所有节点都无法重新加入只能重建集群。我通常会做三份备份本机定时备份、对象存储一份、离线硬盘一份。不要觉得这个习惯多余真实事故中我至少见过两起因运维误删pki目录导致整个集群瘫痪的案例。这篇文章里的大部分命令我都用生产环境的标准在测试环境验证过一遍如果你照着操作大概率可以一次性得到一个健康可用的集群。但如果你和我一样喜欢在踩坑中理解系统那么我建议你在初始化前、初始化后分别做一次kubectl cluster-info dump对比两次输出的差异这比背任何排错命令都更能帮助你建立对Kubernetes控制平面工作机制的直觉。最后再分享一个我自己的小习惯每套集群建好后第一时间把kubeadm-config.yaml和现有CNI配置记录下来放到内部文档或Git仓库里。环境越复杂这套初始化档案的价值越高。以后不管是谁接手这套集群都能从这些记录里快速恢复上下文而不必靠猜。