跨多个大洲的 Kubernetes:在全球数据中心实现 k3s 和 k8s 高可用
横跨美国、欧洲和亚洲、能够承受整个大洲故障的 Kubernetes:etcd 为何不适合远距离部署、每个区域一个 k3s 集群、通过 Flux 实现 GitOps、Geo-DNS 故障转移、跨区域的数据,以及与 kubeadm 的区别。
如果希望应用以高可用、可自动扩缩容的方式运行,Kubernetes 就是事实上的标准。一个很自然的想法,是让单个 Kubernetes 集群横跨美国、欧洲和亚洲的服务器,但这个想法会卡在一个细节上:etcd,也就是 Kubernetes 保存其全部状态的数据库。本文介绍如何让跨多个大洲的 Kubernetes 依然可行,而且能够承受整个大洲的故障:每个区域一个集群,通过 GitOps 统一交付,再由 Geo-DNS 把用户引导到最近的健康区域。
我们使用 k3s 搭建这套架构。k3s 是一个轻量且经过完整认证的 Kubernetes 发行版,几分钟就能装好。文末还会说明,改用 kubeadm 部署传统 Kubernetes 时有哪些变化。关于跨多个机房的可用性、Quorum 和故障转移,基础知识在文章跨三大洲的 Docker Swarm中有详细介绍;本文聚焦于 Kubernetes 的不同之处。
一个集群横跨所有大洲,还是每个区域一个集群?
对于跨多个大洲的 Kubernetes,正确的架构是每个区域一个集群,而不是用一个集群横跨所有大洲。每个集群独立运行,全部从同一个 Git 仓库发布,并由带健康检查的 Geo-DNS 分配用户。某个区域发生故障时,其他区域会接管,无需跨越大洋去协调一个共享的集群状态。
为什么 etcd 不适合远距离部署
Kubernetes 把每一项状态、每一份配置和每一次变更都保存在 etcd 中,这是一个基于 Raft 共识的键值存储。每一次写入都需要得到全部 etcd 成员中多数成员的确认。etcd 默认的心跳间隔为 100 毫秒,选举超时为 1 秒;而欧洲、北美和亚洲之间的数据包延迟在 80 到 250 毫秒之间。这些时间可以调大,但这样一来,集群中的每一次写入都会变慢,从调度一个 Pod 到保存一个 Secret 都不例外。k3s 文档在这一点上说得很明确:分布在多个网络中的集群不支持内置 etcd,所有服务器都应位于同一机房。Kubernetes 本身的设计也是让一个集群覆盖同一区域内的多个可用区,而不是多个大洲。
三种模式对比
| 模式 | 工作方式 | 评价 |
| 一个集群横跨所有大洲 | 控制平面和 etcd 分布在多个大洲 | 不推荐:写入缓慢,etcd 不稳定,k3s 使用内置 etcd 时不受支持 |
| 控制平面位于一个区域,Worker 遍布全球 | Server 节点位于一个机房,Agent 节点位于其他大洲 | 技术上可行,但一旦控制平面所在的区域发生故障,全球范围内都无法再进行任何重新调度 |
| 每个区域一个集群 | 三个相互独立的集群,通过 GitOps 统一发布,前置 Geo-DNS | 推荐:任何一个区域都能在其他区域故障时继续运行,错误的影响仅限于单个区域 |
“每个区域一个集群”的方案还有第二个常被低估的优点:控制平面中的错误、对集群所做的错误变更,或是一次失败的升级,影响的始终只是一个区域。其他区域的用户对此毫无察觉。
k3s 还是 k8s?
两者都是真正的 Kubernetes,API、清单和工具完全相同。区别在于架构和投入的工作量:
| 特性 | k3s | 使用 kubeadm 的 k8s |
| 安装 | 一条命令,一个二进制文件 | 需要逐一配置容器运行时、kubeadm、kubelet 和网络插件 |
| 单个 Server 节点所需资源 | 至少 2 个核心和 2 GB 内存 | 明显更多,视组件而定 |
| 自带组件 | Ingress 控制器(Traefik)、网络(Flannel)、存储 Provisioner、Service 负载均衡器 | 只有核心组件,其余一切由您自行选择 |
| 高可用 | 由三台服务器组成的内置 etcd | 三个控制平面节点,API 前置负载均衡器 |
| 适用于 | 大多数应用、小型团队、每个区域单节点 | 希望自行决定每个组件的团队 |
对于每个区域一个集群的架构,我们推荐使用 k3s:只有每个集群本身都足够简单,同时运维三个集群才会轻松。如果您已有 kubeadm 经验,可以原样沿用这套架构,下文关于 kubeadm 的章节会说明其中的区别。
架构:三个区域、三个集群、一个入口
| 组件 | 作用 | 可应对哪种故障 |
| 每个区域一个 k3s 集群 | 在靠近用户的地方运行应用 | 整个区域发生故障 |
| 每个区域三台服务器(扩展阶段) | 在区域内保持 etcd 和控制平面的高可用 | 区域内个别服务器发生故障 |
| Git 仓库和每个集群中的 Flux | 每个集群自行从 Git 获取期望状态 | 不存在会成为故障点的中央交付服务器 |
| 通过 DNS 验证获取证书的 Ingress | 接收用户的请求 | 证书不受 DNS 切换的影响 |
| 带健康检查的 Geo-DNS | 把用户引导到最近的健康区域 | 无法访问的区域 |
| 复制式数据存储与备份 | 在多个区域保存数据 | 区域故障时的数据丢失 |
入门与扩展
入门配置是三台服务器,美国、欧洲和亚洲各一台,每台都是一个独立的单节点集群。一台服务器发生故障,它所在的区域也随之失效,Geo-DNS 会把该区域的用户引导到相邻区域。这一阶段在设计上就已经能够应对整个大洲的故障。扩展阶段,每个区域在同一机房部署三台服务器,这样每个区域自身也能承受单台服务器的故障,而无需把用户引导到其他区域。
哪些 KernelHost 机房位置适合
KernelHost 在美因河畔法兰克福的 maincubes 数据中心运营服务器,并在欧洲、北美和亚太地区的更多机房位置提供虚拟服务器,其中包括美国的三个机房位置,以及加拿大、伦敦、斯特拉斯堡、华沙、赫尔辛基、新加坡、日本、悉尼和孟买。完整列表见服务器机房位置页面。本文示例中,欧洲使用美因河畔法兰克福,北美使用美国东海岸,亚洲使用新加坡。
教程:使用 k3s 在三个区域部署 Kubernetes
示例使用三台运行 Debian 12 或 13 的服务器:k3s-eu、k3s-us 和 k3s-asia。公网地址取自文档专用网段 203.0.113.0/24,域名为 example.com。请将两者替换为您自己的值。
第 1 步:在三个区域准备服务器
在三个区域订购三台服务器,配置至少为 2 vCPU 和 4 GB 内存,这样除了 k3s 之外,您的应用也有足够的空间。按照新 Root 服务器检查清单完成基础加固,并设置含义清晰的主机名。etcd 能从高速 SSD 中明显受益,KernelHost 服务器的 NVMe 存储正好满足这一点。
第 2 步:安装 k3s
在三台服务器上分别用一条命令安装 k3s。这样,每台服务器都会成为一个只有一个节点的完整 Kubernetes 集群:
curl -sfL https://get.k3s.io | sh -
kubectl get nodes
大约一分钟后,kubectl get nodes 会显示该节点为 Ready。k3s 自带 Traefik 作为 Ingress 控制器、Flannel 作为网络,以及一个用于本地卷的 Provisioner。
第 3 步:扩展为每个区域三台服务器
如果要让某个区域自身也具备高可用性,就在同一机房部署三台服务器。第一台服务器启动内置 etcd,另外两台使用共享令牌加入。etcd 要求服务器数量为奇数,有三台服务器时,该区域可以承受一台服务器的故障:
curl -sfL https://get.k3s.io | K3S_TOKEN=机密_令牌 sh -s - server \
--cluster-init \
--tls-san=api.eu.example.com
curl -sfL https://get.k3s.io | K3S_TOKEN=机密_令牌 sh -s - server \
--server https://203.0.113.21:6443 \
--tls-san=api.eu.example.com
同一区域的所有服务器,在网段和功能方面的设置都必须一致。服务器之间需要开放以下端口:etcd 使用的 2379 至 2380/TCP、API 使用的 6443/TCP、Kubelet 使用的 10250/TCP,以及 Flannel 网络使用的 8472/UDP;对外则保持封锁。令牌属于机密:任何知道它的人,都能把自己的服务器加入集群。
第 4 步:配置对全部三个集群的访问
k3s 把访问凭据保存在 /etc/rancher/k3s/k3s.yaml 中。把每个集群的这个文件复制到您的工作电脑上,将其中的 127.0.0.1 替换为服务器的地址,并按区域为各个上下文命名:
kubectl config rename-context default eu
kubectl config use-context eu
kubectl --context us get nodes
6443 端口上的 API 是进入集群权限最大的入口。请在防火墙中只允许来自您自己的地址或 VPN 的访问,绝不能对整个互联网开放。k3s.yaml 文件包含一张管理员证书,应当像 Root 密码一样妥善保管。
第 5 步:在每个集群中用 Flux 实现 GitOps
为了让三个区域运行同一个应用的同一个版本,期望状态保存在一个 Git 仓库中,每个集群中都运行着 Flux,由它自动实现这一状态。也就是说,每个集群都自行获取自己的配置;不存在可能发生故障的中央交付服务器。一种经过实践检验的仓库结构:
apps/
web/ 应用的共享清单
clusters/
eu/ 欧洲的配置和版本
us/ 北美的配置和版本
asia/ 亚洲的配置和版本
flux bootstrap git --url=ssh://git@git.example.com/infra/fleet.git --branch=main --path=clusters/eu
在各自的上下文中,分别用 --path=clusters/us 和 --path=clusters/asia 执行同一条命令。由于每个区域都有自己的目录,您可以先在一个区域发布新版本,之后再发布到其他区域。
第 6 步:以高可用的方式定义应用
在一个区域内,有三样东西能确保应用挺过维护和服务器故障:分布到不同节点上的多个副本、能识别不健康 Pod 的探针,以及一个防止维护操作同时移除所有 Pod 的中断预算:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web
containers:
- name: web
image: registry.example.com/web:1.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 10
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web
spec:
minAvailable: 1
selector:
matchLabels:
app: web
Readiness 探针会在 Pod 没有响应期间将其从流量中摘除,Liveness 探针则会在 Pod 卡死时将其重启。在单节点集群中,跨多个节点的分布还不起作用;一旦该区域有了三台服务器,它就会自动生效。
第 7 步:Ingress 与证书
入口由自带的 Traefik 负责。由于三个区域都服务于同一个域名,请使用 cert-manager 通过 DNS 验证申请 TLS 证书:无论 DNS 记录当前指向哪里,它在每个区域都能正常工作。相反,HTTP 验证在 DNS 记录当前没有指向的每个区域都会失败。如果您不使用 Kubernetes Ingress,可以在文章配置 nginx 反向代理中找到基础知识。
第 8 步:配置带故障转移的 Geo-DNS
一个支持地理路由和健康检查的 DNS 服务,会把来自欧洲的用户引导到美因河畔法兰克福,把来自美洲的用户引导到美国东海岸,把来自亚洲的用户引导到新加坡,并每隔 30 到 60 秒检查一次各区域的入口是否有响应。某个区域发生故障时,它就不再返回该区域的地址。请将记录的 TTL 设为 60 秒,这样切换通常在一到两分钟内即可完成。如果希望从集群内部维护这些记录,可以使用 external-dns。
第 9 步:逐个区域发布并测试故障
新版本先写入某一个区域的目录,观察该区域几分钟,然后再把这个版本应用到其他区域。这样,一个任何检查都没能发现的错误,最多只会波及一个区域。要演练区域故障,可以在该区域的服务器上停止 k3s,同时检查 Geo-DNS 是否把该区域从解析结果中移除,以及相邻区域是否承接了负载:
systemctl stop k3s
systemctl start k3s
在拥有三台服务器的区域中,还要额外演练单台服务器的维护。kubectl drain 会在遵守第 6 步中断预算的前提下迁移 Pod,kubectl uncordon 则让该服务器重新投入使用:
kubectl drain k3s-eu-2 --ignore-daemonsets --delete-emptydir-data
kubectl uncordon k3s-eu-2
跨区域的数据
Kubernetes 分发的是 Pod,而不是数据。自带 Provisioner 创建的 PersistentVolume 只位于某一个节点上,而集群之间默认根本没有共享的数据存储。因此,凡是涉及数据的部分,都与任何跨多个机房的集群一样,适用相同的规则:
- 数据库自行复制。经过实践检验的做法是:主实例位于一个区域,其他区域运行副本;PostgreSQL 的 CloudNativePG 之类的数据库 Operator 也支持跨集群边界的副本。跨大洲时,复制以异步方式进行,出现紧急情况时,最后几秒的写入可能会丢失。如果需要在全球范围内无损写入,可以使用 CockroachDB 或 YugabyteDB 这类面向多区域的数据库。
- 文件和上传内容应存放在兼容 S3 的对象存储中,并复制到第二个区域。
- 会话保存在经过复制的数据库或缓存中,或者由应用使用签名令牌。
- 备份依然必不可少,因为复制会像传播正确数据一样传播错误。Kubernetes 对象和卷适合用 Velero 备份,基础知识请参阅服务器备份策略。
用 kubeadm 代替 k3s 部署 Kubernetes
使用 kubeadm 时,架构保持不变:每个区域一个集群、GitOps、Geo-DNS。不同的是每个集群的搭建方式。您需要在所有节点上安装 containerd 之类的容器运行时以及 kubeadm、kubelet 和 kubectl,在该区域的 API 前面放置一个负载均衡器或一个虚拟地址(例如借助 kube-vip),然后初始化第一个控制平面节点:
kubeadm init --control-plane-endpoint "api.eu.example.com:6443" --upload-certs
输出中包含两条加入命令:一条带有 --control-plane --certificate-key,用于另外两个控制平面节点;另一条用于 Worker 节点。之后,还要安装 Calico 或 Cilium 之类的网络插件,以及 k3s 本来就自带的 Ingress 控制器。如果您需要有针对性地挑选各个组件,或者必须紧跟上游版本,这些额外的工作就是值得的。
为什么选择 KernelHost 部署跨大洲的 Kubernetes
| 需求 | 为什么重要 | KernelHost 的方案 |
| 多个大洲的机房位置 | 每个区域一个集群,就需要在每个区域都有服务器 | 美因河畔法兰克福,外加欧洲、北美和亚太地区的机房位置,一站式提供 |
| 不限流量 | 镜像下载、数据库复制和备份会持续产生流量 | 无限流量 VPS,没有流量总量限制 |
| 高速存储 | etcd 对慢速磁盘很敏感 | 组建 RAID 的 NVMe SSD |
| DDoS 防护 | 每个 Ingress 都可从公网访问 | 每个机房位置均已包含,在核心机房美因河畔法兰克福采用 3.2 Tbps Arbor 实时过滤,不做黑洞路由 |
| 完整的 Root 权限 | k3s、防火墙和内核设置都需要完全的控制权 | 每一台 KVM Root 服务器和独立服务器均提供 |
| 无合约绑定 | 节点和测试集群随时增减 | PrePaid 预付费,无最低合约期,无开通费 |
| 自动化 | 新节点应当通过脚本创建 | 通过 KernelHost API 下单和控制 |
所有云服务器套餐的概览,以及与大型云服务商的费用对比,请见云服务器租用页面。
常见错误及避免方法
- etcd 横跨多个大洲。后果是写入缓慢、选举不稳定;k3s 使用内置 etcd 时,这种部署方式不受支持。解决方法:每个区域一个集群。
- 一个区域部署两台服务器。etcd 需要多数成员在线,两台服务器无法承受任何一台故障。解决方法:一台或三台。
- API 向整个互联网开放。6443 端口就是集群的万能钥匙。解决方法:只允许来自自己的地址或通过 VPN 访问。
- 中央交付服务器。一旦它发生故障,就什么都无法发布了。解决方法:在每个集群中运行 Flux,由它自行从 Git 获取配置。
- 所有区域同时更新。这样一来,一个错误就会影响所有用户。解决方法:通过仓库中的目录逐个区域更新。
- 通过 HTTP 验证申请证书。在 DNS 记录当前未指向的区域,证书续期会失败。解决方法:DNS 验证。
- 数据库放在本地卷中且没有复制。一旦节点故障,数据就无法访问。解决方法:通过数据库 Operator 实现复制。
- 没有探针,也没有中断预算。不健康的 Pod 仍会继续接收流量,维护时所有 Pod 会同时下线。解决方法:Readiness 探针和 Liveness 探针,再加上 PodDisruptionBudget。
要点总结
- 对于跨多个大洲的 Kubernetes,每个区域一个集群才是正确的做法;横跨所有大洲的单个集群会败在 etcd 的延迟上。
- 入门配置是三台服务器,美国、欧洲和亚洲各一台;这一阶段就已经能够承受整个大洲的故障。
- 扩展阶段,每个区域在同一机房部署三台服务器,这样每个区域也能承受个别服务器的故障。
- 每个集群中的 Flux 从同一个 Git 仓库逐个区域发布应用。
- 带健康检查和短 TTL 的 Geo-DNS 会把用户引导到最近的健康区域,证书通过 DNS 验证获取。
- Kubernetes 分发的是 Pod,而不是数据:数据库需要自己的复制机制,备份依然必不可少。
- 对于这种架构,k3s 通常比 kubeadm 更合适,因为运维三个简单的集群比运维三个复杂的集群更轻松。
常见问题
一个 Kubernetes 集群能横跨多个大洲运行吗?
如何搭建跨多个区域的高可用 Kubernetes?
k3s 和 k8s 有什么区别?
一个高可用的 k3s 集群需要多少台服务器?
k3s 需要哪些端口?
如何将应用发布到多个 Kubernetes 集群?
Kubernetes 区域之间的故障转移是如何实现的?
区域发生故障时,如何保住数据?
跨多个大洲,选 Kubernetes 还是 Docker Swarm?
哪些 KernelHost 机房位置适合部署跨多个大洲的 Kubernetes?
跨三个大洲运行 Kubernetes 的费用是多少?
2026 KernelHost GmbH。保留所有权利。本教程受著作权法保护,未经我们书面同意,不得在其他网站上转载,节选转载或改写后转载同样不被允许。欢迎在注明出处并附上链接的前提下引用。

