一个真实的问题:单可用区集群有多脆弱?
配置前的检查
关于Multi-AZ,最值得先弄清楚的是配置边界和排错顺序。假设你把Kubernetes集群的所有节点(控制面和Worker)都放在同一个数据中心机房,也就是同一个可用区(Availability Zone,简称AZ)。某天这个机房因为光缆被挖断、电力波动或者冷却系统故障而整体下线,你的整个集群——包括API Server、调度器、所有应用Pod——全部不可用。更糟的是,即使数据有备份,恢复集群到另一个机房也需要重建网络、重新调度,可能需要几小时甚至更久。
这就是单可用区部署的致命弱点:单一故障域。在云上,可用区是物理隔离的数据中心单元,每个AZ拥有独立的电力、冷却和网络,AZ之间通过低延迟光纤连接。因此,要想让集群扛得住AZ级别的灾难,就必须把节点和关键组件分散到多个可用区,也就是跨可用区(Multi-AZ)部署。
核心概念:可用区与跨可用区高可用怎么理解?
配置前的检查
想象一下你经营一家连锁奶茶店。如果只有一个店铺,一次火灾就让生意全停。但如果你在城市的东、西、南三个方向各开一家店,其中任何一家出问题,顾客可以转向其他店。这里的“店铺”就是可用区,“连锁店系统”就是跨可用区集群。
在云上,可用区(AZ)是一组物理上隔离的数据中心,通常一个地域(Region)包含多个AZ,比如阿里云华北2(北京)可用区A、B、C,AWS us-east-1有6个AZ。每个AZ内部网络延迟极低(<1ms),AZ之间延迟通常在2~5ms,带宽充足但跨AZ流量会产生额外的网络费用(按GB计费)。
高可用(High Availability)的目标是让系统在部分组件失效时仍能正常服务。对于Kubernetes集群,高可用意味着:控制面(Control Plane)的etcd、API Server、Controller Manager、Scheduler至少有2个副本分布在不同的AZ;工作节点(Worker Nodes)也分散部署,并且通过调度规则让应用Pod分布在多个AZ上。
进阶阅读:此处可内链到“Multi-AZ性能优化”指南。
为什么要跨可用区设计Kubernetes网络拓扑?与Multi-AZ
配置前的检查
K8s网络是集群通信的血管,跨AZ的网络拓扑设计直接决定了集群的可用性和性能。设计核心目标有三个:
- 控制面高可用:etcd集群跨AZ部署,保证Leader故障时能快速选举新Leader;API Server负载均衡到多个AZ的实例,避免单点。
- 工作负载高可用:通过PodAntiAffinity将同一Deployment的Pod分布在多个AZ上,确保一个AZ宕机时其他AZ的Pod仍能处理流量。
- 网络通信不因AZ故障受阻:Service的Cluster IP、Ingress控制器、DNS(CoreDNS)等组件也需要多AZ部署,避免单AZ失效导致服务发现或流量入口中断。
跨AZ网络拓扑的关键设计要素
1. 控制面节点的AZ分布
如果你的云厂商支持托管K8s(如阿里云ACK、AWS EKS、GKE),控制面由云厂商负责高可用,通常自动分布在3个AZ上。如果是自建集群,务必在至少3个AZ中各部署一台控制面节点,并使用外部负载均衡器(如SLB、NLB)将流量分发到这些节点。etcd成员也要跨AZ分布,推荐奇数个成员(3或5),这样少数AZ故障时etcd仍然可用。
2. Worker节点的AZ分布与Pod调度
创建工作节点组时,在每个AZ中创建数量相同的节点(比如每个AZ 3台,共9台)。然后利用PodAntiAffinity规则,让Pod尽量散布到不同AZ:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- my-app
topologyKey: topology.kubernetes.io/zone
topology.kubernetes.io/zone是节点自动带有的标签,表示节点所在的AZ。通过这个规则,K8s会尽量把Pod调度到不同AZ的节点上。
3. 网络插件(CNI)的跨AZ通信
常见的CNI如Calico、Flannel、Cilium都支持跨AZ通信,底层依赖云厂商的VPC网络。每个节点上的Pod IP通常从节点所在的VPC子网中分配。跨AZ的Pod通信会经过云厂商的底层路由器,延迟略高但带宽充足。你需要确保:
- 每个AZ中至少有一个节点子网(/24或更大),不同AZ的子网CIDR不重叠。
- 节点安全组允许来自其他AZ子网的Pod CIDR流量(通常是全部内网允许)。
- 如果使用Calico的IPIP或VXLAN模式,注意隧道封装会增加CPU消耗和延迟;在云VPC环境下,建议使用Direct Routing模式(如Calico的“云原生”模式或Cilium的eBPF直接路由),避免二次封装。
4. Service与Ingress的跨AZ负载均衡
Service(ClusterIP类型)通过kube-proxy在节点上做DNAT,天然可以在任何节点上接收流量并转发到后端Pod,即使Pod在其他AZ。但为了最优路径,建议使Service的externalTrafficPolicy: Local,这样外部流量只会到达有健康Pod的节点,避免跨AZ的SNAT和额外跳转。不过这会带来流量不均衡的问题,需要结合云厂商的负载均衡器使用。
对于Ingress,推荐在每个AZ部署一个Ingress Controller的Pod(通过PodAntiAffinity或节点亲和性),并在前面挂载云厂商的SLB/ALB(负载均衡器)。SLB/ALB通常支持跨AZ分发流量,并且可以配置健康检查,只把流量发给健康的Ingress Pod。
5. CoreDNS与集群关键组件的多AZ部署
CoreDNS负责服务发现,如果所有CoreDNS Pod都在同一个AZ,该AZ故障时整个集群域名解析中断。将CoreDNS的副本数设置为至少等于AZ数量(比如3),并添加topologySpreadConstraints让Pod均匀分布在各个AZ上:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
k8s-app: kube-dns
想继续深入:此处可内链到“Multi-AZ优化清单”文章。
跨AZ部署的隐藏成本与权衡与Multi-AZ
我的处理经验
理想很丰满,实际运维时需要面对几个现实问题:
- 跨AZ流量费用:云厂商对流出节点的流量收费,同AZ内免费,跨AZ按量计费(通常0.1~0.2元/GB)。如果应用Pod之间频繁跨AZ通信,账单可能激增。解决方案:对延迟不敏感且流量大的批处理任务尽量调度到同AZ,通过节点亲和性控制。
- 延迟增加:同AZ延迟1ms以内,跨AZ可能2~5ms。对于Redis、数据库等高QPS的中间件,每增加2ms可能导致P99延迟明显上升。这类中间件建议同AZ部署主节点,跨AZ部署从节点做灾备。
- 网络带宽不对称:部分云厂商的AZ间带宽有限制,大流量场景需要提前测试。
因此,并非所有工作负载都需要跨AZ。对于非关键应用或测试环境,单AZ足够。但对于生产级的核心业务,尤其是需要SLA保障的服务,跨AZ是必需品。
关联教程:此处可内链到“Multi-AZ部署与验证”内容。
总结:从零到一搭建跨AZ K8s集群的最佳实践
容易忽略的细节
如果你刚开始规划集群,可以按以下步骤走:
- 选择一个云地域,至少启用3个可用区(如果云厂商提供,有些只提供2个)。
- 创建VPC,并在每个AZ中创建至少一个子网(用于节点)和一个额外的Pod子网(如果CNI需要)。
- 部署托管K8s集群(推荐),或者自建控制面并跨AZ分布。
- 创建节点组,每个AZ节点数量相同,并确保节点带有正确的
topology.kubernetes.io/zone标签。 - 安装CNI时选择直接路由模式(如Calico VXLAN on cloud routing 或 Cilium native routing),避免过度封装。
- 为所有有状态工作负载(数据库、缓存)单独设计跨AZ策略:主从跨AZ或同AZ主+跨AZ备份。
- 使用PodTopologySpreadConstraints和PodAntiAffinity控制Pod分布。
- 监控跨AZ流量成本和延迟,设置告警。
跨AZ高可用不是免费午餐,但它是应对AZ级灾难最有效的手段。理解背后的网络拓扑设计,才能避免“跨AZ了但根本没用好”的尴尬。按这个顺序复查,Multi-AZ遇到异常时也更容易定位。
延伸阅读
