複数の大陸にまたがる Kubernetes:k3s と k8s を世界各地のデータセンターで高可用に運用する
米国、ヨーロッパ、アジアにまたがり、大陸が 1 つまるごと停止しても問題ない Kubernetes。etcd が長距離通信に耐えられない理由、リージョンごとの k3s クラスター、Flux による GitOps、Geo-DNS フェイルオーバー、リージョンをまたぐデータ、kubeadm との違いを解説します。
アプリケーションを障害に強く、自動でスケールさせながら動かしたいとき、標準となるのが Kubernetes です。米国、ヨーロッパ、アジアのサーバーに 1 つの Kubernetes クラスターをまたがらせるのは自然な発想ですが、ある 1 点でうまくいきません。Kubernetes がすべての状態を保存するデータベース、etcd です。本記事では、それでも複数の大陸にまたがる Kubernetes を実現する方法を紹介します。しかも、大陸が 1 つまるごと停止しても問題ない構成です。リージョンごとに 1 つのクラスターを置き、GitOps で共通のデプロイを行い、Geo-DNS でユーザーを最寄りの正常なリージョンへ振り分けます。
構成には k3s を使います。k3s は数分でインストールできる軽量なディストリビューションで、Kubernetes 完全準拠の認定を受けています。最後に、kubeadm で構築する従来型の Kubernetes では何が変わるのかも説明します。複数拠点にまたがる可用性、クォーラム、フェイルオーバーの基礎は、記事 3 大陸にまたがる Docker Swarm で詳しく解説しています。本記事では、Kubernetes ならではの違いを取り上げます。
全大陸で 1 つのクラスターか、リージョンごとに 1 つのクラスターか?
複数の大陸にまたがる Kubernetes では、全大陸をまたぐ 1 つのクラスターではなく、リージョンごとに 1 つのクラスターを置くのが正しいアーキテクチャです。 各クラスターは独立して動作し、すべて同じ Git リポジトリからデプロイされ、ヘルスチェック付きの Geo-DNS がユーザーを振り分けます。あるリージョンが停止しても、共通のクラスター状態を海を越えてすり合わせる必要はなく、ほかのリージョンが処理を引き継ぎます。
etcd が長距離通信に耐えられない理由
Kubernetes は、あらゆる状態、設定、変更を etcd に保存します。etcd は Raft コンセンサスを採用したキーバリューストアです。書き込みのたびに、etcd の全メンバーの過半数から確認を得る必要があります。etcd の既定値では、ハートビート間隔が 100 ミリ秒、リーダー選出のタイムアウトが 1 秒です。一方、ヨーロッパ、北米、アジアの間では、パケットの遅延が 80~250 ミリ秒に達します。これらの値は引き上げられますが、そうするとクラスター内のあらゆる書き込みが遅くなります。Pod のスケジューリングから Secret の保存まで、すべてです。この点について、k3s のドキュメントの記述は明確です。組み込みの etcd は複数のネットワークに分散したクラスターではサポートされておらず、すべてのサーバーを同じ拠点に置くべきだとしています。Kubernetes 自体も、1 つのクラスターがカバーするのは 1 つのリージョン内の複数のゾーンまでという前提で設計されており、複数の大陸は想定していません。
3 つのパターンの比較
| パターン | 仕組み | 評価 |
| 全大陸にまたがる 1 つのクラスター | コントロールプレーンと etcd を複数の大陸に分散 | 非推奨:書き込みが遅く、etcd が不安定になり、組み込み etcd を使う k3s ではサポート対象外 |
| コントロールプレーンは 1 つのリージョン、ワーカーは世界各地 | サーバーを 1 つの拠点に置き、エージェントをほかの大陸に配置 | 技術的には動作するが、コントロールプレーンのあるリージョンが停止すると、世界のどこでも再スケジューリングができなくなる |
| リージョンごとに 1 つのクラスター | 独立した 3 つのクラスターを GitOps でまとめてデプロイし、前段に Geo-DNS を配置 | 推奨:どのリージョンもほかのリージョンの停止を乗り切れ、障害の影響は 1 つのリージョンにとどまる |
「リージョンごとに 1 つのクラスター」という方式には、見落とされがちなもう 1 つの利点があります。コントロールプレーンの障害、クラスターへの誤った変更、失敗したアップグレードの影響は、常に 1 つのリージョンにしか及びません。ほかのリージョンのユーザーは、そのことにまったく気づきません。
k3s か k8s か?
どちらも同じ API、同じマニフェスト、同じツールを使う本物の Kubernetes です。違いは、構成と手間にあります。
| 項目 | k3s | kubeadm による k8s |
| インストール | コマンド 1 つ、バイナリファイル 1 つ | コンテナランタイム、kubeadm、kubelet、ネットワークプラグインを個別にセットアップ |
| サーバーノード 1 台あたりのリソース | 最低 2 コア、2 GB RAM | コンポーネント次第で大幅に増える |
| 同梱されるもの | Ingress コントローラー(Traefik)、ネットワーク(Flannel)、ストレージプロビジョナー、Service ロードバランサー | 中核部分のみで、それ以外はすべてご自身で選ぶ |
| 高可用性 | 3 台のサーバーによる組み込み etcd | 3 台のコントロールプレーンノードと、API の前段のロードバランサー |
| 向いているケース | ほとんどのアプリケーション、小規模なチーム、リージョンごとのシングルノード構成 | すべてのコンポーネントを自分たちで決めたいチーム |
リージョンごとに 1 つのクラスターを置くアーキテクチャには、k3s をおすすめします。3 つのクラスターを無理なく運用できるのは、1 つひとつがシンプルな場合だけだからです。すでに kubeadm の経験がある方は、このアーキテクチャをそのまま採用できます。違いは、後述の kubeadm のセクションで説明します。
アーキテクチャ:3 つのリージョン、3 つのクラスター、1 つの入口
| 構成要素 | 役割 | カバーできる障害 |
| リージョンごとに 1 つの k3s クラスター | ユーザーの近くでアプリケーションを実行する | リージョン全体の停止 |
| リージョンごとに 3 台のサーバー(拡張段階) | リージョン内で etcd とコントロールプレーンを高可用に保つ | リージョン内の個々のサーバーの停止 |
| Git リポジトリとクラスターごとの Flux | 各クラスターが望ましい状態を自ら Git から取得する | 中央のデプロイサーバーという障害点をなくす |
| DNS チャレンジで証明書を取得する Ingress | ユーザーからのリクエストを受け付ける | DNS の切り替えに関係なく証明書が機能する |
| ヘルスチェック付きの Geo-DNS | ユーザーを最寄りの正常なリージョンへ振り分ける | 到達できなくなったリージョン |
| レプリケーションによるデータ保持とバックアップ | 複数のリージョンにデータを保持する | リージョン停止時のデータ損失 |
最初の構成と拡張
最初の構成は、米国、ヨーロッパ、アジアに 1 台ずつ置いた 3 台のサーバーで、それぞれが独立したシングルノードクラスターです。サーバーが 1 台停止するとそのリージョンが停止し、Geo-DNS がそのリージョンのユーザーを隣のリージョンへ送ります。この段階ですでに、大陸 1 つがまるごと停止する事態を想定した設計になっています。拡張段階では、各リージョンの同じ拠点にサーバーを 3 台ずつ置きます。そうすれば、各リージョンが単独でサーバー 1 台の停止を乗り切れるようになり、ユーザーを別のリージョンへ振り向ける必要もなくなります。
適した KernelHost の拠点
KernelHost はフランクフルト・アム・マインの maincubes データセンターでサーバーを運用しており、ヨーロッパ、北米、アジア太平洋の各地にある拠点でも仮想サーバーを提供しています。米国の 3 拠点、カナダ、ロンドン、ストラスブール、ワルシャワ、ヘルシンキ、シンガポール、日本、シドニー、ムンバイなどです。全拠点の一覧は サーバーロケーション のページに掲載しています。本記事の例では、ヨーロッパにフランクフルト・アム・マイン、北米に米国東海岸、アジアにシンガポールを使います。
手順:k3s で 3 つのリージョンに Kubernetes を構築する
例では、Debian 12 または 13 を載せた 3 台のサーバー、k3s-eu、k3s-us、k3s-asia を使います。パブリックアドレスはドキュメント用ネットワーク 203.0.113.0/24 のもので、ドメインは example.com です。どちらもご自身の値に置き換えてください。
ステップ 1:3 つのリージョンにサーバーを用意する
3 台のサーバーを 3 つのリージョンで注文します。k3s に加えてアプリケーションも動かせるよう、最低でも 2 vCPU と 4 GB RAM を選んでください。新しいルートサーバーのチェックリスト にある基本的なセキュリティ対策を実施し、役割がわかるホスト名を付けます。etcd は高速な SSD の恩恵をはっきりと受けます。KernelHost サーバーの NVMe ストレージは、この条件を満たしています。
ステップ 2:k3s をインストールする
3 台のサーバーそれぞれに、コマンド 1 つで k3s をインストールします。これで各サーバーが、ノード 1 つからなる完全な Kubernetes クラスターになります。
curl -sfL https://get.k3s.io | sh -
kubectl get nodes
1 分ほどで、kubectl get nodes がノードを Ready と表示します。Ingress コントローラーの Traefik、ネットワークの Flannel、ローカルボリューム用のプロビジョナーが同梱されています。
ステップ 3:リージョンごとに 3 台のサーバーへ拡張する
リージョン自体を高可用にしたい場合は、同じ拠点に 3 台のサーバーを置きます。1 台目のサーバーが組み込みの etcd を起動し、残りの 2 台は共通のトークンを使って参加します。etcd には奇数台のサーバーが必要で、3 台構成ならリージョンはサーバー 1 台の停止に耐えられます。
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
1 つのリージョン内のサーバーはすべて、ネットワーク範囲と機能について同じ設定にする必要があります。サーバー間では、etcd 用の 2379~2380/TCP、API 用の 6443/TCP、kubelet 用の 10250/TCP、Flannel ネットワーク用の 8472/UDP の各ポートを開けておく必要があり、外部からは遮断したままにします。トークンは秘密情報です。これを知っている人は、自分のサーバーをクラスターに参加させられます。
ステップ 4:3 つのクラスターすべてへのアクセスを設定する
k3s は認証情報を /etc/rancher/k3s/k3s.yaml に保存します。各クラスターのこのファイルを作業用の PC にコピーし、ファイル内の 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 を導入する
3 つのリージョンすべてで同じアプリケーションを同じバージョンで動かすために、望ましい状態を 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 を指定して実行します。リージョンごとに専用のディレクトリがあるため、新しいバージョンをまず 1 つのリージョンにロールアウトし、ほかのリージョンにはその後で展開できます。
ステップ 6:アプリケーションを障害に強い形で定義する
リージョン内では、次の 3 つによって、アプリケーションがメンテナンスやサーバーの停止を乗り切れるようになります。異なるノードに分散配置される複数のレプリカ、異常な 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 が応答しない間、その Pod にトラフィックを送らないようにします。Liveness プローブは、Pod が応答不能に陥ったときに再起動します。シングルノードのクラスターでは複数ノードへの分散はまだ効果がありませんが、リージョンのサーバーが 3 台になれば自動的に有効になります。
ステップ 7:Ingress と証明書
入口の役割は、同梱の Traefik が担います。3 つのリージョンはすべて同じドメインを提供するため、TLS 証明書は cert-manager を使い、DNS チャレンジで取得します。DNS チャレンジなら、DNS レコードがその時点でどこを指しているかに関係なく、どのリージョンでも機能します。一方、HTTP チャレンジは、レコードがその時点で指していないリージョンではどこでも失敗します。Kubernetes の Ingress を使わない場合の基礎は、記事 nginx をリバースプロキシとして設定する で解説しています。
ステップ 8:フェイルオーバー付きの Geo-DNS を設定する
Geo ルーティングとヘルスチェックを備えた DNS サービスは、ヨーロッパのユーザーをフランクフルト・アム・マインへ、アメリカ大陸のユーザーを米国東海岸へ、アジアのユーザーをシンガポールへ振り分け、各リージョンの入口が応答しているかを 30~60 秒ごとに確認します。あるリージョンが停止すると、DNS サービスはそのリージョンのアドレスを返さなくなります。レコードの TTL を 60 秒に設定しておけば、切り替えはたいてい 1~2 分で完了します。レコードをクラスター側から管理したい場合は、external-dns を利用できます。
ステップ 9:リージョンごとに順番にロールアウトし、障害をテストする
新しいバージョンは、まず 1 つのリージョンのディレクトリに記述し、そのリージョンを数分間観察してから、ほかのリージョンにも適用します。こうすれば、どのチェックにも引っかからない不具合があっても、影響は最大で 1 つのリージョンにとどまります。リージョンの停止は、そのリージョンのサーバーで k3s を止めて予行演習します。その際、Geo-DNS がそのリージョンを応答から外し、隣のリージョンが負荷を引き受けるかを確認します。
systemctl stop k3s
systemctl start k3s
サーバーが 3 台あるリージョンでは、サーバー 1 台のメンテナンスも予行演習します。kubectl drain はステップ 6 のバジェットを守りながら Pod を移し、kubectl uncordon はサーバーを運用に戻します。
kubectl drain k3s-eu-2 --ignore-daemonsets --delete-emptydir-data
kubectl uncordon k3s-eu-2
リージョンをまたぐデータ
Kubernetes が分散するのは Pod であり、データではありません。 同梱のプロビジョナーが作る PersistentVolume はちょうど 1 つのノード上にあり、クラスター間には標準では共有のデータストレージがまったくありません。そのため、データを扱うものについては、複数拠点にまたがるどのクラスターとも同じことが当てはまります。
- データベースは自らレプリケーションを行います。実績があるのは、1 つのリージョンにプライマリインスタンスを置き、ほかのリージョンにレプリカを置く構成です。PostgreSQL 向けの CloudNativePG のようなデータベースオペレーターは、クラスターの境界を越えたレプリカにも対応しています。大陸間のレプリケーションは非同期で行われるため、万一の際には直近数秒分の書き込みが失われる可能性があります。世界規模でデータを失わずに書き込みたい場合は、CockroachDB や YugabyteDB のようなマルチリージョン対応のデータベースがあります。
- ファイルとアップロードは、2 つ目のリージョンへレプリケーションされる S3 互換のオブジェクトストレージに保存します。
- セッションは、レプリケーションされたデータベースかキャッシュに保存するか、アプリケーション側で署名付きトークンを使います。
- バックアップは引き続き欠かせません。レプリケーションは、正しいデータと同じように誤りも複製してしまうからです。Kubernetes のオブジェクトとボリュームには Velero が適しています。基礎については サーバーのバックアップ戦略 をご覧ください。
k3s の代わりに kubeadm で Kubernetes を構築する
kubeadm でもアーキテクチャは変わりません。リージョンごとに 1 つのクラスター、GitOps、Geo-DNS です。異なるのは、各クラスターの構築方法です。すべてのノードに containerd などのコンテナランタイムと kubeadm、kubelet、kubectl をインストールし、リージョンの API の前段にロードバランサーか、kube-vip などを使った仮想アドレスを置いてから、1 台目のコントロールプレーンノードを初期化します。
kubeadm init --control-plane-endpoint "api.eu.example.com:6443" --upload-certs
出力には参加用のコマンドが 2 つ含まれています。1 つは残り 2 台のコントロールプレーンノード用の --control-plane --certificate-key 付きのコマンド、もう 1 つはワーカー用のコマンドです。その後、Calico や Cilium などのネットワークプラグインと、k3s なら最初から同梱されている Ingress コントローラーをインストールします。この追加の手間に見合うのは、個々のコンポーネントを意図して選びたい場合や、アップストリームのバージョンに厳密に合わせる必要がある場合です。
複数の大陸にまたがる Kubernetes に KernelHost を選ぶ理由
| 要件 | 重要な理由 | KernelHost では |
| 複数の大陸にある拠点 | リージョンごとに 1 つのクラスターを置くには、各リージョンにサーバーが必要 | フランクフルト・アム・マインに加え、ヨーロッパ、北米、アジア太平洋の拠点を 1 社でまとめて提供 |
| トラフィック無制限 | イメージのダウンロード、データベースのレプリケーション、バックアップが継続的に通信を生む | 転送量の上限がない 無制限トラフィック VPS |
| 高速なストレージ | etcd は遅いディスクの影響を受けやすい | RAID 構成の NVMe SSD |
| DDoS 対策 | どの Ingress もインターネットから到達できる | すべての拠点で標準付属、中核拠点のフランクフルト・アム・マインでは 3.2 Tbps の Arbor リアルタイムフィルタリングを備え、null-routing は行わない |
| 完全な root 権限 | k3s、ファイアウォール、カーネル設定には完全な制御が必要 | すべての KVM ルートサーバーと専用サーバーで利用可能 |
| 契約の縛りなし | ノードやテスト用クラスターは増えたり減ったりする | PrePaid 方式、最低利用期間なし、初期費用なし |
| 自動化 | 新しいノードはスクリプトで作成したい | KernelHost API による注文と操作 |
すべてのクラウドプランの概要と、大手クラウド事業者とのコスト比較は、クラウドサーバーレンタル のページでご覧いただけます。
よくある失敗とその回避策
- etcd を大陸をまたいで構成している。 書き込みが遅くなり、リーダー選出が不安定になります。組み込み etcd を使う k3s では、そもそもサポートされていません。対策:リージョンごとに 1 つのクラスターを置く。
- 1 つのリージョンにサーバーが 2 台。 etcd には過半数が必要なので、2 台では 1 台の停止にも耐えられません。対策:1 台か 3 台にする。
- API をインターネット全体に公開している。 ポート 6443 はクラスターのマスターキーです。対策:自分のアドレスからか、VPN 経由でのみ許可する。
- 中央のデプロイサーバーに頼っている。 そのサーバーが停止すると、何もデプロイできなくなります。対策:各クラスターで Flux を動かし、それぞれが Git から直接取得するようにする。
- すべてのリージョンを同時に更新している。 不具合があると、すべてのユーザーが影響を受けます。対策:リポジトリのディレクトリを使い、リージョンごとに順番に更新する。
- 証明書を HTTP チャレンジで取得している。 DNS レコードがその時点で指していないリージョンでは、更新に失敗します。対策:DNS チャレンジを使う。
- データベースをレプリケーションなしでローカルボリュームに置いている。 ノードが停止すると、データにアクセスできなくなります。対策:データベースオペレーターでレプリケーションする。
- プローブもバジェットも設定していない。 異常な Pod にもトラフィックが流れ続け、メンテナンスですべての Pod が同時に停止します。対策:Readiness プローブと Liveness プローブに加えて PodDisruptionBudget を設定する。
まとめ
- 複数の大陸にまたがる Kubernetes では、リージョンごとに 1 つのクラスターを置くのが正解です。全大陸をまたぐ 1 つのクラスターは、etcd のレイテンシがネックになってうまくいきません。
- 最初の構成は、米国、ヨーロッパ、アジアに 1 台ずつ置いた 3 台のサーバーです。この段階ですでに、大陸 1 つがまるごと停止しても乗り切れます。
- 拡張段階では、各リージョンの同じ拠点にサーバーを 3 台ずつ置きます。これで、各リージョンが個々のサーバーの停止にも耐えられるようになります。
- 各クラスターの Flux が、同じ Git リポジトリからアプリケーションをリージョンごとに順番にデプロイします。
- ヘルスチェックと短い TTL を設定した Geo-DNS がユーザーを最寄りの正常なリージョンへ振り分け、証明書は DNS チャレンジで取得します。
- Kubernetes が分散するのは Pod であり、データではありません。データベースには独自のレプリケーションが必要で、バックアップも引き続き欠かせません。
- このアーキテクチャには、たいてい kubeadm より k3s のほうが適しています。シンプルなクラスター 3 つのほうが、複雑なクラスター 3 つよりも運用しやすいからです。
よくあるご質問
Kubernetes クラスターを複数の大陸にまたがって運用できますか?
Kubernetes を複数のリージョンにまたがって高可用に構築するには、どうすればよいですか?
k3s と k8s の違いは何ですか?
高可用な k3s クラスターには、何台のサーバーが必要ですか?
k3s にはどのポートが必要ですか?
複数の Kubernetes クラスターにアプリケーションをデプロイするには、どうすればよいですか?
Kubernetes のリージョン間のフェイルオーバーは、どのように機能しますか?
リージョンが停止したとき、データはどうやって守られますか?
複数の大陸にまたがる構成には、Kubernetes と Docker Swarm のどちらが向いていますか?
複数の大陸にまたがる Kubernetes に適した KernelHost の拠点はどこですか?
3 つの大陸にまたがる Kubernetes の費用はどのくらいですか?
2026 KernelHost GmbH。無断複写・転載を禁じます。本ガイドは著作権により保護されております。他のウェブサイトへの掲載は、一部のみの場合や編集を加えた場合であっても、当社の書面による同意なしには認められません。出典の明記とリンクを添えた引用は歓迎いたします。

