1. 项目概述:为什么在公有云上自建K8S不是“重复造轮子”,而是生产级落地的必经之路
“公有云上自建高可用K8S集群”——这八个字背后,藏着过去五年里我参与过的17个中大型系统迁移项目中最常被低估、也最容易踩坑的核心环节。很多人第一反应是:“云厂商不是都提供托管K8S服务(比如EKS、AKS、GKE)吗?自己搭不是白费力气?”这话没错,但只说对了一半。托管服务解决的是“有没有”的问题,而自建集群解决的是“能不能稳、敢不敢改、要不要控”的问题。我在某金融类SaaS平台做容器化改造时,就因为初期直接用托管K8S,结果在灰度发布阶段遭遇节点自动升级导致API Server短暂不可用,下游支付链路超时熔断,单次故障影响了32分钟的交易峰值期。事后复盘发现,托管服务的控制平面升级策略、网络插件版本锁定、节点OS补丁节奏,全都不在我们运维团队的掌控范围内。而切换到自建方案后,我们把etcd备份频率从默认的5分钟拉到30秒级快照+异地冷备,把kube-apiserver的健康检查探针从HTTP改为TCP+自定义指标联动,把节点滚动更新窗口从“全量并发”收紧为“最多2节点/批次+人工确认”。这些细节,没有一行代码写在K8S官方文档里,却真实决定了业务SLA能否守住99.99%。
这个项目不是给技术爱好者练手的玩具,而是面向中大型企业IT架构师、SRE工程师和云平台负责人的实战指南。它解决的是三个刚性需求:可控性(你得知道每个组件跑在哪台机器上、用什么内核、连什么网段)、可审计性(所有变更必须留痕、可回滚、可追溯)、可扩展性(当你要集成自研的GPU调度器、国产加密卡驱动或私有镜像签名验证模块时,托管服务往往不支持)。它适合两类人:一类是正在规划混合云架构、需要统一管控公有云与IDC集群的平台团队;另一类是合规要求严格的行业(如金融、医疗、能源),必须满足等保三级、信创适配、数据不出域等硬性条款的技术负责人。如果你还在用kubectl apply -f 一堆yaml就以为掌握了K8S,那这个项目会帮你把“声明式API”背后的“命令式执行”逻辑彻底掰开揉碎——比如,当你执行kubectl drain node-01时,底层到底触发了多少次etcd写入?Pod驱逐的优先级队列是如何构建的?cordon操作和taint添加之间的时间窗是否可能被新Pod抢占?这些,才是自建集群真正要直面的战场。
2. 整体架构设计与核心选型逻辑:不做“配置搬运工”,先想清楚“谁管谁”
2.1 高可用的本质不是“多装几台Master”,而是“故障域隔离+状态分离”
很多团队一上来就堆Master节点:3台Master+3台Worker,以为这就是高可用。实测下来,这种架构在公有云上反而更脆弱。原因很简单:公有云的“可用区”(AZ)不是物理机房,而是逻辑隔离单元,同一AZ内的实例共享底层宿主机、网络交换机甚至电力供应。我见过最典型的案例是某电商客户,在单AZ内部署5节点集群,结果一次底层宿主机内核panic导致整个AZ的4台Master全部失联,etcd集群因多数派丢失而进入只读状态,持续47分钟。真正的高可用设计,必须强制跨AZ部署控制平面。我们的标准方案是:3个Master节点分别落在3个不同AZ,且每个AZ仅部署1台Master + 1台Worker。这样设计有三重保障:第一,单AZ故障最多损失1个Master和1个Worker,etcd仍能维持2/3多数派;第二,Worker节点分散在3个AZ,避免业务Pod全部挤在同一个网络出口;第三,为后续跨AZ流量调度(如Ingress按延迟路由)预留基础。
提示:不要迷信“自动扩缩容”对Master节点的承诺。公有云的Auto Scaling Group(ASG)无法感知etcd成员资格变化,盲目启用可能导致新Master加入后未完成初始化就被标记为健康,进而引发脑裂。所有Master节点必须使用固定实例(On-Demand或Reserved Instance),Worker节点才可交由ASG管理。
2.2 组件分层:把K8S拆成“控制平面”“数据平面”“网络平面”三块独立拼图
自建K8S最忌讳“all-in-one”式部署。我们坚持将集群拆解为三个物理隔离层:
控制平面层:仅包含kube-apiserver、etcd、kube-scheduler、kube-controller-manager。它们运行在专用的、无标签(no taint)的Master节点上,禁止调度任何业务Pod。这里的关键是etcd——它不是K8S的附属品,而是整个集群的“心脏起搏器”。我们强制要求:etcd必须独占磁盘(不与系统盘混用)、使用SSD(非NVMe亦可,但IOPS需≥3000)、开启wal日志压缩、设置--quota-backend-bytes=8589934592(8GB)防爆仓。实测发现,当etcd数据目录超过12GB时,compact操作会导致API Server响应延迟飙升至2s以上,这是很多“集群变慢”问题的根源。
数据平面层:即Worker节点,承载所有业务Pod。我们采用“角色标签+污点”双重控制:通过node-role.kubernetes.io/worker: ""标签标识,同时打上node.kubernetes.io/unreachable:NoExecute污点,确保只有明确容忍该污点的DaemonSet(如日志采集、监控探针)才能运行。这里有个反直觉但关键的经验:Worker节点的CPU核数不宜过多。我们测试过64核机型,结果在高密度Pod场景下,kubelet同步Pod状态的goroutine竞争激烈,导致NodeReady状态上报延迟达15秒。最终选定32核机型,配合--max-pods=110参数(预留10个slot给系统组件),稳定性提升3倍。
网络平面层:这是最容易被忽视的“隐形杀手”。公有云的VPC网络本身具备高可用,但K8S的CNI插件会叠加一层Overlay网络。我们放弃Flannel(UDP封装性能损耗大)、拒绝Calico(BGP模式在云环境配置复杂),最终选择Cilium + eBPF。理由很实在:eBPF程序直接运行在内核态,绕过iptables链,网络延迟降低40%;它的NetworkPolicy实现不依赖iptables规则同步,策略生效时间从分钟级缩短到毫秒级;更重要的是,Cilium能原生对接云厂商的Security Group,让K8S网络策略和云防火墙策略真正统一。某次安全审计中,客户要求“禁止Pod访问公网但允许访问RDS”,用传统方案要配两套规则,而Cilium一条CRD就能搞定。
2.3 工具链选型:kubeadm不是终点,而是起点
kubeadm确实是官方推荐的集群初始化工具,但它只解决“怎么装”,不解决“怎么管”。我们构建了一套三层工具链:
底层编排层:Terraform。不是用它来创建EC2实例,而是管理“基础设施即代码”的完整生命周期。比如,Master节点的IAM Role权限、安全组端口开放、EBS卷加密密钥、VPC路由表关联——这些全部用HCL代码定义。好处是:当需要重建集群时,只需terraform destroy && terraform apply,所有云资源状态完全一致,杜绝“配置漂移”。
中层集群层:kubeadm + 自定义manifest。我们修改了kubeadm-config.yaml中的几个关键参数:禁用默认的CoreDNS自动部署(改用Helm Chart管理)、设置--pod-network-cidr=10.244.0.0/16(与Cilium配置对齐)、开启--feature-gates=RotateKubeletServerCertificate=true(证书自动轮换)。特别注意:kubeadm init生成的admin.conf文件,我们不会直接分发给运维人员,而是用client-go写一个轻量工具,从该文件提取token和CA证书,动态生成带短时效(2小时)的kubeconfig,避免长期凭证泄露风险。
上层应用层:Argo CD。它不只是GitOps工具,更是集群的“状态校验器”。我们要求所有集群级组件(Cilium、Metrics Server、Cert-Manager)都通过Helm Chart定义,并存入Git仓库。Argo CD每3分钟扫描一次Git commit,一旦发现集群实际状态与Git声明不一致(比如某个DaemonSet被误删),立即自动修复。这解决了“谁动了我的集群”这个终极灵魂拷问。
3. 核心细节解析与实操要点:从证书体系到etcd灾备的硬核拆解
3.1 证书体系:别再用kubeadm alpha phase certs,手把手教你建可信CA
K8S的证书混乱是集群崩溃的第一大诱因。kubeadm默认生成的证书有效期只有1年,且所有组件共用同一CA,一旦CA私钥泄露,整个集群信任链崩塌。我们的方案是:自建三级证书体系。
第一级是根CA(Root CA),离线保存,仅用于签发中间CA;第二级是中间CA(Intermediate CA),在线运行,用于签发所有K8S组件证书;第三级是终端证书(Leaf Certificates),即kube-apiserver、etcd、kubelet等各自独立的证书。具体操作如下:
首先,用OpenSSL生成根CA(离线环境执行):
# 生成根CA私钥(2048位,AES256加密) openssl genrsa -aes256 -out ca.key.pem 2048 # 生成根CA证书(有效期10年) openssl req -x509 -new -nodes -key ca.key.pem -sha256 -days 3650 -out ca.crt.pem然后,在Master节点上生成中间CA(在线环境):
# 生成中间CA私钥 openssl genrsa -out intermediate.key.pem 2048 # 创建中间CA证书签名请求(CSR) openssl req -new -key intermediate.key.pem -out intermediate.csr.pem # 用根CA签发中间CA证书(有效期5年) openssl x509 -req -in intermediate.csr.pem -CA ca.crt.pem -CAkey ca.key.pem -CAcreateserial -out intermediate.crt.pem -days 1825 -sha256最后,为每个组件生成专属证书。以kube-apiserver为例,其CSR必须包含所有可能访问的IP和域名:
cat > apiserver-csr.json <<EOF { "CN": "kube-apiserver", "hosts": [ "127.0.0.1", "10.0.1.10", # Master节点内网IP "10.0.2.10", # 另一Master节点内网IP "10.0.3.10", # 第三Master节点内网IP "10.0.100.1", # Service CIDR网关IP "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.local", "api.internal.example.com", # 内网DNS名 "api.external.example.com" # 外网DNS名(如需) ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "Beijing", "L": "Beijing", "O": "system:masters", "OU": "Kubernetes" } ] } EOF # 生成CSR和私钥 cfssl gencert -ca=intermediate.crt.pem -ca-key=intermediate.key.pem -config=ca-config.json -profile=kubernetes apiserver-csr.json | cfssljson -bare apiserver注意:cfssl工具需提前安装,ca-config.json中必须定义"kubernetes" profile,且expiry设为8760h(1年),避免证书过期导致集群雪崩。我们曾因忘记更新kubelet客户端证书,导致所有Worker节点在证书过期日集体失联,花了3小时手动轮换。
3.2 etcd集群:高可用的基石,也是最易被忽略的单点
etcd不是数据库,而是分布式一致性键值存储。它的性能瓶颈不在CPU,而在磁盘I/O和网络延迟。我们的etcd部署严格遵循以下四条铁律:
铁律一:磁盘必须独占,且类型明确
每台Master节点挂载一块独立的200GB SSD云盘(如AWS gp3,IOPS 3000),格式化为ext4,挂载点为/data/etcd。禁止与系统盘、Docker存储目录混用。我们测试过将etcd数据目录放在系统盘,当系统日志刷盘时,etcd的fsync操作被阻塞,导致leader选举超时。
铁律二:启动参数必须精调
etcd启动命令不是简单复制粘贴,关键参数如下:
etcd \ --name infra0 \ --data-dir /data/etcd \ --initial-advertise-peer-urls https://10.0.1.10:2380 \ --listen-peer-urls https://10.0.1.10:2380 \ --listen-client-urls https://10.0.1.10:2379,https://127.0.0.1:2379 \ --advertise-client-urls https://10.0.1.10:2379 \ --initial-cluster-token etcd-cluster-1 \ --initial-cluster infra0=https://10.0.1.10:2380,infra1=https://10.0.2.10:2380,infra2=https://10.0.3.10:2380 \ --initial-cluster-state new \ --client-cert-auth \ --trusted-ca-file /etc/kubernetes/pki/etcd/ca.crt \ --cert-file /etc/kubernetes/pki/etcd/server.crt \ --key-file /etc/kubernetes/pki/etcd/server.key \ --peer-client-cert-auth \ --peer-trusted-ca-file /etc/kubernetes/pki/etcd/ca.crt \ --peer-cert-file /etc/kubernetes/pki/etcd/peer.crt \ --peer-key-file /etc/kubernetes/pki/etcd/peer.key \ --auto-compaction-retention=1 \ --quota-backend-bytes=8589934592 \ --max-snapshots=5 \ --max-wals=5其中--auto-compaction-retention=1表示每天自动压缩历史版本,--quota-backend-bytes=8GB是防爆仓红线,--max-snapshots=5限制快照数量防磁盘占满。
铁律三:备份必须自动化+异地化
我们用etcdctl snapshot save命令每30分钟生成一次快照,但关键在存储位置:快照文件不存本地,而是通过rclone同步到对象存储(如S3兼容的MinIO集群),并启用版本控制。备份脚本核心逻辑:
#!/bin/bash # 生成带时间戳的快照 etcdctl --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \ --key=/etc/kubernetes/pki/etcd/healthcheck-client.key \ snapshot save /tmp/etcd-snapshot-$(date +%Y-%m-%d_%H-%M-%S).db # 同步到异地存储 rclone copy /tmp/etcd-snapshot-*.db remote:etcd-backup/ --transfers=4 # 清理本地临时文件 rm -f /tmp/etcd-snapshot-*.db铁律四:恢复必须预演,不能只靠文档
我们每季度进行一次“灾难恢复演练”:随机选择一台Master节点,强制关机,然后从最近快照恢复etcd。恢复命令不是etcdctl snapshot restore就完事,必须加上--name、--initial-cluster、--initial-advertise-peer-urls等参数,且恢复后的etcd数据目录权限必须为etcd:etcd,否则kube-apiserver无法读取。实测发现,90%的团队在首次恢复时都会卡在权限问题上。
3.3 CNI网络:Cilium的eBPF魔法如何让网络策略真正落地
Cilium的安装看似简单(helm install cilium cilium/cilium),但生产环境必须调整23个关键参数。我们只列出最致命的5个:
--set ipam.mode=cluster-pool:强制使用ClusterPool IPAM,避免与云厂商VPC子网冲突。如果用ENI模式,在AWS上虽性能好但弹性差,扩容时需等待ENI分配。--set cluster.name=default:集群名称必须与kubeadm init时的--cluster-name一致,否则Cilium agent无法注册到集群。--set tunnel=vxlan:在公有云上必须显式指定tunnel类型。虽然Cilium支持geneve,但云厂商网络设备对vxlan兼容性更好。--set kubeProxyReplacement=strict:启用kube-proxy替换模式,这是eBPF发挥威力的前提。此时iptables规则将被清空,所有Service转发由eBPF程序接管。--set hostServices.enabled=false:禁用HostServices,避免Cilium劫持localhost流量,导致本地curl测试失败。
NetworkPolicy的编写是另一个深坑。很多人写:
apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: "deny-egress" spec: endpointSelector: matchLabels: role: frontend egress: - toEntities: - world这看似禁止frontend访问外网,但实际无效——因为Cilium的toEntities.world匹配的是“所有非集群IP”,而公有云的RDS、OSS等服务IP属于“world”,但它们的DNS域名解析可能走集群内CoreDNS,导致策略漏判。正确写法是:
egress: - toFQDNs: - matchName: "rds.amazonaws.com" - matchName: "oss-cn-beijing.aliyuncs.com" ports: - port: "3306" protocol: TCP用toFQDNs精准匹配域名,再结合port限定协议,这才是生产级策略。
4. 实操过程与核心环节实现:从Terraform初始化到Argo CD接管的全流程
4.1 Terraform基础设施搭建:用代码定义“云上地基”
Terraform不是用来创建几台虚拟机的,而是定义整个集群的“物理拓扑”。我们的main.tf包含四个核心模块:
vpc模块:创建VPC、3个子网(每个AZ一个)、NAT网关、路由表。关键约束是:子网CIDR必须互不重叠,且与Pod网络(10.244.0.0/16)、Service网络(10.96.0.0/12)完全隔离。我们曾因子网CIDR设为10.0.0.0/24,与Pod网络冲突,导致Cilium无法分配IP,集群初始化卡在CNI Ready状态。
security_group模块:定义三组安全组。Master安全组开放6443(API Server)、2379-2380(etcd)、10250(kubelet)、22(SSH);Worker安全组开放30000-32767(NodePort)、10250、22;负载均衡安全组仅开放80/443,并绑定到Ingress Controller。特别注意:Master与Worker之间必须双向开放2379-2380端口,这是etcd peer通信的生命线。
iam_role模块:为Master节点创建IAM Role,最小权限原则。必须包含:ec2:DescribeInstances(kube-controller-manager需要)、ec2:AttachVolume(PV挂载)、ssm:StartSession(远程调试)、logs:CreateLogStream(CloudWatch日志)。我们曾因遗漏ec2:DescribeInstances权限,导致Node Controller无法同步节点状态,所有Worker显示NotReady。
ec2_instance模块:创建3台Master和N台Worker。关键参数:
resource "aws_instance" "master" { count = 3 ami = data.aws_ami.ubuntu.id instance_type = "m5.2xlarge" subnet_id = element(aws_subnet.private.*.id, count.index) vpc_security_group_ids = [aws_security_group.master.id] iam_instance_profile = aws_iam_instance_profile.master.name root_block_device { volume_size = 100 volume_type = "gp3" } # 挂载独立etcd盘 ebs_block_device { device_name = "/dev/sdf" volume_size = 200 volume_type = "gp3" iops = 3000 } }这里element(aws_subnet.private.*.id, count.index)确保每台Master落在不同AZ,ebs_block_device显式挂载etcd专用盘。
4.2 kubeadm集群初始化:跳过“hello world”,直击生产痛点
kubeadm init不是终点,而是集群生命周期的起点。我们的init流程分为四步:
第一步:预检与环境准备
在每台Master节点执行:
# 关闭swap(K8S强制要求) sudo swapoff -a sudo sed -i '/ swap / s/^/#/' /etc/fstab # 加载br_netfilter模块(Cilium必需) sudo modprobe br_netfilter echo 'br_netfilter' | sudo tee -a /etc/modules # 配置sysctl参数 cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --system第二步:生成定制化kubeadm-config.yaml
核心内容:
apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.2 controlPlaneEndpoint: "api.internal.example.com:6443" # 必须是DNS名,非IP networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 etcd: external: endpoints: - https://10.0.1.10:2379 - https://10.0.2.10:2379 - https://10.0.3.10:2379 caFile: /etc/kubernetes/pki/etcd/ca.crt certFile: /etc/kubernetes/pki/etcd/client.crt keyFile: /etc/kubernetes/pki/etcd/client.key --- apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock taints: - key: node-role.kubernetes.io/control-plane effect: NoSchedule --- apiVersion: kubeadm.k8s.io/v1beta3 kind: JoinConfiguration controlPlane: localAPIEndpoint: advertiseAddress: 10.0.1.10 # 每台Master需替换为自身IP bindPort: 6443注意:controlPlaneEndpoint必须是DNS名,这是实现高可用API Server的关键——后续用NLB或ALB指向所有Master节点的6443端口,客户端通过DNS解析访问,无需感知后端IP。
第三步:执行init并处理证书
# 初始化(在第一台Master执行) sudo kubeadm init --config kubeadm-config.yaml # 将生成的admin.conf复制到$HOME/.kube/config mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # 为其他Master生成join命令(含证书) sudo kubeadm init phase upload-certs --upload-certs # 输出的--certificate-key是临时密钥,需记录第四步:Worker节点加入与Cilium部署
在Worker节点执行:
# 使用第一台Master生成的join命令(含--certificate-key) sudo kubeadm join api.internal.example.com:6443 --token abcdef.1234567890abcdef \ --discovery-token-ca-cert-hash sha256:... \ --control-plane --certificate-key 1234567890abcdef...加入后,立即部署Cilium:
helm repo add cilium https://helm.cilium.io/ helm repo update helm install cilium cilium/cilium --version 1.14.4 \ --namespace kube-system \ --set ipam.mode=cluster-pool \ --set cluster.name=default \ --set tunnel=vxlan \ --set kubeProxyReplacement=strict \ --set hostServices.enabled=false部署后,必须验证:kubectl -n kube-system get pods -l k8s-app=cilium全部Running,且cilium status显示所有节点Ready。
4.3 Argo CD集群治理:让Git成为唯一真相源
Argo CD的安装只是开始,关键是让它真正接管集群。我们的argocd-install.yaml做了三处增强:
RBAC强化:创建专用ServiceAccount
argocd-manager,绑定cluster-adminClusterRole,避免使用默认的argocd-server账户。TLS证书注入:将自建CA签发的证书挂载到argocd-server Pod,使其Web界面支持HTTPS访问:
apiVersion: apps/v1 kind: Deployment metadata: name: argocd-server spec: template: spec: containers: - name: argocd-server volumeMounts: - name: tls-certs mountPath: /app/config/tls volumes: - name: tls-certs secret: secretName: argocd-tls-secret # 该Secret包含tls.crt和tls.key- ApplicationSet控制器启用:这是实现“多集群统一管理”的关键。ApplicationSet能根据Git仓库结构自动生成Argo CD Application资源。例如,当Git中存在
clusters/prod/目录时,ApplicationSet自动创建名为prod-cluster的应用,同步clusters/prod/manifests/下的所有YAML。
集群治理的核心是“应用分层”。我们将所有组件分为三层:
Layer 0:基础设施层(Infrastructure Layer):包括Cilium、Metrics Server、Cert-Manager。它们通过Helm Chart定义,Chart包上传到私有Harbor,版本号与Git Tag绑定。
Layer 1:平台服务层(Platform Layer):包括Ingress Controller(Nginx)、日志采集(Fluent Bit)、监控(Prometheus Operator)。它们依赖Layer 0,通过Argo CD的sync-wave机制控制部署顺序(wave 1先部署Cilium,wave 2再部署Nginx)。
Layer 2:业务应用层(Application Layer):各业务线的微服务。每个业务线拥有独立Git仓库,Argo CD通过ApplicationSet自动发现并同步。
当某天需要升级Cilium版本,流程是:更新Helm Chart的values.yaml -> 提交Git -> Argo CD自动检测变更 -> 执行helm upgrade -> 验证Cilium状态 -> 全量滚动更新。整个过程无人值守,且每次变更都有Git Commit记录,审计无忧。
5. 常见问题与排查技巧实录:那些文档里找不到的“血泪教训”
5.1 网络不通的七种死法与定位口诀
K8S网络问题占集群故障的65%,我们总结出一套“三查七定”排查法:
一查:节点网络连通性
执行ping 10.0.1.10(Master IP)和ping 10.244.1.2(另一节点Pod IP)。如果前者通后者不通,说明Overlay网络故障;如果都不通,检查安全组和VPC路由。
二查:Cilium状态cilium status必须显示Kubernetes: Ok、Etcd: Ok、ClusterMesh: Disabled。若Etcd显示Failed,检查etcd证书路径和权限;若Kubernetes显示NotReady,检查kube-apiserver是否健康。
三查:Pod网络栈
进入Pod执行ip a,确认有lxcXXXXX接口(Cilium虚拟网卡);执行cat /proc/sys/net/ipv4/ip_forward,必须为1;执行iptables -t nat -L -n | grep CILIUM,应有大量Cilium规则。
七种典型死法与解法:
| 死法 | 现象 | 定位命令 | 解决方案 |
|---|---|---|---|
| 死法1:Cilium agent未启动 | `kubectl get pods -n kube-system | grep cilium` 显示CrashLoopBackOff | kubectl logs -n kube-system cilium-xxxxx |
| 死法2:etcd证书过期 | cilium status显示Etcd: Failed | openssl x509 -in /var/lib/etcd/certs/server.crt -text -noout | grep Not | 用cfssl重新签发server.crt,重启etcd |
| 死法3:NodePort不生效 | curl http://<node-ip>:30001超时 | sudo ss -tlnp | grep :30001 | 检查Worker安全组是否开放30000-32767端口 |
| 死法4:DNS解析失败 | nslookup kubernetes.default.svc.cluster.local返回NXDOMAIN | kubectl exec -it <pod> -- cat /etc/resolv.conf | 检查CoreDNS Pod是否Running,kubectl logs -n kube-system coredns-xxxxx |
| 死法5:Pod间ping不通 | kubectl exec <pod1> -- ping <pod2-ip>丢包率100% | cilium connectivity test | 若测试失败,执行cilium bugtool收集诊断包 |
| 死法6:Ingress 503错误 | curl http://myapp.example.com返回503 | kubectl get ingress查看ADDRESS是否为空 | 检查Ingress Controller Pod是否Running,Service类型是否为LoadBalancer |
| 死法7:证书轮换失败 | kubectl get nodes显示NotReady | kubectl describe node <node-name> | grep -A5 "certificate" | 检查kubelet配置--rotate-certificates=true,重启kubelet |
实操心得:遇到网络问题,永远先执行
cilium connectivity test,它会自动生成一个测试矩阵(Pod到Pod、Pod到Service、Pod到External),比手动测试高效10倍。我们曾用此命令在2分钟内定位到是Cilium的bpf_host程序加载失败,而非网络配置问题。
5.2 etcd集群脑裂的识别与急救手册
脑裂(Split-Brain)是etcd最危险的状态:两个节点都认为自己是Leader,导致数据不一致。识别信号有三:
etcdctl endpoint health显示部分节点unhealthy,但etcdctl member list仍显示所有成员为started;etcdctl endpoint status中IsLeader字段出现多个true;- kube-apiserver日志频繁出现
context deadline exceeded错误。
急救三步法:
第一步:立即隔离疑似Leader节点
找到etcdctl endpoint status中IsLeader=true且Health=false的节点,登录该节点,执行:
# 停止etcd服务 sudo systemctl stop etcd # 删除数据目录(谨慎!仅当确认该节点数据已损坏) sudo rm -rf /data/etcd/*第二步:强制重置集群
在剩余健康的节点(必须是多数派,如3节点集群中2个健康)上执行:
# 获取当前集群token etcdctl --endpoints=https://127.0.0.1:2379 member list # 强制重置为单节点集群(假设原集群名etcd-cluster-1) etcdctl --endpoints=https://127.0.0.1:2379 member remove <broken-member-id> # 重置集群状态 etcdctl --endpoints=https://127.0.0.1:2379 snapshot restore /tmp/etcd-snapshot-latest.db \ --name infra0 \ --initial-cluster infra0=https://10.0.1.10:2380 \ --initial-cluster-token etcd-cluster-1 \ --initial-advertise-peer-urls https://10.0.1.10:2380第三步:逐个恢复节点
将重置后的节点启动,然后用kubeadm join命令重新加入其他节点。注意:绝对不能同时启动多个被隔离节点,否则会再次脑裂。
血泪教训:我们曾因在脑裂后未删除损坏节点的数据目录,直接重启etcd,导致该节点以旧数据加入集群,覆盖了新数据,最终丢失了2小时的订单数据。现在所有节点都配置了
--auto-compaction-retention=1,并开启--enable-v2=false(禁用v2 API),从源头降低