news 2026/10/20 3:49:00

K8s集群底座精讲:etcd集群部署与Pod通信实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
K8s集群底座精讲:etcd集群部署与Pod通信实现

最近在搭Kubernetes集群,第一件事就是etcd。很多人一上来就装kubeadm、装kubelet,结果跑到kube-apiserver那一步发现连不上etcd,或者集群起来了但Pod跨节点通信一通乱像。其实只要把etcd的部署逻辑和Pod网络模型这两块吃透,整个K8s的底座就稳了。这篇文章就围绕“etcd集群部署”和“Pod通信实现”两件事展开,把我的实操过程、参数选择、踩坑记录全部写出来,适合正在自己搭集群、想搞懂底层原理的人。

1. 先把etcd在Kubernetes里的位置搞清楚

1.1 它不是数据库,而是整个集群的“状态大脑”

etcd在Kubernetes里的定位,一句话概括:集群所有状态的唯一事实来源。你执行kubectl get nodes,这个结果不是kubelet上报给API Server然后API Server自己记住的,而是API Server从etcd里读出来的。节点信息、Pod信息、ConfigMap、Secret、Service、Deployment期望副本数,全都以key-value形式存在etcd里。

所以etcd一旦挂了,整个Kubernetes控制面就瘫痪了。工作节点上的Pod可能还在跑,但你没法调度、没法滚动更新、没法扩缩容,kubectl命令全部超时。这就是为什么etcd部署要当成整个集群里最重的组件来对待。

它和传统数据库的区别也很关键:etcd是给分布式系统存元数据的,不是给你存业务大对象的。它用Raft协议做共识,保证了多副本之间的强一致性;它提供Watch机制,客户端可以订阅某个key的变化,Kubernetes的控制器全靠这个机制来感知状态变化;它还有Lease机制,用于节点心跳、控制器选举这些场景。Redis也能存数据,但强一致性和Watch能力远不如etcd适合这种场景。

1.2 为什么Kubernetes选etcd而不是别的存储

我经常被问:为什么不直接用MySQL或者Redis?原因有三点,这三点其实决定了整个Kubernetes控制面的工作方式。

第一,强一致。Kubernetes里的许多操作必须在“不变量”下进行,比如同一个Pod不能被调度到两个节点。etcd通过Raft保证写入必须经过多数派确认,客户端读到的数据一定是已经达成共识的数据,不会出现脑裂后的数据分叉。

第二,Watch机制。Kubernetes的各个控制器(Deployment Controller、Scheduler等)不是轮询数据库,而是通过kubectl get --watch这类机制,底层就是etcd的Watch。你创建一个Deployment,Scheduler马上收到新增Pod的事件,立刻开始调度。如果是关系型数据库,要么高频轮询,要么自己实现消息通知,复杂度高得多。

第三,轻量级且专门为K/V场景优化。etcd的底层存储是MVCC多版本并发控制,每个key有revision,支持历史版本查询,这让Kubernetes可以基于resourceVersion做增量同步和故障恢复。

1.3 部署之前先想清楚的事

动手之前,有几个问题必须先回答。如果不提前规划,后面大概率要返工。

第一个问题是版本兼容性。Kubernetes不同版本对etcd版本有明确要求,比如较新的K8s版本基本要求etcd 3.5.x。你去查官方文档的“etcd cluster compatibility”表,盯着大版本匹配就行,别搞个3.4的etcd配新版本Kubernetes,虽然能启动但API兼容性容易出问题。

第二个问题是单节点还是集群。学习环境、测试环境单节点完全够了,但生产环境我强烈建议至少三节点,etcd集群需要奇数节点才能容忍故障:3节点允许挂1个,5节点允许挂2个。别用偶数节点,多一个副本既浪费资源,容错能力还没有提升。

第三个问题是网络规划。etcd节点之间的通信端口是2380,客户端通信端口是2379。如果你把etcd部署在Kubernetes集群之外,要保证API Server能访问到2379,etcd节点之间能互相访问2380。VPC、安全组、防火墙规则都要提前放行。

最后一个问题:要不要做TLS?我的答案很直接:必须做。生产环境裸奔etcd等于把集群管理员权限扔到网上。后面我会专门讲证书怎么生成。

2. 从零部署一套etcd集群

2.1 硬件与版本:别在第一步踩坑

先说硬件建议。etcd对CPU不太敏感,但极度依赖磁盘延迟。它的每一次写入都需要刷盘,磁盘性能直接影响整个Kubernetes控制面的请求延迟。生产环境请用SSD,本地盘优先,不要用网络盘,网络盘的延迟抖动会让etcd的fsync耗时飙升,进而引发选举超时。

内存建议给2GB以上,etcd会缓存一部分key的索引。磁盘空间按照集群规模估,每个key的value都比较小,但revision历史会占空间,建议给etcd的数据目录单独分一块盘,比如20GB起步。

版本这里我以etcd v3.5.x系列为例,后面的命令都是在这个版本下验证过的。安装方式最简单的是下载官方二进制,从GitHub Releases页把etcd-v3.5.x-linux-amd64.tar.gz拉下来解压即可,包里面有etcd和etcdctl两个可执行文件。记得把这两个文件放到/usr/local/bin/下,方便全局调用。

tar -xzf etcd-v3.5.15-linux-amd64.tar.gz sudo mv etcd-v3.5.15-linux-amd64/etcd /usr/local/bin/ sudo mv etcd-v3.5.15-linux-amd64/etcdctl /usr/local/bin/ etcd --version etcdctl version

注意etcdctl在3.4之前默认走v2 API,3.4之后默认v3 API,但为了保险,使用前我习惯加一个环境变量ETCDCTL_API=3。后面所有命令我都默认这个版本。

2.2 单节点快速跑起来:用于学习和验证

要快速感受etcd,单节点是最直接的。新建一个数据目录,然后指定listen地址启动:

mkdir -p /var/lib/etcd etcd --name infra0 \ --data-dir /var/lib/etcd \ --listen-client-urls http://0.0.0.0:2379 \ --advertise-client-urls http://127.0.0.1:2379 \ --listen-peer-urls http://0.0.0.0:2380 \ --initial-advertise-peer-urls http://127.0.0.1:2380 \ --initial-cluster infra0=http://127.0.0.1:2380 \ --initial-cluster-token etcd-cluster-1 \ --initial-cluster-state new

这里的参数解释一下:

  • --listen-client-urls:etcd对外提供客户端服务的监听地址,API Server会连这个端口。
  • --advertise-client-urls:告诉客户端“你应该访问哪个地址”,当有多个网卡或需要NAT时,这个地址必须能被客户端路由到。
  • --listen-peer-urls:etcd节点之间互通、跑Raft协议用的端口。
  • --initial-cluster:集群成员的初始列表,每个成员对应一个peer URL。单节点时只有一个。
  • --initial-cluster-state new:表示这是新建集群,而不是加入已有集群。如果不小心写成existing,且本地没有快照,节点会启动失败。

启动后开另一个终端验证:

export ETCDCTL_API=3 etcdctl member list etcdctl endpoint health --cluster etcdctl put demo-key demo-value etcdctl get demo-key

能正常返回就算成功。单节点etcd对Kubernetes学习环境完全够用,你直接用这个etcd,让kube-apiserver通过--etcd-servers=http://127.0.0.1:2379连上来就行。

2.3 三节点静态集群:生产环境的标准姿势

生产环境我建议用静态集群方式,就是手动在每台机器上指定所有节点成员信息。虽然etcd也支持动态发现服务,但静态配置最简单、最可控、没有额外依赖。

假设有三台服务器,IP分别是10.0.1.11、10.0.1.12、10.0.1.13。每个节点上的启动参数类似下面。以第一个节点为例:

etcd --name etcd-1 \ --data-dir /var/lib/etcd \ --listen-client-urls https://0.0.0.0:2379 \ --advertise-client-urls https://10.0.1.11:2379 \ --listen-peer-urls https://0.0.0.0:2380 \ --initial-advertise-peer-urls https://10.0.1.11:2380 \ --initial-cluster etcd-1=https://10.0.1.11:2380,etcd-2=https://10.0.1.12:2380,etcd-3=https://10.0.1.13:2380 \ --initial-cluster-token etcd-cluster-prod \ --initial-cluster-state new \ --cert-file=/etc/etcd/pki/server.crt \ --key-file=/etc/etcd/pki/server.key \ --peer-cert-file=/etc/etcd/pki/peer.crt \ --peer-key-file=/etc/etcd/pki/peer.key \ --enable-v2=false

第2个、第3个节点只需要把name、advertise-client-urls、initial-advertise-peer-urls换成自己的IP即可,其余集群列表保持一致。三个节点都启动后,任何一个节点上执行:

etcdctl member list etcdctl endpoint health --cluster

就能看到三个成员都处于healthy状态。

这里有个细节:如果某个节点启动后始终报“failed to connect to peer”,先别怀疑网络,先看--initial-cluster里每个peer URL的IP是否和该节点--initial-advertise-peer-urls完全一致。首字母都必须精确匹配,不能一个写内网IP一个写公网IP,否则握手阶段就失败。

2.4 证书与安全策略:TLS不是可选而是必选

没有TLS的etcd,只要网络可达,任何人都可以用etcdctl直接读写集群数据,所有Secret全部明文拿到。所以生产环境一定要做双向TLS。

最简单的方式是用官方提供的cfssl工具生成自签证书。过程分为三步:创建CA、生成服务端证书、生成客户端证书。我这里不展开所有配置文件,只说核心命令流程。

# 生成CA私钥和证书 cfssl gencert -initca ca-csr.json | cfssljson -bare ca # 生成etcd服务端和peer证书,这里要包含节点IP作为SAN cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \ -profile=server server-csr.json | cfssljson -bare server # 生成客户端证书 cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \ -profile=client client-csr.json | cfssljson -bare client

CSR文件里的hosts字段一定要填上所有etcd节点的IP和域名,否则节点间用IP访问时TLS握手会报证书不匹配。

之后etcd启动参数里加上前面看到的--cert-file、--key-file、--peer-cert-file、--peer-key-file,同时建议加上--client-cert-auth=true和--peer-client-cert-auth=true,要求客户端和peer也提供证书。这样再用etcdctl时,必须指定CA、客户端证书和私钥:

etcdctl --endpoints=https://10.0.1.11:2379 \ --cacert=/etc/etcd/pki/ca.pem \ --cert=/etc/etcd/pki/client.pem \ --key=/etc/etcd/pki/client-key.pem \ endpoint health --cluster

这个步骤比较繁琐,但一次配好后面省心。Kubernetes的kube-apiserver连接etcd时也会要求用到etcd客户端证书,后面配置API Server时刚好能复用同一套CA签发的新证书。

2.5 集群健康检查与常用命令

部署完etcd,建议做一次完整体检。常用命令我整理一下:

# 查看成员状态,包括leader信息 etcdctl endpoint status --cluster -w table # 查看所有endpoint的健康状态 etcdctl endpoint health --cluster # 检查告警(比如空间不足时会有告警) etcdctl alarm list # 查看当前存储的key etcdctl get / --prefix --keys-only | head -20 # 查看当前配置的quota和当前存储使用 etcdctl --endpoints=... status --cluster -w json | jq

生产环境我建议把这些检查做成一个脚本或者监控项,写入Prometheus的blackbox或者etcd exporter。etcd本身暴露/metrics端点,里面有etcd_server_leader_changes_seen_total、etcd_server_heartbeat_send_failures_total等关键指标,接上Prometheus之后可以提前发现集群隐患。

3. etcd在K8s中的使用细节

3.1 API Server如何和etcd交互

Kubernetes里只有kube-apiserver会直接连接etcd,其他组件一律通过API Server访问数据。这种设计把etcd的访问收敛到唯一入口,方便做权限控制、审计、缓存。

API Server启动配置里与etcd相关的核心参数是--etcd-servers。如果你用二进制方式部署,在kube-apiserver的启动参数里写上etcd地址即可;如果你用kubeadm部署,这些参数被封装在静态Pod的yaml里,通常位于/etc/kubernetes/manifests/kube-apiserver.yaml。

API Server与etcd之间走的是etcd v3的gRPC接口,所有资源对象以JSON或Protocol Buffers格式存入etcd。当客户端执行kubectl get pods时,API Server先查自己的缓存(cache),缓存未命中才去etcd读,读完后会Watch对应key,保证本地缓存与etcd一致。

API Server还会用etcd的Lease机制做节点心跳。每个Node对应对应一个Lease对象,kubelet周期性更新Lease,控制器根据Lease超时判断节点是否失联。这个机制里etcd的超时阈值和API Server的缓存刷新策略需要配合好,不然会出现节点明明活着,控制器却认为它失联的情况,后面我会在故障排查里讲。

3.2 数据模型:从key到value的实际结构

etcd中的Kubernetes数据都挂在/registry前缀下,比如:

  • /registry/pods/default/nginx-xxxx:某个Pod的完整定义
  • /registry/deployments/default/nginx:Deployment资源
  • /registry/nodes/192.168.1.20:节点信息
  • /registry/services/endpoints/default/nginx:Endpoints

你可以直接登录etcd,用etcdctl读到这些资源的原始JSON。但要小心,只读没问题,写操作千万不要做,因为你手工写入的格式和字段不一定符合API Server预期,容易弄坏集群。

我实际排查过一个诡异问题:某个Deployment状态一直不对,后来发现是有人直接操作etcd,误删了对应的/registry/deployments/...key。这种做法极其危险,所以我给团队的铁律是:etcd里的Kubernetes数据只能看,不能动,任何修改必须走kubectl。

3.3 备份、恢复与碎片整理

etcd的运维中,备份和恢复是必须练熟的技能,因为控制面的数据一旦丢失,整个集群等于从零开始。

备份用etcdctl snapshot save:

export ETCDCTL_API=3 etcdctl --endpoints=https://10.0.1.11:2379 \ --cacert=/etc/etcd/pki/ca.pem \ --cert=/etc/etcd/pki/client.pem \ --key=/etc/etcd/pki/client-key.pem \ snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db

恢复流程我之前写过多次,这里再强调一遍:准备一个新的空数据目录,在新目录上执行etcdctl snapshot restore,指定--name、--data-dir和--initial-cluster,然后把启动参数里的数据目录指向这个新目录。恢复操作不能在原etcd运行状态下直接覆盖数据目录,必须先停etcd。

还有一点很容易忽略:etcd的MVCC机制会让历史版本无限累积,长期运行的集群,哪怕数据量不大,压缩前和压缩后的磁盘占用差距也很明显。所以要定期清理历史版本:

# 精简所有历史版本(保留当前最新版本) etcdctl compact $(etcdctl endpoint status -w json | jq -r '.[0].Status.raftAppliedIndex')

压缩之后磁盘空间不会立刻释放,还需要执行etcdctl defrag。这个操作比较重,建议在业务低峰离线或逐个节点执行,因为defrag期间该节点的读写性能会明显下降。

4. Pod通信的核心原理与实现路径

4.1 单节点Pod为什么默认就能互通

先想一个问题:一个Kubernetes节点上有两个Pod,它们为什么能互相ping通?因为Kubernetes要求每个Pod必须有一个独立的IP,并且所有Pod之间有直接路由。

这个“独立IP”是怎么来的?每个Pod启动时,Kubelet先创建一个pause容器,这个容器的作用是持有网络命名空间。Pod里后续创建的普通容器都通过JoinNetworkNamespace加入到pause的网络命名空间里,所以Pod内所有容器共享同一个IP和端口空间,而不同Pod之间则拥有独立的IP。

pause容器创建时会分配一个veth pair。veth pair可以理解为一根虚拟网线,一头在容器内(eth0),另一头在宿主机上(比如veth0abc),宿主机这一头插入一个网桥或者路由接口。如果使用的CNI是bridge模式,宿主机上会有一个cni0网桥,所有Pod的veth宿主机端都连到这个网桥上,于是同一节点内的Pod天然二层互通。

从流量路径看,Pod A到同节点Pod B的包大致是这样的:

  1. Pod A的eth0发出数据包,源IP是Pod A的IP。
  2. 包通过veth pair到达宿主机cni0网桥。
  3. 网桥查到目标IP对应的MAC地址就是Pod B的veth宿主机端,直接转发过去。
  4. 包进入Pod B的veth,到达Pod B的eth0。

这一过程没有经过任何三层的路由和封装,所以延迟很小。

4.2 跨节点通信的三种典型实现

跨节点Pod通信稍微复杂一些。核心问题在于:每个节点有自己的Pod网段,比如节点A是10.244.1.0/24,节点B是10.244.2.0/24,节点A上IP为10.244.2.5的Pod在节点B上,节点A怎么知道这个包该发到节点B?

Kubernetes本身不解决这个问题,它通过CNI规范把网络实现交给插件。常见做法有以下几种:

第一种是overlay网络,代表是Flannel的VXLAN模式和Calico的IPIP/VXLAN模式。在VXLAN模式下,跨节点的Pod报文会被封装在UDP包中,外层IP是源节点到目标节点的物理IP,内层是Pod IP之间的原始报文。目标节点收到后解封装,再交给目标Pod。这样对底层网络没有任何要求,不需要配置路由,普通三层网络也能跑。代价是多了封装解封装的开销,而且需要额外配置VXLAN相关的路由和ARP表。

第二种是host-gw模式,Flannel的host-gw就是典型。它不封装报文,而是直接在宿主机路由表里写入到达各节点Pod网段的路由,下一跳指向物理IP。数据包直接走物理网络转发,性能好。但要求所有节点在同一个二层网络内,因为下一跳必须能通过ARP解析到MAC地址,跨VXLAN这种场景就不适用。

第三种是BGP模式,Calico默认支持。Calico在每个节点上跑一个BGP组件,与其他节点的BGP对等体交换路由,最终在全局路由表里就能看到所有Pod网段的路由。BGP模式在网络规模大、需要精细路由控制时很合适,但BGP本身对网络基础设施有一定要求,比如交换机可能要支持BGP或者需要配置Route Reflector。

4.3 CNI插件怎么被调用

要理解CNI插件,必须先知道它是个可执行文件。Kubelet创建Pod时,会根据Pod配置的注解和集群的CNI配置目录/etc/cni/net.d找到对应的CNI配置文件,然后调用/opt/cni/bin/下的插件二进制。

CNI插件的工作流大概是这样的:

  1. Kubelet请求CRI运行时(比如containerd)创建Pod sandbox。
  2. 运行时根据CNI配置调用插件,传入一个CNI_ARGS环境变量,包含网络命名空间路径、Pod名称等。
  3. 插件执行ADD操作,创建/移动网络接口到Pod的命名空间,分配IP地址,配置路由和iptables。
  4. 执行完成后,插件把结果(IP地址、MAC、路由等)以JSON格式返回给运行时,运行时再把它保存为该Pod的网络状态。

因此,当你想换CNI插件时,只需要替换/etc/cni/net.d下配置文件和/opt/cni/bin下插件,然后重建Pod即可。用kubeadm部署集群时,默认不会有CNI配置,所以集群初始化后节点是NotReady状态,装上CNI插件后才会变成Ready。

5. 实操:部署CNI并打通跨节点Pod通信

5.1 准备两个节点的Kubernetes环境

我这里以两个节点为例,假设已经用kubeadm初始化了一个控制平面节点和一个工作节点,Pod网段设为10.244.0.0/16,Service网段用默认的10.96.0.0/12。初始化命令大致是:

kubeadm init --pod-network-cidr=10.244.0.0/16 --control-plane-endpoint=10.0.1.11

初始化完成后,把工作节点的kubelet配置也准备好,加入集群。此时由于没有CNI,两个节点的状态应该是NotReady:

kubectl get nodes

你会看到STATUS列是NotReady,这是正常现象。Pod网络没起来,节点健康检查无法通过。

这里顺便说一句:Pod网段的选择会直接影响后续网络方案。如果你计划用Flannel的默认网段10.244.0.0/16,那就别乱改;如果你用Calico,默认通常是192.168.0.0/16。两种方案不要混用,否则路由冲突排查到天亮。

5.2 安装并配置Flannel作为CNI

Flannel的部署非常简单,其官方manifests文件里包含了一个DaemonSet,会通过DaemonSet在每个节点上运行flanneld进程。

kubectl apply -f kube-flannel.yml

部署后稍等片刻,查看Pod状态:

kubectl get pods -n kube-flannel -o wide

当所有flannel Pod都处于Running后,再查看节点,应该全部变成Ready:

kubectl get nodes

Flannel默认使用的backend是VXLAN。每个节点上会创建一个名为flannel.1的VXLAN设备,并维护一张有关节点Pod网段的路由表。你可以查看其中一个节点的路由:

ip route ip -d link show flannel.1

路由表里通常有两条关键路由:

  • 本节点Pod网段通过cni0这个本地网桥直连。
  • 其他节点的Pod网段通过flannel.1设备,下一跳是对端节点的VTEP地址。

这里要特别注意一个概念:VXLAN不是纯隧道,它依赖目标节点上的VTEP地址(一般是该节点物理网卡IP)来构建外层UDP头。所以Flannel要求集群节点间UDP 8472端口要放通,否则封装包根本送不过去。

5.3 验证Pod到Pod、Pod到Service的连通性

网络通了之后,我们用实际流量验证一次。

先创建两个测试Pod,分别调度到不同节点:

kubectl create deployment nettest-a --image=busybox --replicas=1 -- sleep 3600 kubectl create deployment nettest-b --image=busybox --replicas=1 -- sleep 3600 # 分别查看Pod所在节点和IP kubectl get pods -o wide

假设nettest-a的IP是10.244.1.3,在节点1上;nettest-b的IP是10.244.2.4,在节点2上。进入nettest-a执行ping:

kubectl exec -it deploy/nettest-a -- ping -c 3 10.244.2.4

如果能通,跨节点Pod通信已经OK。再看Pod到Service的通信。给nettest-b创建一个ClusterIP类型的Service:

kubectl expose deployment nettest-b --port=80 --target-port=80 kubectl get svc nettest-b

进入nettest-a访问Service IP:

kubectl exec -it deploy/nettest-a -- wget -qO- http://<ServiceIP>:80

如果能curl通,说明kube-proxy的规则和IPVS/iptables转发链是正常的。

这里我用的是wget,因为busybox镜像里没带curl。这个细节也值得记住:在容器里排查网络时,先确认镜像里有哪个工具,没有就换镜像或者用kubectl debug。

5.4 Calico模式切换:让流量路径更可控

如果网络规模更大、对NetworkPolicy有要求,我一般用Calico。Calico的部署量级比Flannel重一些,但它支持BGP路由和丰富的网络策略。

默认Calico使用IPIP模式,加密和性能都不是最优。如果你的节点在同一二层网络,建议改成VXLAN或者BGP模式。改法很简单,编辑Calico的安装配置环境变量,把CALICO_IPV4POOL_IPIP设置为Never,然后重新应用即可。切换后,节点上会多出类似tunl0或cali+的接口,路由表也会出现更精细的分配路由。

选择Flannel还是Calico,我的经验是:

  • 小集群、快速上手、对网络策略没有强需求:选Flannel。
  • 需要精细的NetworkPolicy、集群规模较大、想在网络层做更多控制:选Calico。
  • 底层网络已经打通二层,对性能敏感:可以考虑Flannel host-gw或Calico BGP。

6. etcd与网络故障排查实录

6.1 etcd集群健康状况排查

遇到Kubernetes控制面表现异常,我第一个查的就是etcd。命令固定三件套:

etcdctl endpoint health --cluster -w table etcdctl endpoint status --cluster -w table etcdctl member list

如果health输出里某个endpoint是false,先看它的日志。常见错误包括:

  • apply request took too long:磁盘写入慢,报错节点可能磁盘IO已达瓶颈。
  • failed to send out heartbeat on time:网络抖动,节点间心跳延迟过高。
  • clock difference:节点系统时间不同步,Raft协议对时间敏感,这个必须用NTP对齐。

我遇到过最经典的一个坑:三个节点的etcd,有一个节点时钟比另外两个慢了1秒,结果这个节点频繁发起选举,集群一会儿可用一会儿不可用。排查了很久才发现是NTP服务没配置好。所以etcd节点上线时,第一件事就是确认clock同步,别让时间问题浪费半天时间。

6.2 etcd选主抖动

如果状态里看到leader频繁变化,基本就是网络或磁盘的问题。第一步看日志:

journalctl -u etcd -f

出现leader changed、election相关高频日志,优先检查节点间2380端口的网络质量。可以用ping和mtr确认丢包率,如果跨机房节点,还要关注防火墙是否限制了ICMP或UDP。

另一个隐蔽问题是CPU抢占。如果节点上还有其他高负载进程,etcd的Raft心跳线程被抢占,同样会导致选主超时。生产环境我建议把etcd单独部署在专用节点上,或用cgroup限制其他进程资源。

6.3 Pod网络不通的排查顺序

交叉节点Pod不通是出现频率最高的故障。我的排查顺序是固定的,每一步都能快速缩小范围:

  1. 先看路由表。在源节点执行ip route get <目标Pod IP>,确认有路由且下一跳正确。如果显示unroutable,说明CNI插件没有写入路由,优先检查flanneld/calico进程状态。
  2. 再看ARP表。如果下一跳是正确的VTEP或目标节点IP,但ARP解析不到MAC,可能是节点间二层隔离或者ARP被防火墙拦截。
  3. 抓包确认封装。在源节点抓tcpdump -i any host <目标Pod IP> -nn,看有没有发出去。在目标节点抓tcpdump -i flannel.1,看有没有收到。如果源发出去但目标收不到,大概率是VXLAN UDP端口被拦截;如果目标收到但回包异常,检查iptables和路由表。
  4. 检查节点防火墙。很多云主机默认的firewalld/iptables规则会拦截VXLAN、BGP等端口。我建议CNI要求的端口在部署前就加入白名单,而不是出问题后再放行,后者会打断现有长连接,造成更复杂的现场。

我在排查过程中还经常遇到一个误区:Pod能ping通但TCP建连失败。这通常不是CNI的问题,而是目标Pod内的应用没有监听预期端口,或者kube-proxy的规则把流量转发到了一个不存在的Endpoint。此时kubectl get endpoints <service-name>看一下Endpoints列表是否为空,基本能确认。

6.4 经验总结:这些坑我能避,你也能

把etcd和Pod通信相关的坑整理成一张速查表,方便你在现场对照:

现象大概率原因排查/解决建议
etcd节点状态不健康磁盘IO慢、网络丢包、时钟漂移检查fsync延迟、2380端口连通性、NTP状态
API Server报etcd连接超时客户端证书过期/API版本不兼容检查kube-apiserver日志,确认证书有效期
节点NotReady,flannel Pod正常Pod网段CIDR与配置不一致核对kubeadm init参数和CNI配置里的IP网段
跨节点Pod不通但同节点通VXLAN端口被防火墙拦截放行UDP 8472或IPIP协议号4
Pod能ping通但Service不通kube-proxy规则或Endpoints为空检查kubectl get endpoints及IPVS规则
etcd数据目录越来越大历史revision未压缩定期compact + defrag
节点间心跳延迟高CPU被抢占或网络拥塞资源隔离,网络质量监控

还有几个细节值得单独说:

证书过期问题,我至少见过三次。etcd证书有效期通常一年或更短,证书快过期时etcd不会直接拒绝连接,而是报TLS握手错误,日志里会出现remote error: tls: bad certificate。线上一定要给证书加监控,提前30天告警。

另外,etcd和CNI的网络配置都不要随意超时重启。etcd重启需要时间做WAL回放和数据同步,如果等待时间不够就用etcdctl endpoint health判断恢复状态,别着急动它。CNI插件重启会重写网络规则,正在运行的Pod流量会中断,尽量在低峰期操作。

最后再分享一个小习惯:每次部署etcd或更换CNI方案前,我都会先做一次 etcd snapshot,然后把/etc/cni/net.d和/run/flannel/subnet.env这类关键文件用tar打一份备份。这个习惯帮我避免过很多次回滚时的痛苦。Kubernetes集群不复杂,但底座(etcd)和血管(Pod网络)必须慎之又慎,只要这两块稳了,上层应用怎么折腾都不会翻车。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 3:48:43

C#教育平台分片上传与文件夹树形控件封装实战

如果你在做教育平台相关的项目&#xff0c;大概率迟早会遇到这个需求&#xff1a;老师要把整个课程文件夹一次性扔到网页里&#xff0c;里面混着几十个视频、PPT、作业包&#xff0c;后端用的是 C#&#xff0c;前端还得实时展示一棵带上传状态的文件夹树。我最初以为这事不难&a…

作者头像 李华
网站建设 2026/10/20 3:48:43

Flutter for OpenHarmony适配实战:vector_math数学库核心能力与复杂变换方案

最近把主工程切到 Flutter for OpenHarmony 上&#xff0c;第一件让我头疼的事就是数学依赖。你写普通页面根本感觉不到&#xff0c;可一旦涉及自定义布局、动画曲线、2D/3D 渲染&#xff0c;一套靠谱的高性能数学库就成了刚需。Vector_math 这个包在 Flutter 生态里一直很低调…

作者头像 李华
网站建设 2026/10/20 3:47:58

大模型上下文模式全解析:从原理到工程实现

“context-mode”这个词&#xff0c;在大多数LLM应用开发者眼里&#xff0c;第一反应是“上下文模式”&#xff0c;但你真的去翻开源项目里的代码时&#xff0c;会发现它往往是一个被低估的模块。我见过太多团队把精力花在提示词工程上&#xff0c;结果一上生产就翻车在上下文管…

作者头像 李华
网站建设 2026/10/20 3:47:39

从“rea”到完整前端应用:React+TypeScript+Vite实战全记录

最近接了个前端小项目&#xff0c;需求文档极其简洁&#xff0c;通篇只有三个字符&#xff1a;rea。没有设计稿&#xff0c;没有交互说明&#xff0c;没有后端接口文档&#xff0c;连项目启动命令都没写。就这么一个省到极致的代号&#xff0c;要求我把它做成一个能跑、能看、能…

作者头像 李华
网站建设 2026/10/20 3:47:32

输送机物料识别数据集:带标注信息与三种格式的目标检测实战解析

咱们搞工业视觉的人&#xff0c;听到“带标注数据集”这六个字&#xff0c;通常眼睛会先亮一下。这年头&#xff0c;模型算法开源的一抓一大把&#xff0c;真正卡脖子的往往是干净、规范、覆盖场景足够真实的标注数据。所以看到这套“输送机上物体识别数据集”的时候&#xff0…

作者头像 李华
网站建设 2026/10/20 3:47:22

基于微信小程序的校园外卖直送平台 骑手配送系统 商家

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华