跨可用区高可用需三要素:一用topologySpreadConstraints按zone打散Pod;二健康检查须验证DB/Redis/下游gRPC等依赖;三配置热更新需watch机制防时序不一致。

跨可用区部署必须显式控制Pod拓扑分布
Kubernetes默认不保证Pod跨可用区(AZ)分散,哪怕你设置了replicas: 3,所有副本也可能挤在同一个AZ里。一旦该AZ断电或网络隔离,服务直接不可用。
必须用topologySpreadConstraints强制打散:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: user-service
关键点:
-
topologyKey必须与节点标签一致,常见值为topology.kubernetes.io/zone或云厂商自定义键(如 AWS 的failure-domain.beta.kubernetes.io/zone) -
whenUnsatisfiable: DoNotSchedule比ScheduleAnyway更安全,宁可调度失败也不容忍单点集中 - 需提前给节点打标:
kubectl label node ip-10-0-1-100.us-west-2.compute.internal topology.kubernetes.io/zone=us-west-2a
健康检查必须穿透依赖链而非只看进程存活
livenessProbe 返回 200 不等于服务可用——它可能连不上数据库、Redis 或下游 gRPC 服务,但 HTTP handler 仍能响应。
立即学习“go语言免费学习笔记(深入)”;
真实健康检查要包含:
- 本地 goroutine 状态(如关键 worker 是否卡死)
- 主数据库连接 + 执行
SELECT 1延迟 ≤ 500ms - 核心缓存(如 Redis)PING 延迟 ≤ 100ms
- 至少一个关键下游服务的 gRPC 连通性(用
grpc.Dial配WithBlock()+ 超时)
示例路径:/health?deep=true,返回体带 "db_ping_ms": 42, "redis_ping_ms": 17,供 Prometheus 抓取告警。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
服务发现需区分跨AZ与同AZ优先级
默认 Kubernetes Service 的 ClusterIP 是轮询所有 endpoints,跨 AZ 调用会引入额外延迟和网络风险。生产环境应启用拓扑感知路由。
两种可行方式:
- 用 Istio:设置
destinationRule的trafficPolicy.loadBalancer为LEAST_CONN并开启topologyAwareHints - 用 CoreDNS + 自定义插件:根据客户端 Pod 的
topology.kubernetes.io/zone标签,只返回同 AZ 的 endpoints IP
注意:gRPC 客户端不能依赖 DNS 轮询做负载均衡,必须用 round_robin 或 least_request resolver,并确保 resolver 能读取 endpoint 的拓扑标签。
配置热更新在跨AZ场景下容易丢变更
当 ConfigMap 更新时,Kubernetes 默认是滚动更新所有 Pod,但跨 AZ 部署下,不同 AZ 的 Pod 可能因网络延迟收到更新时间差达数秒——导致短暂配置不一致。
安全做法:
- 用
watch监听 ConfigMap 变更,而不是靠 volume mount 的文件修改事件(后者有 inotify 延迟) - Viper 配置库必须设
viper.WatchConfig()+viper.OnConfigChange()回调,且回调内加锁防止并发 reload - 敏感配置(如数据库密码)绝不用 ConfigMap,必须走
Secret+envFrom注入,Secret 更新触发 Pod 重建(这是预期行为)
跨 AZ 的高可用不是“多跑几个实例”就能解决的事——拓扑感知、深度健康检查、配置同步时序,三者缺一不可。最容易被忽略的是:健康检查没校验下游,结果故障时 K8s 还在往已失联的 AZ 转发流量。

















