云原生技术15-你的K8s集群真的高可用吗?5个必做的容灾检查,从单点故障到“永不宕机“的架构进化,三Master + 负载均衡 + etcd集群:生产级K8s部署指南
1、AI程序员系列文章2、AI面试系列文章3、AI编程系列文章目录开篇那些让人崩溃的K8s故障高可用架构全景图etcd集群K8s的心脏Master组件高可用Worker节点高可用集群联邦多集群统一管理备份策略最后一道防线文末三件套开篇那些让人崩溃的K8s故障你是否遇到过这样的噩梦场景Master节点宕机整个集群瞬间变成植物人kubectl命令全部超时生产环境直接停摆etcd数据丢失集群状态一夜回到解放前所有Deployment、Service配置灰飞烟灭升级时服务中断以为只是打个补丁结果业务挂了2小时老板在群里疯狂你效率技巧生产环境的高可用不是可选项而是必选项。就像你不能指望一台服务器永远不死机也不能指望单Master的K8s能扛住生产流量。本文将给出经过验证的K8s高可用架构方案让你的集群真正做到永不宕机。高可用架构全景图先来看一张完整的架构图理解各个组件是如何协同工作的┌─────────────────────────────────────┐ │ 用户/客户端 │ └───────────────┬─────────────────────┘ │ ┌───────────────▼─────────────────────┐ │ 负载均衡器 (VIP) │ │ ┌─────────┬─────────┬─────────┐ │ │ │ LB-1 │ LB-2 │ LB-3 │ │ │ │(Keepalived/VIP漂移) │ │ │ └─────────┴─────────┴─────────┘ │ └───────────────┬─────────────────────┘ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ ┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐ │ Master-1 │◄──────►│ Master-2 │◄──────►│ Master-3 │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ API Server│ │ │ │ API Server│ │ │ │ API Server│ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │Controller│ │ │ │Controller│ │ │ │Controller│ │ │ │ Manager │ │ │ │ Manager │ │ │ │ Manager │ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ Scheduler│ │ │ │ Scheduler│ │ │ │ Scheduler│ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ etcd │◄─┼────────┼──► etcd │◄──┼────────┼──► etcd │ │ │ │ (Member) │ │ │ │ (Member) │ │ │ │ (Member) │ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ └────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ └───────────────────────────┼───────────────────────────┘ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ ┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐ │ Worker-1 │ │ Worker-2 │ │ Worker-3 │ │ (可用区A) │ │ (可用区B) │ │ (可用区C) │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ kubelet │ │ │ │ kubelet │ │ │ │ kubelet │ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │kube-proxy│ │ │ │kube-proxy│ │ │ │kube-proxy│ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │ │ Pod │ │ │ │ Pod │ │ │ │ Pod │ │ │ │ Pod │ │ │ │ Pod │ │ │ │ Pod │ │ │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │ └────────────────┘ └─────────────────┘ └─────────────────┘核心组件说明组件数量作用高可用机制Master节点3控制平面多副本负载均衡etcd3或5数据存储Raft共识算法Worker节点3工作负载多可用区部署负载均衡器2流量入口Keepalived VIP漂移⚠️避坑警告很多新手以为只要Master有3个节点就高可用了结果etcd只部署了1个。记住etcd是K8s的心脏心脏停了大脑Master再聪明也没用。etcd集群K8s的心脏为什么etcd如此重要etcd存储了K8s的所有状态数据Pod位置、Service配置、Secret、ConfigMap……可以说etcd挂了整个集群就失忆了。Raft共识算法少数服从多数etcd使用Raft算法保证数据一致性。核心规则3节点集群可容忍1个节点故障需要2个节点存活5节点集群可容忍2个节点故障需要3个节点存活┌─────────────────────────────────────────────────────────────┐ │ etcd集群写入流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 客户端 ──► Leader ──► 写入日志 ──► 复制到Followers │ │ │ │ │ │ │◄─────────────────────────┘ │ │ │ 等待多数节点确认 │ │ ▼ │ │ 提交写入 │ │ │ │ ⚠️ 必须半数以上节点确认才算成功 │ └─────────────────────────────────────────────────────────────┘部署etcd集群# 在三台Master节点上分别执行假设IP为10.0.0.1/2/3 # 节点1 etcd --name etcd-1 \ --data-dir /var/lib/etcd \ --listen-client-urls https://10.0.0.1:2379 \ --advertise-client-urls https://10.0.0.1:2379 \ --listen-peer-urls https://10.0.0.1:2380 \ --initial-advertise-peer-urls https://10.0.0.1:2380 \ --initial-cluster etcd-1https://10.0.0.1:2380,etcd-2https://10.0.0.2:2380,etcd-3https://10.0.0.3:2380 \ --initial-cluster-token k8s-etcd-cluster \ --initial-cluster-state new \ --client-cert-auth \ --trusted-ca-file/etc/etcd/ca.crt \ --cert-file/etc/etcd/server.crt \ --key-file/etc/etcd/server.key # 节点2和3类似修改--name和IP即可验证etcd集群健康状态# 查看集群成员 ETCDCTL_API3 etcdctl member list \ --endpointshttps://10.0.0.1:2379 \ --cacert/etc/etcd/ca.crt \ --cert/etc/etcd/client.crt \ --key/etc/etcd/client.key # 输出示例 # 8e9e05c52164694d, started, etcd-1, https://10.0.0.1:2380, https://10.0.0.1:2379 # 62e44c4e7f2e8b1a, started, etcd-2, https://10.0.0.2:2380, https://10.0.0.2:2379 # 3a8f9c2d4e5b6a7f, started, etcd-3, https://10.0.0.3:2380, https://10.0.0.3:2379 # 检查集群健康 ETCDCTL_API3 etcdctl endpoint health \ --endpointshttps://10.0.0.1:2379,https://10.0.0.2:2379,https://10.0.0.3:2379 \ --cacert/etc/etcd/ca.crt \ --cert/etc/etcd/client.crt \ --key/etc/etcd/client.key # 输出示例 # https://10.0.0.1:2379 is healthy: successfully committed proposal # https://10.0.0.2:2379 is healthy: successfully committed proposal # https://10.0.0.3:2379 is healthy: successfully committed proposal效率技巧etcd集群成员数量必须是奇数3、5、7…。偶数节点在故障时更容易出现脑裂而且不会提高容错能力。etcd快照备份最后一根救命稻草# 创建快照 ETCDCTL_API3 etcdctl snapshot save /backup/etcd-$(date %Y%m%d-%H%M%S).db \ --endpointshttps://10.0.0.1:2379 \ --cacert/etc/etcd/ca.crt \ --cert/etc/etcd/client.crt \ --key/etc/etcd/client.key # 查看快照状态 ETCDCTL_API3 etcdctl snapshot status /backup/etcd-20240101-120000.db # 从快照恢复灾难恢复时使用 ETCDCTL_API3 etcdctl snapshot restore /backup/etcd-20240101-120000.db \ --data-dir/var/lib/etcd-new \ --initial-clusteretcd-1https://10.0.0.1:2380,etcd-2https://10.0.0.2:2380,etcd-3https://10.0.0.3:2380 \ --initial-cluster-tokenk8s-etcd-cluster \ --nameetcd-1⚠️避坑警告etcd快照备份必须定期进行并存储在集群外部如对象存储。我见过太多人把备份放在etcd节点本地结果节点挂了备份也没了。Master组件高可用API Server多副本负载均衡API Server是K8s的前台接待所有操作kubectl、控制器、调度器都要通过它。实现高可用的关键是多副本负载均衡。# kube-apiserver配置要点 apiVersion: v1 kind: Pod metadata: name: kube-apiserver namespace: kube-system spec: containers: - name: kube-apiserver image: k8s.gcr.io/kube-apiserver:v1.28.0 command: - kube-apiserver - --advertise-address10.0.0.1 # 本机IP - --etcd-servershttps://10.0.0.1:2379,https://10.0.0.2:2379,https://10.0.0.3:2379 - --bind-address0.0.0.0 - --secure-port6443 - --enable-admission-pluginsNodeRestriction - --authorization-modeNode,RBAC - --tls-cert-file/etc/kubernetes/pki/apiserver.crt - --tls-private-key-file/etc/kubernetes/pki/apiserver.keyController Manager Scheduler选主机制这两个组件不能同时运行多个活跃副本否则会出现冲突。K8s通过Lease机制实现选主┌─────────────────────────────────────────────────────────────┐ │ Leader Election流程 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Master-1 Master-2 Master-3 │ │ │ │ │ │ │ │ 尝试获取Lease │ │ │ │ │──────────────►│ │ │ │ │ │ │ │ │ │◄──────────────┘ │ │ │ │ 成功成为Leader │ │ │ │ │ │ │ │ │ 定期续租 │ │ │ │ │──────────────►│ │ │ │ │ │ │ │ │ │ 续租失败 │ │ │ │ │ (Master-1挂了) │ │ │ │ │ 尝试获取Lease │ │ │ │ │──────────────►│ │ │ │ │◄──────────────┘ │ │ │ │ Master-2成为新Leader │ │ │ └─────────────────────────────────────────────────────────────┘# 查看当前Leader kubectl get endpoints kube-controller-manager -n kube-system -o yaml kubectl get endpoints kube-scheduler -n kube-system -o yaml # 查看Lease资源K8s 1.19推荐方式 kubectl get lease kube-controller-manager -n kube-system -o yaml kubectl get lease kube-scheduler -n kube-system -o yaml负载均衡器配置Keepalived HAProxy# /etc/keepalived/keepalived.conf vrrp_script check_haproxy { script killall -0 haproxy interval 2 weight 2 } vrrp_instance VI_1 { interface eth0 state MASTER # 备节点改为BACKUP virtual_router_id 51 priority 101 # 备节点改为100 virtual_ipaddress { 10.0.0.100/24 # VIP地址 } track_script { check_haproxy } } # /etc/haproxy/haproxy.cfg frontend k8s-api bind 10.0.0.100:6443 default_backend k8s-masters backend k8s-masters balance roundrobin option tcp-check server master-1 10.0.0.1:6443 check server master-2 10.0.0.2:6443 check server master-3 10.0.0.3:6443 check效率技巧故障切换时间可以控制在30秒以内。Keepalived的VRRP协议默认1秒宣告一次3次未收到宣告就认为Master挂了VIP漂移到备机。Worker节点高可用多可用区部署不要把鸡蛋放在一个篮子里# 使用Pod反亲和性分散部署 apiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 3 selector: matchLabels: app: web-app template: metadata: labels: app: web-app spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - web-app topologyKey: topology.kubernetes.io/zone containers: - name: web image: nginx:latest污点与容忍特殊工作负载的VIP通道# 给特定节点打污点比如GPU节点 kubectl taint nodes node-gpu-1 dedicatedgpu:NoSchedule # Pod配置容忍度允许调度到GPU节点 apiVersion: v1 kind: Pod metadata: name: gpu-pod spec: tolerations: - key: dedicated operator: Equal value: gpu effect: NoSchedule containers: - name: cuda image: nvidia/cuda:latest节点故障自动处理# 查看节点状态 kubectl get nodes -o wide # 节点故障后Pod默认5分钟后会被驱逐 # 可以调整这个阈值 kubectl edit deployment web-app # 添加 spec: template: spec: tolerations: - key: node.kubernetes.io/unreachable operator: Exists effect: NoExecute tolerationSeconds: 60 # 1分钟后驱逐默认300秒⚠️避坑警告不要把所有Worker节点放在同一个可用区我见过某云厂商可用区故障导致用户整个K8s集群瘫痪的案例。至少跨2个可用区部署最好是3个。集群联邦多集群统一管理什么时候需要集群联邦业务跨多个地域部署北京、上海、广州需要跨集群的服务发现和流量调度单集群规模太大超过5000节点需要拆分┌─────────────────────────────────────────────────────────────┐ │ K8s集群联邦架构 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────┐ │ │ │ 联邦控制平面 │ │ │ │ (Kubefed/Karmada) │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ │ │ │ │ │ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │ │ │ 集群-1 │ │ 集群-2 │ │ 集群-3 │ │ │ │ (北京) │ │ (上海) │ │ (广州) │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ └───────────────┼───────────────┘ │ │ │ │ │ ┌──────────▼──────────┐ │ │ │ 全局服务发现 │ │ │ │ (ServiceExport/ │ │ │ │ ServiceImport) │ │ │ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘使用Karmada部署联邦应用# 部署策略跨3个集群每个集群2个副本 apiVersion: policy.karmada.io/v1alpha1 kind: PropagationPolicy metadata: name: web-app-propagation spec: resourceSelectors: - apiVersion: apps/v1 kind: Deployment name: web-app placement: clusterAffinity: clusterNames: - cluster-beijing - cluster-shanghai - cluster-guangzhou replicaScheduling: replicaSchedulingType: Divided replicaDivisionPreference: Weighted weightPreference: staticWeightList: - targetCluster: clusterNames: [cluster-beijing] weight: 2 - targetCluster: clusterNames: [cluster-shanghai] weight: 2 - targetCluster: clusterNames: [cluster-guangzhou] weight: 2效率技巧集群联邦不是银弹它增加了架构复杂度。如果你的业务不需要跨地域部署单集群多可用区就够了。不要为了技术而技术。备份策略最后一道防线三层备份体系层级工具备份内容频率集群状态etcd快照所有K8s资源每小时应用数据VeleroPV数据、应用配置每天底层存储云厂商快照磁盘快照每天Velero安装与使用# 安装Velero CLI wget https://github.com/vmware-tanzu/velero/releases/download/v1.12.0/velero-v1.12.0-linux-amd64.tar.gz tar -xzf velero-v1.12.0-linux-amd64.tar.gz sudo mv velero-v1.12.0-linux-amd64/velero /usr/local/bin/ # 安装Velero到集群使用S3兼容存储 velero install \ --provider aws \ --plugins velero/velero-plugin-for-aws:v1.8.0 \ --bucket k8s-backups \ --secret-file ./credentials-velero \ --use-volume-snapshotsfalse \ --backup-location-config regionminio,s3ForcePathStyletrue,s3Urlhttp://minio.example.com:9000 # 创建备份包含所有命名空间 velero backup create full-backup-$(date %Y%m%d) --include-namespaces * # 创建备份仅生产命名空间 velero backup create prod-backup --include-namespaces production # 查看备份列表 velero backup get # 从备份恢复 velero restore create --from-backup full-backup-20240101自动化备份脚本#!/bin/bash # /opt/backup/k8s-backup.sh BACKUP_DIR/backup/etcd DATE$(date %Y%m%d-%H%M%S) RETENTION_DAYS7 # etcd备份 ETCDCTL_API3 etcdctl snapshot save ${BACKUP_DIR}/etcd-${DATE}.db \ --endpointshttps://127.0.0.1:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key # 压缩备份 gzip ${BACKUP_DIR}/etcd-${DATE}.db # 上传到对象存储示例使用rclone rclone copy ${BACKUP_DIR}/etcd-${DATE}.db.gz s3:k8s-backups/etcd/ # 清理旧备份 find ${BACKUP_DIR} -name etcd-*.db.gz -mtime ${RETENTION_DAYS} -delete # 发送通知示例使用企业微信 curl -X POST https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyYOUR_KEY \ -H Content-Type: application/json \ -d {\msgtype\: \text\, \text\: {\content\: \K8s etcd备份完成: etcd-${DATE}.db.gz\}}⚠️避坑警告备份不是目的恢复才是。定期做恢复演练我见过太多人备份了几年真正需要恢复时才发现备份文件损坏或无法使用。故障演练验证高可用性演练清单# 1. 模拟Master节点故障 # 关闭一台Master节点验证 # - kubectl命令是否正常 # - VIP是否漂移 # - 新Pod能否创建 # 2. 模拟etcd节点故障 # 关闭一台etcd节点验证 # - 集群是否仍然可用 # - 数据写入是否正常 # 3. 模拟Worker节点故障 # 关闭一台Worker节点验证 # - Pod是否自动迁移 # - 服务是否仍然可用 # 4. 模拟网络分区 # 使用iptables模拟网络故障 iptables -A INPUT -s 10.0.0.2 -j DROP # 阻断与Master-2的通信高可用性检查脚本#!/bin/bash # k8s-ha-check.sh echo K8s高可用性检查 # 检查etcd集群健康 echo -e \n[1/5] etcd集群健康检查 ETCDCTL_API3 etcdctl endpoint health \ --endpointshttps://10.0.0.1:2379,https://10.0.0.2:2379,https://10.0.0.3:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key # 检查Master节点状态 echo -e \n[2/5] Master节点状态 kubectl get nodes -l node-role.kubernetes.io/control-plane -o wide # 检查系统Pod状态 echo -e \n[3/5] 系统Pod状态 kubectl get pods -n kube-system # 检查Leader选举状态 echo -e \n[4/5] Leader选举状态 kubectl get lease -n kube-system # 检查备份状态 echo -e \n[5/5] 最近备份文件 ls -lh /backup/etcd/*.db.gz 2/dev/null | head -5 || echo 未找到备份文件 echo -e \n 检查完成 文末三件套1. 【源码获取】关注此系列获取后续更新后台回复**‘k8sha’**获取本文完整配置脚本和架构图。2. 【思考题】你的K8s集群做过故障演练吗如果现在就拔掉一台Master节点的网线你的业务能撑多久欢迎在评论区分享你的容灾方案。3. 【系列预告】服务网格Istio/Linkerd深度实践流量治理从此不再头疼混合云K8s跨云部署摆脱厂商锁定边缘计算K3sKubeEdge把K8s延伸到边缘设备CI/CDArgoCDTekton云原生时代的DevOps实践总结K8s高可用不是简单的多部署几台机器而是一个系统工程层级关键措施目标etcd层3/5节点Raft集群定期快照数据零丢失Master层多副本负载均衡Leader选举99.99%可用性Worker层多可用区Pod反亲和性故障自动恢复应用层健康检查优雅退出重试机制用户体验无损备份层etcd快照Velero云快照可恢复性记住高可用是设计出来的不是运维出来的。在架构设计阶段就考虑容错比故障发生后再救火要有效得多。标签Kubernetes高可用etcd集群Master高可用负载均衡集群联邦容灾备份参考文档Kubernetes官方高可用文档etcd集群运维指南Karmada官方文档