vclusterで仮想Kubernetesクラスターを構築する:リソース節約と開発環境の分離

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

単一Kubernetesクラスターを共有する際の問題

開発チーム全員が同じKubernetesクラスターを共有したことがある方なら、その苦労はよくわかるはずです。誰かが誤って他人のネームスペースを削除してしまう。リソースクォータの設定ミスで別の開発者のデプロイが壊れる。新人がClusterRoleを誤って適用して、チームの半数がデプロイできなくなる——そんなことが起きます。

よくある解決策は「クラスターを分ける」か「ネームスペースで分離する」かの二択です。どちらも実際のコストが伴い、すべてのチームにとって最適とは言えません。

3つのアプローチを比較する

1. 完全に分離したクラスター(開発者ごとに1クラスター)

各開発者が専用クラスターを持つ方法です。ローカル環境(kind、minikube)またはクラウドでプロビジョニングします。

  • 完全な分離:他の人が干渉できない
  • 自由度が高い:任意のCRDをインストール、クラスターレベルの設定を変更できる
  • コストが高い:クラウドクラスターは費用がかかり、ローカルでも4〜8GBのRAMを消費する
  • 起動が遅い:実際のクラスターのプロビジョニングには秒単位ではなく分単位の時間がかかる

2. ネームスペースによる分離(全員が1つのクラスターを使用)

各開発者(またはチーム・環境)が専用ネームスペースを持ち、RBACルールで操作範囲を制限します。

  • リソース効率が良い:コントロールプレーンを全員で共有できる
  • 高速:ネームスペースは数秒で作成できる
  • 分離が不完全:クラスターレベルのリソース(CRD、ClusterRole、ノード)は共有されたまま
  • RBACの複雑さ:過剰な権限付与なしに適切なパーミッションを設定するのは難しい
  • 開発者にcluster-adminを与えられない:CRDやClusterRoleが必要なHelmチャートをインストールできない

3. vclusterによる仮想クラスター

vclusterは、ホストクラスターのネームスペースで完全に機能するKubernetes APIサーバーを動かします。開発者からは完全なadminアクセス権を持つ本物のクラスターに見え、ホストクラスター側からはネームスペース内のいくつかのPodとして見えます。

  • 軽量:各vclusterは実際のクラスターより大幅に少ないリソースで動作する
  • 高速:新しい仮想クラスターは60秒以内に起動できる
  • 完全な分離:各vclusterが独自のAPIサーバー、etcd(またはSQLite)、コントロールプレーンを持つ
  • cluster-adminアクセス:開発者はCRDのインストール、ClusterRoleの作成など何でもできる——ホストには影響しない
  • ホストレベルのワークロード:実際のPodはホストクラスターのノード上で動作する(vclusterが同期する)

vclusterのメリットとデメリット

vclusterが得意なこと

  • 開発用サンドボックス:数秒で分離された環境を立ち上げ、作業が終わったら削除できる。プラットフォームチームがクリーンアップする手間がない。
  • CI/CDパイプライン:パイプライン実行ごとに専用クラスターで結合テストを実行できる。実行間で状態が漏れない。
  • マルチテナントSaaS:実際のクラスターをプロビジョニングするコストなしに、顧客ごとに仮想クラスターを提供できる。
  • トレーニング環境:受講者がインフラを危険にさらすことなく、クラスターレベルの設定を自由に試せる。
  • OperatorやCRDのテスト:CRDを自由にインストール・アンインストールできる——ホストには一切影響しない。

vclusterの制限事項

  • ノードレベルのアクセス:すべての実ノードで動作するDaemonSetが必要なアプリの場合、vclusterの同期処理が複雑になることがある。
  • ホストネットワーキング:LoadBalancerサービスやNodePortは、vclusterから外部へトラフィックを公開するために追加設定が必要になる。
  • ストレージ:PersistentVolumeはホストに同期されるため通常は問題ないが、ホストのStorageClassサポートに依存する。
  • リソースオーバーヘッド:各vclusterはコントロールプレーンに少量のオーバーヘッドを追加する(約100〜200MB RAM)。10クラスター程度なら問題ないが、100以上になるとキャパシティ計画が必要になる。

始める前に推奨される準備

ホストとなるKubernetesクラスターが必要です。ローカル開発ならkindk3dが最適です。本番グレードのvclusterホスティングには、任意のマネージドクラスター(EKS、GKE、AKS)が使えます。

また以下も必要です:

  • ホストクラスターに接続するよう設定されたkubectl
  • helm v3以上(vclusterは内部でHelmを使用)
  • vcluster CLI(以下でインストール方法を説明)

特に推奨したい点として、仮想コントロールプレーンにk3sを使用する(軽量セットアップ向けのvclusterデフォルト)と、etcdの代わりにSQLiteを使う組み合わせがあります。開発用サンドボックスのユースケースでは十分すぎるほどで、フルのetcdを使った場合と比べてメモリ使用量を約40%削減できます。

実装ガイド

ステップ1:vcluster CLIをインストールする

# Linux / macOS
curl -L -o vcluster "https://github.com/loft-sh/vcluster/releases/latest/download/vcluster-linux-amd64"
chmod +x vcluster
sudo mv vcluster /usr/local/bin/

# インストールの確認
vcluster version

ステップ2:最初の仮想クラスターを作成する

# 「vcluster-dev」ネームスペースに「dev-sandbox」という名前のvclusterを作成する
vcluster create dev-sandbox --namespace vcluster-dev

# このコマンドの動作:
# 1. ネームスペースが存在しない場合は作成する
# 2. vclusterコントロールプレーン(APIサーバー+シンカー)をデプロイする
# 3. vclusterが準備完了になるまで待機する
# 4. kubectlコンテキストを新しいvclusterに自動的に切り替える

コマンドが完了すると、すでに仮想クラスターに接続された状態になっています。kubectl get nodesを実行するとノードが表示されます——vclusterが生成した仮想ノードの表現です。

kubectl get nodes
# NAME                 STATUS   ROLES    AGE   VERSION
# vcluster-dev-sandbox Ready    <none>   30s   v1.28.0

ステップ3:vcluster内にアプリをデプロイする

# 仮想クラスター内にnginxをデプロイする
kubectl create deployment nginx --image=nginx:alpine
kubectl expose deployment nginx --port=80 --type=ClusterIP

# 動作確認
kubectl get pods
kubectl get svc

次に、ホストクラスターに切り替えて、vclusterが実際に作成したものを確認してみましょう:

# ホストクラスターのコンテキストに戻る
vcluster disconnect

# ホスト上のvclusterネームスペースを確認する
kubectl get pods -n vcluster-dev
# 表示されるもの:vclusterコントロールプレーンPod+nginxのPod(vclusterから同期済み)

vcluster内で宣言されたワークロードは、実際のPodとしてホストに同期されます。vcluster APIサーバーは開発者にはネイティブリソースとして見せますが、実際にはホストのノード上でスケジューリングされています。これがvclusterの仕組みの核心です。

ステップ4:開発者にvclusterへのアクセスを付与する

# vclusterのkubeconfigをエクスポートする
vcluster connect dev-sandbox --namespace vcluster-dev --print > dev-sandbox-kubeconfig.yaml

# このファイルを開発者に共有する
# 他のkubeconfigと同様に使用できる:
export KUBECONFIG=./dev-sandbox-kubeconfig.yaml
kubectl get pods

ステップ5:環境ごとに複数のvClusterを作成する

# 開発者ごとに1つ作成する
vcluster create dev-alice --namespace vcluster-alice
vcluster create dev-bob --namespace vcluster-bob

# または環境ステージごとに作成する
vcluster create staging --namespace vcluster-staging
vcluster create qa --namespace vcluster-qa

# 実行中のvclusterを一覧表示する
vcluster list
# NAME         NAMESPACE          STATUS    CONNECTED
# dev-alice    vcluster-alice     Running
# dev-bob      vcluster-bob       Running
# staging      vcluster-staging   Running

ステップ6:values.yamlでvclusterをカスタマイズする

デフォルト設定を超えるカスタマイズには、Helm valuesファイルを使用します:

# vcluster-values.yaml
controlPlane:
  distro:
    k3s:
      enabled: true
  statefulSet:
    resources:
      requests:
        memory: 128Mi
        cpu: 50m
      limits:
        memory: 512Mi
        cpu: 500m

syncer:
  extraArgs:
    - "--sync-all-nodes"   # ノードのラベルとテイントをvclusterに同期する
# 作成時にカスタム値を適用する
vcluster create dev-sandbox \
  --namespace vcluster-dev \
  --values vcluster-values.yaml

ステップ7:作業完了後にvclusterを削除する

# vclusterを削除してネームスペースをクリーンアップする
vcluster delete dev-sandbox --namespace vcluster-dev

すべてが完全に削除されます——ホスト上に残留するCRDも、古くなったClusterRoleも一切ありません。ネームスペース分離と比べてみてください。あちらのクリーンアップでは、ネームスペース境界の外で作成されたクラスタースコープのリソースを探し回ることになりがちです。

実際の運用結果

本番環境では、CI/CDの結合テスト環境として具体的にこのパターンを活用してきました。各パイプラインジョブがvclusterを作成し、フルのテストスイート(CRDをインストールするHelmチャートを含む)を実行し、完了後にクラスターを削除します。数ヶ月間、数千回のパイプライン実行を経ても、ホスト上にテスト関連のリソース漏れは一切発生していません。

k3s+SQLiteで8つのvclusterを同時実行したところ、全コントロールプレーン合計で約1.2GBのRAMを消費しました。ほとんどのマネージドプロバイダーにおける単一の実クラスターのコントロールプレーンは、それ以上のメモリを使用します。

クイックリファレンス

# よく使うコマンド
vcluster create <name> --namespace <ns>    # 作成して接続する
vcluster list                                # vclusterの一覧を表示する
vcluster connect <name> --namespace <ns>   # 既存のvclusterに再接続する
vcluster disconnect                          # ホストクラスターに切り替える
vcluster delete <name> --namespace <ns>   # 完全に削除する

共有クラスターの混乱に悩んでいるチームには、vclusterを試してみる価値があります。実際のクラスターのコストなしに本物の分離が得られ、そのトレードオフは開発サンドボックス、テストパイプライン、マルチテナント構成のいずれにおいても十分に成立します。

Share: