集群联邦:实现零宕机Kubernetes

Hacker News Top 工具

摘要

本文介绍如何使用Linkerd的多集群扩展来联邦Kubernetes集群以实现零宕机故障转移,结合了层级、扁平及联邦模式,跨越三个GKE集群,采用全网格拓扑和混沌测试。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/29 14:05

# 联邦多集群实现零宕机 Kubernetes 来源:https://linkerd.io/2026/06/24/federating-clusters-for-zero-downtime-kubernetes/index.html 每个多区域部署最终都会遇到同样的尴尬时刻:整个集群挂掉,而两个区域外运行的服务副本形同虚设——因为你没有任何机制把它们当作一个整体。故障转移变成了一本运维手册:恢复、重定向 DNS、等待一次理论上你已经付费避免的宕机。Linkerd 的多集群扩展填补了这个空缺,让多个集群可以将一个服务表现为一个负载均衡端点。官方文档忽略的一点是:真实平台几乎不会只选*一种*多集群模式。有些服务想要联邦(相同的服务无处不在,一个端点,自动故障转移),而另一些想要镜像(通过名称访问特定的远程服务)。而且你经常希望这两种模式共存于同一组链接上。文档会分别讲解每种模式。本文将在三个 GKE 集群上把三种模式全部串联起来,包括全网格链接拓扑、一个干掉整个集群的混沌测试,以及你可以克隆并在全新 GCP 项目上运行的脚本。 **配套仓库**:这里引用的每个脚本都位于[这个仓库](https://github.com/dtaskai/linkerd-multicluster-demo)中。请随意克隆,设置你的项目 ID,然后运行。 ## Linkerd 多集群模式:网关、扁平与联邦 Linkerd 的多集群扩展支持三种模式。好处是它们并非互斥:在同一组链接的集群上,模式通过服务的标签来按需选择。 | 模式 | 标签 | 结果 | 网络要求 | |------|------|------|----------| | **层级(网关)** | `mirror.linkerd.io/exported=true` | 服务被镜像为 `-`,流量通过网关路由 | 网关 IP 可达 | | **扁平(Pod 到 Pod)** | `mirror.linkerd.io/exported=remote-discovery` | 服务被镜像为 `-`,流量直接发往远程 Pod | 扁平网络(Pod IP 可路由) | | **联邦** | `mirror.linkerd.io/federated=member` | 所有同名服务合并为 `-federated`,跨集群负载均衡 | 扁平网络(Pod IP 可路由) | 运营上最重要的区别是:层级镜像可以在*任何*网络上工作——只需要网关 IP 可达;而扁平模式和联邦模式需要真正的 Pod 到 Pod 连通性。在 GCP 上,基于 VPC 对等的 VPC 原生 GKE 集群可以免费提供这种扁平网络。因此,你可以为你的核心工作负载在扁平网络上运行联邦服务,同时仍然通过网关从不在该网络上的集群镜像一个专用服务。我见过的多数平台团队最终都会采用这种混合方式。 ## 多区域架构:GKE 集群设置 我们在三个区域部署了三个 GKE 集群,彼此之间完全链接(总共六条定向链接)。三个演示服务,各自使用不同的多集群模式: GCP 项目 - **前端(frontend)** 是联邦模式,运行在所有三个集群中。每个集群中一个联邦前端服务可以跨所有九个 Pod(3 个副本 × 3 个集群)进行负载均衡。当某个集群宕机时,剩余的六个 Pod 能无感知地吸收流量,无需应用变更。 - **API(api)** 是扁平镜像模式,运行在 `west` 和 `east` 中。`north` 集群将其作为 `api-west` 和 `api-east` 消费,这些是明确的远程服务名称,流量直接发送到远程 Pod。当你需要客户端决定与哪个后端通信时(例如为了数据本地性而保持请求在区域内),就会用到这种模式。 - **分析(analytics)** 是网关镜像模式,仅运行在 `east` 中。通过 Linkerd 网关导出,使得 `west` 和 `north` 可以将其作为 `analytics-east-gw` 访问,而不需要与 `east` 的 Pod 建立扁平网络连接。它主要是为了证明网关模式可以与扁平模式和联邦模式在同一组链接上共存。 - 一个 GCP 账户(免费套餐额度足够。使用三个标准集群和较小的节点池) - `gcloud` CLI,已认证(`gcloud auth login`) - `kubectl` v1.28+ - `step` CLI,`brew install step`(用于证书生成) - `helm` v3 - 整个设置大约需要 30 分钟 基础设施脚本会自动为你启用 `compute` 和 `container` API,因此全新项目也能开箱即用。 ## 第 0 步:配置 克隆仓库,根据示例文件创建本地 `.env` 文件,并根据你的 GCP 项目进行定制。默认值足以完成整个演示,因此大多数情况下你只需要修改项目 ID。 ``` git clone <repo-url> cd blog-linkerd-federation cp env.example .env ``` 打开 `.env` 并至少设置你的项目 ID。文件中的其他所有内容都带有合理的默认值: ``` export GCP_PROJECT="your-project-id" export REGION_WEST="us-central1" export REGION_EAST="us-east1" export REGION_NORTH="europe-west1" # 每个区域一个可用区。我们将节点位置固定到单个可用区,这样 num-nodes 就是 # 总节点数——详见下方成本说明。 export ZONE_WEST="us-central1-a" export ZONE_EAST="us-east1-b" export ZONE_NORTH="europe-west1-b" export CLUSTER_MACHINE_TYPE="e2-medium" export CLUSTER_NODE_COUNT="1" export FRONTEND_REPLICAS="3" ``` 至少设置 `GCP_PROJECT`。其他所有内容都带有合理的默认值:三个区域、每个区域一个可用区、以及较小的节点池以控制成本。如果你运行 `cat .env`,应该能看到所有变量都已填充。 将变量加载到当前 shell 中,以便脚本可以读取它们: > 下方的每个脚本都会读取这个文件,并且它们都使用 `set -euo pipefail`,因此缺失的变量会大声报错而不是默默失败。这就是 `env.example` 携带完整变量集的原因——包括 VPC 和集群名称,而不仅仅是项目 ID。 ## 第 1 步:使用 VPC 对等创建三个 GKE 集群 运行基础设施脚本以创建网络和集群。这大约需要 10–15 分钟,所以是喝杯咖啡的好时机。 这个脚本执行以下操作: 1. 启用 `compute` 和 `container` API(如果已启用则为空操作)。 2. 创建**三个 VPC**,具有不重叠的 Pod 和服务 CIDR——这是 VPC 对等的硬性要求。 3. 设置**全网状 VPC 对等**(west↔east,east↔north,north↔west),使用 `--export-custom-routes` 和 `--import-custom-routes`,以便实际通告 Pod CIDR。这就是我们获得扁平网络的方式。 4. 创建**三个 GKE 标准集群**,每个 VPC/区域一个,每个固定到单个可用区。 5. 将 **kubectl 上下文**重命名为 `west`、`east`、`north`。 以下是脚本使用的地址规划。这些范围故意不重叠,以便 VPC 对等能够正确路由 Pod 流量: | 集群 | VPC 子网 | Pod CIDR | Service CIDR | |------|----------|----------|--------------| | west | 10.10.0.0/20 | 10.100.0.0/14 | 10.104.0.0/20 | | east | 10.20.0.0/20 | 10.108.0.0/14 | 10.112.0.0/20 | | north | 10.30.0.0/20 | 10.116.0.0/14 | 10.120.0.0/20 | 不重叠的范围是不可妥协的。如果 Pod CIDR 在跨 VPC 对等中重叠,路由会静默崩溃。Pod 会从错误的集群获取响应,或者连接超时,日志中没有任何有用信息。问我怎么知道的。 **一个可用区,不是三个。** GKE 的*区域*集群默认会将 `--num-nodes` 个节点放置到**每个**可用区(共三个)。如果 `--num-nodes` 为 1,那就是每个集群 3 个节点,总共 9 个节点,费用翻三倍。脚本将 `--node-locations` 固定到单个可用区,这样 `CLUSTER_NODE_COUNT=1` 就真的意味着每个集群只有 1 个节点。 **成本说明:** 三个标准集群各带一个 `e2-medium` 节点,整个演示每天大约花费 $10–15 美元(管理费 + 节点 + east 上一个小型网关负载均衡器)。拆除脚本会删除所有资源。 ## 第 2 步:使用共享信任锚安装 Linkerd 在所有三个集群中使用共享信任锚安装 Linkerd。该脚本会生成证书、安装控制平面,并配置每个集群信任其他集群以进行跨集群 mTLS。 ``` ./scripts/02-linkerd-install.sh ``` 这会生成一个根 CA 和每集群的签发证书,然后在所有三个集群上安装 Linkerd: ``` root.crt (shared trust anchor) ├── issuer-west.crt + issuer-west.key ├── issuer-east.crt + issuer-east.key └── issuer-north.crt + issuer-north.key ``` 每集群的签发证书是一个值得保持的生产习惯:如果一个集群的签发者被泄露,你可以隔离地轮换它,而不影响其他集群。共享根证书让跨集群 mTLS 能够工作。每个代理都可以验证其他代理的身份,追溯到同一个锚点。 为了控制资源使用(和成本),本次安装只包含控制平面,没有 Viz 附加组件。 ## 第 3 步:安装多集群并创建全网状链接拓扑 设置多集群组件并在集群之间创建全网状拓扑。此步骤之后,每个集群都可以消费来自其他集群的服务。 ``` ./scripts/03-multicluster-setup.sh ``` 这一步涉及的内容最多。我们创建**六条定向链接**,每个集群都链接到其他每个集群,这样每个集群都能获得联邦工作负载的 `-federated` 服务,并且每个集群都可以消费来自任何其他集群的镜像服务。 复杂之处在于网关。只有 `east` 需要一个(它是唯一一个以层级方式导出 `analytics` 的集群),因此我们在 east 的安装中启用网关,而让其他集群没有网关。每个集群一次安装,所有标志一次性设置,无需在之后第二次运行安装来附加网关: ``` # west:无网关,为它消费的每个集群配置一个控制器 linkerd --context west multicluster install --gateway=false \ --set controllers[0].link.ref.name=east \ --set controllers[1].link.ref.name=north \ --set controllers[2].link.ref.name=east-gw \ | kubectl --context west apply -f - # east:此处启用网关,为它消费的集群配置控制器 linkerd --context east multicluster install --gateway=true \ --set controllers[0].link.ref.name=west \ --set controllers[1].link.ref.name=north \ | kubectl --context east apply -f - # north:无网关,为 west、east 以及 east 的网关链接配置控制器 linkerd --context north multicluster install --gateway=false \ --set controllers[0].link.ref.name=west \ --set controllers[1].link.ref.name=east \ --set controllers[2].link.ref.name=east-gw \ | kubectl --context north apply -f - ``` 注意控制器数量。服务镜像控制器运行在*消费*方,每条链接一个。`west` 和 `north` 都通过网关消费 `analytics`,因此它们需要第三个控制器用于 `east-gw` 链接;`east` 不消费自己的 analytics,因此只需要两个。 然后我们生成链接。扁平/联邦链接使用 `--gateway=false`;到 `east` 的网关感知链接(用于 analytics 导出)是一个*独立*的链接,命名为 `east-gw`: ``` # 扁平链接(无网关)——用于联邦 + 扁平镜像服务 linkerd --context east multicluster link-gen --cluster-name=east --gateway=false \ | kubectl --context west apply -f - linkerd --context west multicluster link-gen --cluster-name=west --gateway=false \ | kubectl --context east apply -f - # ...(所有六个方向) # 从 east 到其他集群的网关感知链接(用于 analytics 层级导出) linkerd --context east multicluster link-gen --cluster-name=east-gw \ | kubectl --context west apply -f - linkerd --context east multicluster link-gen --cluster-name=east-gw \ | kubectl --context north apply -f - ``` 之后,在任何集群上运行 `linkerd multicluster check` 应该会报告每个链接的集群都健康。 ## 第 4 步:部署演示服务 部署演示工作负载。接下来的部分会为它们打上联邦、扁平镜像和网关镜像的标签,并展示每种模式创建的内容。 ``` ./scripts/04-deploy-app.sh ``` 三个服务,三种模式,故意都使用相同的 `buoyantio/bb` 镜像——一个返回固定字符串的小型 HTTP 服务器。应用本身不是重点。重点在于一条 `kubectl label` 命令就能改变 Linkerd 对待服务的方式,其他一切保持不变。 ### frontend(联邦) 在所有三个集群中部署,并为每个集群设置不同的响应字符串,然后打上联邦标签: ``` for ctx in west east north; do kubectl --context $ctx -n mc-demo label svc/frontend mirror.linkerd.io/federated=member done ``` 几秒钟内,`frontend-federated` 会出现在所有三个集群中: ``` $ kubectl --context west -n mc-demo get svc NAME TYPE CLUSTER-IP PORT(S) AGE frontend ClusterIP 10.104.1.50 8080/TCP 45s frontend-federated ClusterIP 10.104.2.100 8080/TCP 10s ``` ### api(扁平镜像) 在 `west` 和 `east` 中为 api 服务打上扁平导出的标签: ``` kubectl --context west -n mc-demo label svc/api mirror.linkerd.io/exported=remote-discovery kubectl --context east -n mc-demo label svc/api mirror.linkerd.io/exported=remote-discovery ``` 现在 `north` 可以将 `api-west` 和 `api-east` 作为独立服务看到: ``` $ kubectl --context north -n mc-demo get svc NAME TYPE CLUSTER-IP PORT(S) AGE frontend ClusterIP 10.120.1.50 8080/TCP 45s frontend-federated ClusterIP 10.120.2.100 8080/TCP 10s api-west ClusterIP 10.120.3.20 8080/TCP 5s api-east ClusterIP 10.120.3.21 8080/TCP 5s ``` `north` 中的客户端明确选择 `api-west` 或 `api-east`。流量将直接发往远程 Pod,路径中没有网关。 ### analytics(网关镜像) 接下来,仅部署到 `east`,并打上层级(网关)导出的标签: ``` kubectl --context east -n mc-demo label svc/analytics mirror.linkerd.io/exported=true ``` 这会在 `west` 和 `north` 中创建 `analytics-east-gw`,通过 east 的 Linkerd 网关路由: ``` $ kubectl --context west -n mc-demo get svc analytics-east-gw NAME TYPE CLUSTER-IP PORT(S) AGE analytics-east-gw ClusterIP 10.104.5.10 8080/TCP 5s ``` 这个服务的端点指向 east 的网关 IP,而不是 analytics Pod 本身。当你无法保证扁平网络连通性,或者你特别希望网关处理负载均衡和 mTLS 终止时,这是正确的取舍。 ## 第 5 步:验证所有三种模式 对三种服务模式生成流量,并验证每种模式都能按预期解析。这会部署一个流量生成器到 `north`,循环访问所有三种服务模式,并输出日志。响应字符串直接来自部署,因此你会看到哪个集群处理了每个请求: ``` [federated] frontend from east [federated] frontend from west [federated] frontend from north [flat-west] api from west [flat-east] api from east [gateway] analytics from east ``` 你也可以检查端点,看看每种模式如何以不同方式解析: ``` # 联邦:端点跨越所有三个集群 $ linkerd --context west diagnostics endpoints frontend-federated.mc-demo.svc.cluster.local:8080 NAMESPACE IP PORT POD SERVICE mc-demo 10.100.1.15 8080 frontend-xxx-west frontend.mc-demo mc-demo 10.108.0.42 8080 frontend-xxx-east frontend.mc-demo mc-demo 10.116.0.33 8080 frontend-xxx-north frontend.mc-demo # 扁平镜像:端点是远程 Pod IP $ linkerd --context north diagnostics endpoints api-west.mc-demo.svc.cluster.local:8080 NAMESPACE IP PORT POD SERVICE mc-demo 10.100.2.10 8080 api-xxx-west api.mc-demo # 网关镜像:端点是 east 的网关 IP(端口 4143) $ kubectl --context west -n mc-demo get endpoints analytics-east-gw NAME ENDPOINTS AGE analytics-east-gw 35.186.xxx.xxx:4143 30s ``` 三种模式,一个网格,一组集群,它们之间的唯一区别是一个标签。 ## 第 6 步:混沌测试——干掉一个集群 这就是联邦大显身手的地方。我们模拟一次完整的集群故障,然后观察每种服务类型如何反应。 ``` ./scripts/06-chaos-test.sh ``` 该脚本将 `east` 中的每个部署缩放到零副本(模拟集群宕机),然后从 `north` 采样所有三种模式的流量。 ### 联邦服务(`frontend-federated`) ``` 之前:west=33% east=33% north=33% 之后:west=50% north=50% ← 自动重新平衡,零错误 ``` 流量立即重新分布。没有错误,没有配置更改。随着 east 的 Pod 从端点列表中消失,Linkerd 的负载均衡器只是简单地扩散了流量。

相似文章

Kubernetes 扩展到 7,500 个节点

OpenAI Blog

# Kubernetes 扩展到 7,500 个节点 来源:[https://openai.com/index/scaling-kubernetes-to-7500-nodes/](https://openai.com/index/scaling-kubernetes-to-7500-nodes/) OpenAI将单个 Kubernetes 集群扩展到这个规模很少见,需要特殊的关注,但好处是提供了一个简单的基础设施,让我们的机器学习研究团队能够更快地迭代并扩展,而无需改变代码。从我们之前关于[扩展到 2,500 个节点⁠](https://openai.com/index/scaling-kube)的文章以来

将 Kubernetes 扩展到 2,500 个节点

OpenAI Blog

OpenAI 分享了将 Kubernetes 扩展到 2,500 个节点的基础设施经验教训,详细介绍了容器镜像拉取的优化方案,包括 kubelet 配置更改、Docker overlay2 迁移和预加载策略,以解决 Pending pod 的问题。

Kubernetes In Anger

Lobsters Hottest

一份全面指南,介绍在生产环境中调试和管理 Amazon EKS 集群,重点关注常见故障模式、事件响应和安全升级。涵盖 EKS 与标准 Kubernetes 的关键差异。