Skip to content
Published on

vClusterで実装するKubernetesマルチテナンシー:仮想クラスター格離と運用ガイド

Authors
  • Name
    Twitter
vCluster Multi-Tenancy

はじめに

Kubernetesを運用していると、必ず直面する課題が**マルチテナンシー(Multi-Tenancy)**問題である。単一の物理クラスター上で、複数のチーム、プロジェクト、または顧客のワークロードを安全に格離しながら、インフラコストを最適化する要件は、組織規模が拡大するほど切実になる。従来的には、ネームスペース基盤の格離、RBAC方針、NetworkPolicyなどを組み合わせてマルチテナンシーを実装してきたが、CRD設置権限の衝突、API Server共有による騒々しい隣人問題、テナント別独立クラスターアップグレード不可などの本質的な限界が存在する。

vClusterはLoft Labs(現vCluster Labs)が開発したオープンソースプロジェクトで、ホストクラスターのネームスペース内で仮想Kubernetesクラスターを実行する方式でこの問題を解決する。各仮想クラスターは独立したAPI Server、Controller Manager、データ保存庫(etcdまたはSQLite)を備えており、テナントに完全なクラスター管理権限を付与しながら、実際のコンピューティングリソースはホストクラスターで共有される。CNCFプロジェクトとして、2025年にはKubeConでvNodeを発表し、ノードレベルの仮想化格離層を追加した。同年、Standalone vClusterを通じてホストクラスターなしでも独立した仮想クラスターを運用できる機能も提供開始した。

本記事では、vClusterのアーキテクチャ原理から開始して、インストール、構成、Syncerメカニズム、RBAC方針、リソースクォータ、ネットワーク格離、監視統合、そして実際の本番環境で発生し得る失敗事例と復旧手順まで包括的に取り扱う。

vClusterアーキテクチャ

vClusterの核心的な考えはホストクラスターのネームスペース1つを仮想クラスターの実行環境として活用することである。仮想クラスター内部で生成されたワークロードは、Syncerコンポーネントを通じてホストクラスターの該当ネームスペースに実際のPodとしてスケジュール되다。

構成要素

vClusterは以下のような主要なコンポーネントで構成される。

  1. Control Plane: 仮想クラスター専用のAPI ServerとController Managerを実行する。基本的にk3sを使用するが、k0s、vanilla k8s(EKS distroを含む)も選択できる。
  2. Data Store: etcd、SQLite、または外部データベース(PostgreSQL、MySQL)をデータ保存庫として使用する。k3sベースのデプロイメント時、デフォルト値は組込みSQLiteである。
  3. Syncer: 仮想クラスターとホストクラスター間のリソース同期を担当する核心的なコンポーネントである。
  4. CoreDNS: 仮想クラスター内部のDNS解析を担当する。ホストクラスターのDNSと連動してサービスディスカバリーをサポートする。

格離レベル階層

vClusterは3つの格離レベルを提供する。

  • Shared(共有): デフォルトモード。仮想クラスターがホストクラスターのネームスペース内で実行され、ホストクラスターのノードを共有する。
  • Private(専用): 仮想クラスター専用ノードを割り当ててコンピューティングリソースを物理的に格離する。
  • Standalone(独立): ホストクラスターなしで独立して実行される仮想クラスター。2025年に導入された機能で、完全なクラスター自律性を保証する。

ネームスペース基盤の格離 vs vCluster比較表

既存のネームスペース基盤のマルチテナンシーとvCluster基盤の仮想クラスターアプローチを様々な観点から比較する。

項目ネームスペース基盤の格離vCluster仮想クラスター
API Server共有(すべてのテナント同一API Server)独立(テナント別専用API Server)
CRD設置クラスター全域に影響、衝突リスク仮想クラスター内で独立設置可能
RBAC複雑度テナント増加時に方針が爆発的増加テナント別独立RBAC、管理簡素化
リソース格離ResourceQuota/LimitRangeベースのソフト格離仮想クラスター+ホストクォータの二重格离
ネットワーク格离NetworkPolicy必須構成デフォルトネームスペース格离+NetworkPolicy追加可能
クラスターアップグレード全体クラスター同時影響仮想クラスター別独立アップグレード可能
Admission Webhook全域適用、テナント間影響仮想クラスター別独立構成
費用効率性高い(最小オーバーヘッド)中程度(コントロールプレーンオーバーヘッド存在)
実装難易度低い中程度(Syncer、ネットワーク理解必要)
テナント自律性低い(クラスター管理者依存)高い(仮想cluster-admin可能)

インストールと構成

CLIを利用した迅速なインストール

vCluster CLIを使用すると、最も迅速に仮想クラスターを生成できる。

# vCluster CLIインストール
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/

# 仮想クラスター生成(基本k3sベース)
vcluster create my-vcluster --namespace team-alpha

# 仮想クラスターに接続(kubeconfigオートー構成)
vcluster connect my-vcluster --namespace team-alpha

# 接続確認
kubectl get namespaces
kubectl get nodes

# 仮想クラスター接続解除
vcluster disconnect

# 仮想クラスター削除
vcluster delete my-vcluster --namespace team-alpha

Helmを利用した本番デプロイメント

本番環境ではHelm chartsを使用して細かい設定を適用することが推奨される。以下は本番レベルのvcluster.yaml設定例である。

# vcluster.yaml - 本番設定例
controlPlane:
  # k3sの代わりにk8sを使用(本番推奨)
  distro:
    k8s:
      enabled: true
      apiServer:
        extraArgs:
          - '--audit-log-path=/var/log/kubernetes/audit.log'
          - '--audit-log-maxage=30'
          - '--audit-log-maxbackup=10'
          - '--audit-log-maxsize=100'
      controllerManager:
        extraArgs:
          - '--terminated-pod-gc-threshold=50'

  # コントロールプレーンリソース制限
  statefulSet:
    resources:
      limits:
        cpu: '2'
        memory: 4Gi
      requests:
        cpu: '500m'
        memory: 1Gi
    persistence:
      size: 20Gi

  # 外部etcd使用(高可用性)
  backingStore:
    etcd:
      deploy:
        enabled: true
        statefulSet:
          resources:
            limits:
              cpu: '1'
              memory: 2Gi
            requests:
              cpu: '200m'
              memory: 512Mi

# リソース同期設定
sync:
  toHost:
    pods:
      enabled: true
    services:
      enabled: true
    configmaps:
      enabled: true
    secrets:
      enabled: true
    endpoints:
      enabled: true
    persistentvolumeclaims:
      enabled: true
    ingresses:
      enabled: true
    storageClasses:
      enabled: false
  fromHost:
    nodes:
      enabled: true
      selector:
        labels:
          team: alpha
    storageClasses:
      enabled: true
    ingressClasses:
      enabled: true

# ネットワーキング設定
networking:
  replicateServices:
    fromHost:
      - from: monitoring/prometheus-server
        to: monitoring/prometheus-server
  resolveDNS:
    - hostname: '*.team-alpha.svc.cluster.local'
      target:
        hostNamespace: team-alpha

# セキュリティ方針
policies:
  resourceQuota:
    enabled: true
    quota:
      requests.cpu: '8'
      requests.memory: '16Gi'
      limits.cpu: '16'
      limits.memory: '32Gi'
      pods: '50'
      services: '20'
      persistentvolumeclaims: '10'
  limitRange:
    enabled: true
    default:
      cpu: '500m'
      memory: '512Mi'
    defaultRequest:
      cpu: '100m'
      memory: '128Mi'

Helm chartsを使用してデプロイするコマンドは以下の通りである。

# Helm リポジトリ追加
helm repo add loft https://charts.loft.sh
helm repo update

# ネームスペース生成
kubectl create namespace team-alpha

# vCluster デプロイ
helm upgrade --install my-vcluster loft/vcluster \
  --namespace team-alpha \
  --values vcluster.yaml \
  --version 0.24.1

# デプロイメント状態確認
kubectl get pods -n team-alpha
kubectl get statefulset -n team-alpha

# vCluster接続
vcluster connect my-vcluster -n team-alpha

Syncer動作原理

SyncerはvClusterの核心的なエンジンで、仮想クラスターとホストクラスター間のリソース同期を担当する。このメカニズムを正確に理解することがvCluster運用の核心である。

同期方向とリソース種類

Syncerの同期は2つの方向で進行される。

仮想→ホスト(toHost): 仮想クラスターで生成されたリソースがホストクラスターのネームスペースに実際に生成される。Pod、Service、ConfigMap、Secret、PVCなどがこの方向で同期される。仮想クラスターでDeploymentを生成すると、Deployment自体は仮想クラスターのetcdにのみ存在するが、結果的に生成されるPodはホストクラスターネームスペースに実際のPodとしてスケジュール される。

ホスト→仮想(fromHost): ホストクラスターのリソースを仮想クラスター内部で使用できるように同期する。StorageClass、IngressClass、Node情報、PriorityClassなどがこの方向で同期される。

リソース名変換(Name Rewriting)

Syncerは仮想クラスターのリソースをホストクラスターに同期する際に、名前衝突を防ぐために名前を変換する。基本変換規則は{vcluster-name}-x-{resource-name}-x-{vcluster-namespace}形式である。例えば、仮想クラスターmy-vclusterdefaultネームスペースにnginx Podを生成すると、ホストクラスターではmy-vcluster-x-nginx-x-team-alphaという名前で生成される。

ラベルとアノテーション伝播

Syncerはホストクラスターのリソースに追加ラベルを付加して仮想クラスターとの関連関係を保持する。vcluster.loft.sh/managed-byvcluster.loft.sh/namespaceなどのラベルが自動的に追加され、ガベージコレクションとリソース追跡に活用される。

高度な同期:Generic Sync

基本サポートリソース以外のカスタムリソース(CRD)を同期する必要がある場合、Generic Sync機能を活用できる。これにより、Cert-ManagerのCertificate、IstioのVirtualServiceなどのカスタムリソースも仮想クラスターからホストクラスターに同期できる。

RBACとリソースクォータ設定

仮想クラスターユーザーRBAC

仮想クラスター内部では、テナントにcluster-admin権限を付与できる。これは仮想クラスター範囲内でのみ有効なため、ホストクラスターのセキュリティには影響を与えない。

# vcluster-tenant-rbac.yaml
# 仮想クラスター内部でテナントにadmin権限を付与
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tenant-admin-binding
subjects:
  - kind: Group
    name: team-alpha-developers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
---
# ホストクラスターでvClusterネームスペースアクセス制限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: vcluster-namespace-viewer
  namespace: team-alpha
rules:
  - apiGroups: ['']
    resources: ['pods', 'services', 'configmaps']
    verbs: ['get', 'list', 'watch']
  - apiGroups: ['']
    resources: ['pods/log']
    verbs: ['get']
  - apiGroups: ['apps']
    resources: ['statefulsets']
    verbs: ['get', 'list', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: vcluster-namespace-viewer-binding
  namespace: team-alpha
subjects:
  - kind: Group
    name: team-alpha-developers
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: vcluster-namespace-viewer
  apiGroup: rbac.authorization.k8s.io

ホストクラスターリソースクォータ

仮想クラスターがホストクラスターのリソースを過剰に消費することを防ぐため、仮想クラスターが実行されるネームスペースにResourceQuotaを適用する。

# host-resource-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: vcluster-team-alpha-quota
  namespace: team-alpha
spec:
  hard:
    requests.cpu: '8'
    requests.memory: '16Gi'
    limits.cpu: '16'
    limits.memory: '32Gi'
    pods: '50'
    services: '20'
    services.loadbalancers: '2'
    services.nodeports: '5'
    persistentvolumeclaims: '10'
    requests.storage: '100Gi'
    count/deployments.apps: '30'
    count/statefulsets.apps: '10'
    count/jobs.batch: '20'
    count/cronjobs.batch: '10'
---
apiVersion: v1
kind: LimitRange
metadata:
  name: vcluster-team-alpha-limits
  namespace: team-alpha
spec:
  limits:
    - type: Container
      default:
        cpu: '500m'
        memory: '512Mi'
      defaultRequest:
        cpu: '100m'
        memory: '128Mi'
      max:
        cpu: '4'
        memory: '8Gi'
      min:
        cpu: '50m'
        memory: '64Mi'
    - type: Pod
      max:
        cpu: '8'
        memory: '16Gi'
    - type: PersistentVolumeClaim
      max:
        storage: '50Gi'
      min:
        storage: '1Gi'

ネットワーク格离(NetworkPolicy)

vCluster環境でのネットワーク格离は2つの層で実装される。まずホストクラスターレベルで仮想クラスターネームスペース間の通信を制御し、さらに仮想クラスター内部ではワークロード間の細分化された方針を適用できる。

ホストクラスターレベルNetworkPolicy

# host-networkpolicy.yaml
# 仮想クラスターネームスペース間通信遮断
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: isolate-vcluster-namespace
  namespace: team-alpha
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # 同じネームスペース内の通信許可
    - from:
        - podSelector: {}
    # イングレスコントローラーからのトラフィック許可
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
          podSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx
    # 監視システムのメトリクス収集許可
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
          podSelector:
            matchLabels:
              app: prometheus
  egress:
    # DNS参照許可
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    # 同じネームスペース内の通信許可
    - to:
        - podSelector: {}
    # 外部インターネットアクセス許可(必要時)
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8
              - 172.16.0.0/12
              - 192.168.0.0/16

このNetworkPolicyは仮想クラスターのホストネームスペースを格离して、他のテナントのネームスペースとの直接通信を遮断する。同時にイングレスコントローラー、監視システム、DNSなど必須インフラサービスとの通信は許可する。

仮想クラスター内部NetworkPolicy

仮想クラスター内部でもマイクロサービス間のネットワーク方針を適用できる。仮想クラスター内部で定義されたNetworkPolicyはSyncerを通じてホストクラスターに適切に変換され適用される。

監視統合

Prometheusメトリクス収集

vClusterのコントロールプレーンおよび仮想クラスター内部ワークロードの監視を構成するには、ホストクラスターのPrometheusが仮想クラスターネームスペースのPodをスクレイピングできるように設定する必要がある。

ホストクラスターのPrometheusにServiceMonitorを追加して、仮想クラスターコントロールプレーンのメトリクスを収集できる。vClusterのAPI Serverメトリクス、etcdメトリクス、Syncerの同期状態メトリクスを含めてコントロールプレーンの健全状態を包括的に監視する。特にSyncerの同期遅延時間とエラー率は仮想クラスターの安定性を判断する核心指標である。

仮想クラスター内部ワークロードのメトリクスはホストクラスターのPrometheusが該当ネームスペースのPodを直接スクレイピングするか、仮想クラスター内部に別途のPrometheusをインストールして収集できる。マルチテナンシー環境ではテナント別Grafanaダッシュボードを構成してアクセス制御を適用し、各チームが自身のワークロード状態のみ確認できるようにすることが良い。

アラート設定推奨事項

本番環境では以下の項目に対するアラートを構成することを推奨する。

  • vClusterコントロールプレーンPodの再始動回数が閾値を超える場合
  • Syncerの同期遅延時間が30秒を超える場合
  • 仮想クラスターネームスペースのリソースクォータ使用量が80%を超える場合
  • etcdデータ保存庫のディスク使用量が70%を超える場合
  • 仮想クラスターAPI Serverの応答時間が異常に増加する場合

失敗事例と復旧手順

事例1:Syncer同期失敗によるPodミスマッチ

症状: 仮想クラスターでDeploymentを削除したが、ホストクラスターに孤児(orphan)Podが残っている状況。仮想クラスターのkubectl get podsとホストクラスターの実際のPod一覧が不一致である。

原因: Syncer PodのOOM KillまたはネットワークパーティションによってDelete イベントが伝播されなかった場合に発生する。

復旧手順:

# 1. ホストクラスターで孤児Pod識別
kubectl get pods -n team-alpha -l vcluster.loft.sh/managed-by=my-vcluster \
  --field-selector=status.phase=Running

# 2. 仮想クラスターで該当Podが存在するか確認
vcluster connect my-vcluster -n team-alpha
kubectl get pods --all-namespaces

# 3. 仮想クラスターに存在しない孤児Pod手動削除
vcluster disconnect
kubectl delete pod <orphan-pod-name> -n team-alpha --grace-period=30

# 4. Syncer Pod再起動で同期状態復元
kubectl rollout restart statefulset my-vcluster -n team-alpha

# 5. 同期状態確認(ログ検証)
kubectl logs statefulset/my-vcluster -n team-alpha -c syncer --tail=100

予防措置: Syncerのリソースリクエスト/制限を十分に設定し、定期的に仮想クラスターとホストクラスター間のリソース一致を検査するCronJobを構成する。

事例2:仮想クラスターetcdデータ損傷

症状: 仮想クラスターAPI Serverが起動しない、etcdコンテナログにpanic: freepages: failed to get all reachable pagesまたはdatabase file is not validエラーが表示される。

原因: PersistentVolumeのディスクI/Oエラー、異常なPod終了、またはストレージバックエンド障害によってetcdデータファイルが損傷した場合である。

復旧手順:

  1. 仮想クラスターのStatefulSetをreplicas: 0にスケールダウンする。
  2. バックアップがある場合、PVCのデータをバックアップデータで置き換える。
  3. バックアップがない場合、PVCを削除して新しいPVCで置き換えた後、仮想クラスターを再生成する。この場合、仮想クラスター内部のすべてのメタデータが失われるが、実際のワークロードPodはホストクラスターで実行が継続される。
  4. StatefulSetを再びreplicas: 1にスケールアップし、ホストクラスターの既存リソースと再同期されるかを確認する。

予防措置: etcdデータの定期的なスナップショットバックアップを構成し、本番環境では高可用性etcdクラスター構成を使用する。

事例3:リソースクォータ超過によるワークロードデプロイメント失敗

症状: 仮想クラスター内部でPod生成が失敗し、forbidden: exceeded quotaエラーが発生せず代わりにPodがPending状態で停止する現象。

原因: ホストクラスターのResourceQuotaが超過されたが、エラーメッセージが仮想クラスターのイベントに適切に伝播されず、テナントが原因を把握しにくい場合である。

復旧手順:

  1. ホストクラスターで該当ネームスペースのResourceQuota使用量を確認する:kubectl describe resourcequota -n team-alpha
  2. 不要なリソースをクリーンアップするかクォータを増加させる。
  3. vCluster設定でイベント同期を有効にしてテナントがホストクラスターのイベントも確認できるようにする。

予防措置: クォータ使用量が80%を超えたらアラートを発生させ、仮想クラスター内部でも別途のResourceQuotaを構成して事前に制限する。

事例4:仮想クラスター間ネットワーク格离失敗

症状: 異なる仮想クラスターのワークロードがホストクラスターで同じネットワークを共有して直接通信が可能な状態。

原因: ホストクラスターにNetworkPolicyが構成されていないか、CNIプラグインがNetworkPolicyをサポートしていない場合(例:Flannelデフォルト設定)。

復旧手順:

  1. CNIプラグインがNetworkPolicyをサポートするか確認する。Calico、Cilium、WeaveNetなどはNetworkPolicyをサポートし、Flannelデフォルト設定はサポートしない。
  2. 上記ネットワーク格离セクションで示されたNetworkPolicyを各仮想クラスターのホストネームスペースに適用する。
  3. 格离が正常に動作するかテストPodを生成して他のネームスペースへの通信が遮断されるか確認する。

事例5:仮想クラスターアップグレード失敗

症状: Helmを通じたvClusterバージョンアップグレード後、仮想クラスターのAPI Serverが起動しないか既存ワークロードとの互換性問題が発生する。

原因: メジャーバージョン間のAPI変更またはSyncerプロトコル変更による非互換性。

復旧手順:

  1. アップグレード前に必ずetcdスナップショットバックアップを実行する。
  2. 失敗時helm rollback my-vcluster <前-リビジョン> -n team-alphaコマンドで前バージョンにロールバックする。
  3. ロールバック後も問題が継続したらバックアップからetcdデータを復元する。

予防措置: ステージング環境で先にアップグレードをテストし、公式アップグレードガイドのBreaking Changesを必ず確認する。本番環境では1段階ずつ段階的にアップグレードする。

運用時注意事項チェックリスト

本番環境でvClusterを運用する際に必ず確認すべき項目を整理する。

デプロイメント前チェック項目:

  • CNIプラグインがNetworkPolicyをサポートするか確認(Calico、Cilium推奨)
  • ホストクラスターのResourceQuotaが仮想クラスターネームスペースに適用されたか確認
  • vClusterコントロールプレーンのリソースリクエスト/制限が適切に設定されたか確認
  • PersistentVolumeのストレージクラスとリクレーム方針が正確であるか確認
  • 仮想クラスターのkubeconfig配布プロセスが安全であるか確認(OIDC連携推奨)

運用中の監視項目:

  • Syncerの同期遅延時間とエラー率の監視
  • 仮想クラスターコントロールプレーンのCPU/メモリ使用量監視
  • etcdデータ保存庫のディスク使用量とコンパクション状態監視
  • 仮想クラスターAPI Serverのリクエスト遅延時間とエラー率監視
  • ホストクラスターネームスペースのリソースクォータ使用量監視

バックアップと災害復旧:

  • etcdスナップショットの定期バックアップスケジュールが構成されたか確認(最小日1回)
  • バックアップ復元手順を四半期ごとにテストするか確認
  • 仮想クラスターの再生成手順が自動化(IaC)されたか確認
  • ホストクラスター障害時の仮想クラスター復旧手順が文書化されたか確認

セキュリティチェック項目:

  • 仮想クラスターユーザーの認証/認可が中央IDp(OIDC、LDAP)と統合されたか確認
  • ホストクラスターのNode アクセス権限が仮想クラスターテナントに露出されていないか確認
  • Pod Security Standards(PSS)が適用されてprivilegedコンテナ実行を防止するか確認
  • 仮想クラスター間ネットワーク格离が正常に動作するか定期的にテストするか確認

アップグレード手順:

  • vClusterバージョンアップグレード前にetcdスナップショットバックアップを実行
  • ステージング環境でアップグレードテスト後に本番適用
  • アップグレード後にSyncer同期状態とワークロード正常動作を確認
  • ロールバック手順とコマンドを事前に準備

まとめ

vClusterはKubernetesマルチテナンシーの根本的な限界を仮想クラスターという抽象化層で解決する実用的なツールである。ネームスペース基盤の格离のみでは満たしにくいCRD独立性、API Server格离、テナント別クラスター管理自律性などの要件を物理クラスター追加なしでも達成できる。

ただしSyncerの動作原理とリソース名変換メカニズムを十分に理解しないと運用時に予想外の問題に直面する可能性があるため、ステージング環境で十分な検証を経た後に本番導入することを推奨する。特にネットワーク格离とリソースクォータ設定は初期構成段階で必ず一緒に適用する必要がある。

参考資料