【チュートリアル】ゼロから始める最小限の高可用性 k3s クラスタ構築

著者序
本文は 私のブログサイト から転載したもので、原文のアドレスは: 【チュートリアル】ゼロから始める最小限の高可用性 k3s クラスタ構築
すべてが始まる前に、以下の点にご注意ください:
- 本文で使用する開発環境は ArchLinux + Fish Shell です。システム間のコマンド構文は完全に互換性がない場合があるため、実際の状況に応じてコマンド形式を調整してください。
- 本文のコマンドは各種 CLI ツール(本文で標注されているか、前置チュートリアルで既にインストール済みで、Linux システムの一般的な CLI ツールは本文で標注されていません)に依存しています。CLI ツールのインストール プロセスは複雑で、各プラットフォームのインストール手順が異なるため、ここでは詳しく説明しません。公式ドキュメントのインストール チュートリアルを参照してください。
はじめに
ご存知の通り、k3s は Rancher Labs が開発した軽量な Kubernetes ディストリビューションであり、現在最も人気のある K8s 軽量化ソリューションです。
従来の運用方法(1Panel/宝塔/SSH など)と比較して、k3s の学習曲線はより急峻で、より多くのコンテナ オーケストレーション概念を理解する必要があります。しかし、一度習得すれば、以下を得ることができます:
k3s のコア上の利点
- 軽量で効率的 - 単一バイナリ ファイル、メモリ使用量 < 512MB、低スペック VPS に完璧に適応
- 本番環境対応 - Kubernetes API と完全互換、標準 K8s への円滑な移行が可能
- 宣言型運用 - YAML で期望状態を記述し、システムが自動的に維持
- 高可用性保証 - 自動フェイルオーバー + マルチノード ロード バランシング
- すぐに使用可能 - ネットワーク、ストレージ、Ingress などのコア コンポーネントが組み込まれている
k3s を通じて、複数の低価��� VPS を企業級の高可用性クラスタに統合し、従来の運用では達成困難な自動化レベルを実現できます。
対象読者と準備
適切な対象者
- Linux の基本知識を持つ開発者
- 従来の運用からコンテナ オーケストレーションへの移行を希望する方
- 個人用の高可用性サービスを構築したい技術愛好家
前置知識
- Linux コマンドライン操作に精通している
- Docker コンテナの基本を理解している
- 基本的なネットワーク知識(SSH、ファイアウォール)を持つ
学習成果
このチュートリアルを完了すると、以下を習得できます:
- k3sup を使用した k3s クラスタの迅速なデプロイ
- k3s コア コンポーネント(API Server、etcd、kubelet など)の役割の理解
- デフォルト コンポーネントの置き換えによるパフォーマンス最適化(Cilium CNI、Nginx Ingress など)
- 最初のアプリケーションのデプロイと外部アクセスの設定
- 基本的なクラスタ運用とトラブルシューティング スキル
デプロイ計画
k3s はデフォルトで一連の簡潔なコンポーネントをインストールしますが、本番レベルの要件を満たすために、どのモジュールを保持または置き換えるかを事前に計画する必要があります。以下の表は推奨される取捨選択戦略を示しています:
| コンポーネント タイプ | k3s デフォルト コンポーネント | 置き換えコンポーネント | 置き換え理由 | k3sup 無効化パラメータ |
|---|---|---|---|---|
| コンテナ ランタイム | containerd | - | デフォルトを保持 | - |
| データ ストレージ | SQLite / etcd | - | シングル ノードは SQLite、クラスタは etcd | - |
| Ingress Controller | Traefik | Nginx Ingress / その他 | チーム習熟度、機能要件の違い | --disable traefik |
| LoadBalancer | Service LB (Klipper-lb) | 外部ロード バランサー | クラウド プロバイダー(例:Cloudflare)提供のロード バランサーはより成熟で強力 | --disable servicelb |
| DNS | CoreDNS | - | デフォルトを保持 | - |
| Storage Class | Local-path-provisioner | Longhorn | 分散ストレージ、高可用性、バックアップ機能 | --disable local-storage |
| CNI | Flannel | Cilium | eBPF パフォーマンス、ネットワーク ポリシー、可観測性 | --flannel-backend=none --disable-network-policy |
環境準備
必須ツール
クラスタを構築する前に、3 つの CLI ツール(k3sup、kubectl、Helm)を準備する必要があります。各自の公式ドキュメントを参照してインストールしてください。ここでは詳しく説明しません。インストール完了後、k3sup version、kubectl version、helm version などのコマンドで検証できます。

サーバー要件
最低 3 台のクラウド サーバー(サンプル環境は Ubuntu 24.04 を使用)を準備し、最小限の 3 ノード高可用性コントロール プレーン として機能させます(推奨構成 ≥ 4C4G)。各ノードの IP を事前に記録し、SSH 公開鍵が配布されていることを確認し、ローカル秘密鍵のパスを把握してください。後続のステップで使用します。
初期コントロール プレーンのデプロイ
k3sup を使用して初期コントロール ノードをデプロイします:
k3sup install \
--ip 初期ノード IP \
--user root \
--ssh-key 秘密鍵の場所 \
--k3s-channel latest \
--cluster \
--k3s-extra-args "--disable traefik --disable servicelb --disable local-storage --flannel-backend=none --disable-network-policy"
図に示すように:

インストール完了後、k3sup は自動的に kubeconfig を現在のディレクトリにコピーします。この設定を直接使用することもできますが、より堅牢な方法は既存の設定ファイルとマージすることです。
kubeconfig のマージ
- 現在の kubeconfig をバックアップします(デフォルトは
~/.kube/configに位置します):
cp ~/.kube/config ~/.kube/config.backup
fish shell(参考用):
cp $KUBECONFIG {$KUBECONFIG}.backup
- 新旧の kubeconfig をフラット ファイルにマージします:
KUBECONFIG=~/.kube/config:./kubeconfig kubectl config view --flatten > ~/.kube/config.new
fish shell(参考用):
KUBECONFIG=$KUBECONFIG:./kubeconfig kubectl config view --flatten > kubeconfig-merged.yaml
- 新しいファイルの内容を確認した後、古い設定を上書きします:
mv ~/.kube/config.new ~/.kube/config
fish shell:
mv ./kubeconfig-merged.yaml $KUBECONFIG
- 新しいコンテキストが有効になったことを確認します:
kubectl config get-contexts
kubectl config use-context default
Cilium のインストール(Flannel の置き換え)
k3s をインストールするときにデフォルト CNI(Flannel)を無効にしたため、ノード間は一時的に通信できません。計画に従って Cilium をデプロイし、ネットワークとネットワーク ポリシー機能を提供します。
Helm を使用して Cilium をインストールします:
# Cilium Helm リポジトリを追加
helm repo add cilium https://helm.cilium.io/
# Helm リポジトリを更新
helm repo update
# Cilium CNI をインストール(シングル レプリカ、デフォルト モード)
helm install cilium cilium/cilium \
--namespace kube-system \
--set operator.replicas=1 \
--set ipam.mode=kubernetes
クラスタが既に kube-proxy を無効にしている場合、
--set kubeProxyReplacement=strictを追加できます。このチュートリアルではより多くのシナリオとの互換性を保つためデフォルト値を保持します。
実行完了後、以下が表示されます:

Cilium コンポーネントの起動完了を待ちます:
# Cilium 関連 Pod の状態を確認
kubectl get pods -n kube-system -l k8s-app=cilium
# ノード状態を確認(NotReady から Ready に変わるはず)
kubectl get nodes

ノードが Cilium に切り替わった後、NotReady から Ready に変わるはずです。次に k3sup を使用して他の 2 つのコントロール ノードを追加します。
コントロール プレーンのスケーリング
高可用性を実現するには、最低 3 つのコントロール ノードが必要です。以下のコマンドを使用して 2 番目のコントロール ノードを追加します:
k3sup join \
--ip 2 番目のノード IP \
--user root \
--ssh-key 秘密鍵の場所 \
--server-ip 初期ノード IP \
--server \
--k3s-channel latest \
--k3s-extra-args "--disable traefik --disable servicelb --disable local-storage --flannel-backend=none --disable-network-policy"
同じ方法で 3 番目のコントロール ノードを追加します:
k3sup join \
--ip 3 番目のノード IP \
--user root \
--ssh-key 秘密鍵の場所 \
--server-ip 初期ノード IP \
--server \
--k3s-channel latest \
--k3s-extra-args "--disable traefik --disable servicelb --disable local-storage --flannel-backend=none --disable-network-policy"
数分待ってからクラスタ状態を確認します:
kubectl get nodes
3 つのコントロール ノードがすべて Ready 状態になったら、コントロール プレーンの構築は完了です。

Nginx Ingress Controller のインストール
k3s デフォルトの Traefik を置き換え、Nginx Ingress Controller をインストールします:
# Nginx Ingress Helm リポジトリを追加
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# Nginx Ingress Controller をインストール(長時間待つ必要がある場合があります)
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.hostPort.enabled=true \
--set controller.hostPort.ports.http=80 \
--set controller.hostPort.ports.https=443 \
--set controller.service.type=ClusterIP
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
hostPort を有効にしたため、Service タイプは ClusterIP として表示され、クラウド プロバイダーの外部アドレ���は自動的に割り当てられません。例えば:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller ClusterIP 10.43.25.32 <none> 80/TCP,443/TCP 1d
ingress-nginx-controller-admission ClusterIP 10.43.184.196 <none> 443/TCP 1d
この場合、エントリ ポイントは実際には各コントロール ノード自体のパブリック IP です。kubectl get nodes -o wide または直接クラウド ホスト パネルを照会して確認できます。後で Cloudflare DNS を設定するときにこの IP を入力します。

Longhorn 分散ストレージのインストール
Longhorn は Rancher が開発したクラウド ネイティブ分散ブロック ストレージ システムで、高可用性、バックアップ、スナップショットなどのエンタープライズ グレード機能を提供します。k3s デフォルトの local-path-provisioner と比較して、Longhorn はノード間の永続ストレージをサポートしています。
前置依存関係チェック
Longhorn をインストールする前に、各ノードに必要な依存関係がインストールされていることを確認する必要があります。Ansible を使用してバッチ処理したい場合は、後続の例を参照してください:
# 各ノードで実行(SSH 経由)
# open-iscsi をチェックしてインストール
apt update
apt install -y open-iscsi nfs-common
# 起動して自動起動を設定
systemctl enable --now iscsid
systemctl status iscsid
Ansible を使用して依存関係をバッチ インストールしたい場合は、以下のタスク スニペットを参照できます:
---
- name: Setup K3s nodes with Longhorn dependencies and CrowdSec
hosts: k3s
become: true
vars:
crowdsec_version: "latest"
tasks:
# ============================================
# Longhorn Prerequisites
# ============================================
- name: Install Longhorn required packages
ansible.builtin.apt:
name:
- open-iscsi # ボリューム マウント用の iSCSI サポート
- nfs-common # バックアップ ターゲット用の NFS サポート
- util-linux # nsenter およびその他のユーティリティを提供
- curl # ダウンロードと API 呼び出し用
- jq # Longhorn CLI 用の JSON 処理
state: present
update_cache: true
tags: longhorn
- name: Enable and start iscsid service
ansible.builtin.systemd:
name: iscsid
enabled: true
state: started
tags: longhorn
- name: Load iscsi_tcp kernel module
community.general.modprobe:
name: iscsi_tcp
state: present
tags: longhorn
- name: Ensure iscsi_tcp loads on boot
ansible.builtin.lineinfile:
path: /etc/modules-load.d/iscsi.conf
line: iscsi_tcp
create: true
mode: '0644'
tags: longhorn
- name: Check if multipathd is installed
ansible.builtin.command: which multipathd
register: multipathd_check
failed_when: false
changed_when: false
tags: longhorn
- name: Disable multipathd if installed (conflicts with Longhorn)
ansible.builtin.systemd:
name: multipathd
enabled: false
state: stopped
when: multipathd_check.rc == 0
tags: longhorn
Longhorn のデプロイ
Helm を使用して Longhorn をインストールします:
# Longhorn Helm リポジトリを追加
helm repo add longhorn https://charts.longhorn.io
helm repo update
# Longhorn をインストール(長時間待つ必要がある場合があります)。ここではディスク容量を節約するためシングル レプリカのみを設定しています。必要に応じて調整してください
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultDataPath="/var/lib/longhorn" \
--set persistence.defaultClass=true \
--set persistence.defaultClassReplicaCount=1
Longhorn コンポーネントの起動を待ちます:
# Longhorn コンポーネント状態を確認
kubectl get pods -n longhorn-system
# StorageClass を確認
kubectl get storageclass

Longhorn UI へのアクセス(オプション)
Longhorn は、ストレージ ボリュームを管理するための Web UI を提供しています。ポート フォワーディングを使用して一時的にアクセスできます:
# ローカルにポート フォワーディング
kubectl port-forward -n longhorn-system svc/longhorn-frontend 8081:80
# ブラウザで http://localhost:8081 にアクセス
確認完了後、ターミナルで
Ctrl+Cを押してポート フォワーディングを停止し、ローカル ポートの継続的な占有を避けてください。
Agent ノードの追加
ここまでで、3 ノードの高可用性コントロール プレーン(control-plane)を構築しました。本番環境では、control-plane ノード上で実際のアプリケーション プログラムを実行したくありません(API Server、etcd などのコア コンポーネントのリソースを占有します)。
したがって、ワークロード(Pods)を実行するための専用 Agent ノード(Worker ノードとも呼ばれます)を追加する必要があります。
インストール前置依存関係
control-plane ノードと同様に、Agent ノードも Longhorn の依存関係を満たす必要があります(これらのノードが永続ボリュームをスケジュールおよび保存できるようにする場合)。
追加する予定のすべての Agent ノードで、事前に以下のコマンドを実行します:
# 各 Agent ノードで実行(SSH 経由)
apt update
apt install -y open-iscsi nfs-common
# 起動して自動起動を設定
systemctl enable --now iscsid
追加コマンドの実行
Agent ノードを追加するコマンドは control-plane ノードを追加するコマンドとほぼ同じですが、2 つの重要な違いがあります: --server フラグを使用せず、追加パラメータはありません。
k3sup join \
--ip <AGENT_ノード IP> \
--user root \
--ssh-key <秘密鍵の場所> \
--server-ip <任意のコントロール ノード IP> \
--k3s-channel latest
ノード状態の確認
複数の Agent ノードを一度に追加できます。追加完了後、数分待つと Cilium と Longhorn のコンポーネントが新しいノードに自動的にスケジュールされます。
kubectl を使用してクラスタ状態を確認します:
kubectl get nodes -o wide
新しく追加されたノードが表示され、ROLE 列は <none> として表示されるはずです(k3s では、<none> は agent/worker ロールを意味します)。
同時に、Cilium と Longhorn の Pod が新しいノード上で正常に起動したかどうかを監視できます:
# Cilium agent は新しいノード上で起動するはず
kubectl get pods -n kube-system -o wide
# Longhorn instance-manager も新しいノード上で起動するはず
kubectl get pods -n longhorn-system -o wide
これでクラスタは高可用性コントロール プレーンとアプリケーション実行用のワーカー ノードを備えています。
Argo CD のインストール(GitOps 継続的デリバリー)
クラスタに宣言型継続的デリバリー機能を持たせるために、Argo CD をインストールできます。これにより、Git リポジトリ内の Kubernetes マニフェストをクラスタに自動的に同期でき、前述でデプロイした Cilium、Ingress、Longhorn などのコンポーネントと組み合わせて完全な GitOps フローを構成できます。
インストール手順
# Argo Helm リポジトリを追加して索引を更新
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
# argocd 名前空間に Argo CD をインストール(デフォルトは ClusterIP Service を使用)
helm install argocd argo/argo-cd \
--namespace argocd \
--create-namespace \
--set controller.replicas=2 \
--set redis-ha.enabled=false \
--set server.service.type=ClusterIP
上記のパラメータの意味は以下の通りです:
argocd: インストール release の名前。後続のアップグレード/アンインストールはこの名前に依存します。argo/argo-cd:argoHelm リポジトリのargo-cdchart を使用します。--namespace argocd: すべてのリソースをargocd名前空間にインストールします。--create-namespace: 名前空間が存在しない場合は自動的に作成します。--set controller.replicas=2: Argo CD コントローラー レプリカ数を 2 に設定し、高可用性を向上させます。--set redis-ha.enabled=false: Redis HA を無効にします。デフォルトのシングル インスタンスはほとんどの開発/テスト シナリオに対応できます。--set server.service.type=ClusterIP: server サービスは ClusterIP タイプを使用します。後で Ingress を通じて外部アクセスを公開します。
インストール完了後、すべてのコンポーネントの準備ができるまで待ちます:
kubectl get pods -n argocd
以下のような出力が表示され、コア コンポーネントがすべて正常に起動したことを示すはずです:
NAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 30s
argocd-application-controller-1 1/1 Running 0 17s
argocd-applicationset-controller-6bf5957996-xnn7c 1/1 Running 0 30s
argocd-dex-server-7cb4b74df8-vqkdv 1/1 Running 0 30s
argocd-notifications-controller-5cbffcc56d-9gntp 1/1 Running 0 30s
argocd-redis-b5f4d9475-584fs 1/1 Running 0 30s
argocd-redis-secret-init-sggs2 0/1 Completed 0 47s
argocd-repo-server-7687bd88c6-4ksfp 1/1 Running 0 30s
argocd-server-67ccc4d44c-6th5p 1/1 Running 0 30s
その中で:
argocd-application-controller-*: Application リソース状態の同期を担当します。高可用性を向上させるために 2 つのレプリカを設定しました。argocd-applicationset-controller: ApplicationSet CRD のオーケストレーションを担当します。argocd-dex-server: Dex 認証サービスを提供します。argocd-notifications-controller: 通知とアラート機能を実装します。argocd-redis-*: Argo CD の組み込み Redis。クラスタ状態をキャッシュするために使用されます(redis-secret-initJob がキーを初期化した後Completed状態になるのは正常です)。argocd-repo-server: Git リポジトリの同期とテンプレート レンダリングを処理します。argocd-server: Web/UI と gRPC API を提供します。
すべての Pod の状態が Running または Completed であれば、Argo CD のインストールは成功し、アクセス エントリ ポイントまたはアプリケーション同期の設定を続行できます。
Web UI へのアクセス
既に ingress-nginx をデプロイしているため、Ingress を通じて Argo CD UI を公開できます(argocd.example.com を自分のドメイン名または一時的なホストに置き換えます):
# argocd-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: argocd.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 443
kubectl apply -f argocd-ingress.yaml
初回ログインには初期管理者パスワードが必要です。以下のコマンドで取得して自分で変更できます:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 --decode
その後、https://argocd.example.com にアクセスしてユーザー名 admin でログインできます。初回ログイン後、パスワードを直ちに変更するか SSO を設定し、Git リポジトリを通じて最初の Application を作成して GitOps ワークフローを開始することをお勧めします。
Cloudflare でドメイン名をホストしている場合は、以下の手順に従ってドメイン名解決を完了できます:
- Ingress 例の
argocd.example.comを Cloudflare でホストしている実際のドメイン名に置き換えます - Cloudflare コンソール → DNS → A レコードを追加します。名前は上記のサブドメイン、値はクラスタ エントリ ポイントのパブリック IP を入力します。Ingress Controller が
hostPortを有効にしている場合、エントリ ポイントは任意のコントロール ノードのパブリック IP です。外部ロード バランサーを使用している場合は、対応するロード バランサー アドレスを入力します。 - 必要に応じて Cloudflare プロキシ(オレンジ色のクラウド)を有効にするかどうかを選択します。有効にした場合は、Argo CD に信頼できる証明書を設定することをお勧めします(例えば cert-manager を使用して Let's Encrypt を申請)。
- 解決完了後、DNS が有効になるまで待ってから、Cloudflare が提供するドメイン名を通じて Argo CD UI にアクセスできます。自動的に解決を管理したい場合は、external-dns + Cloudflare API Token と組み合わせて、完全に自動化された GitOps ドメイン名同期を実現できます。
次のステップ
【チュートリアル】ゼロから始めるエンタープライズ グレード高可用性 PostgreSQL クラスタ構築