CIU Blog 記事 KaaS 編

はじめに

はじめまして、CyberAgent group Infrastructure Unit(CIU)に所属している長井佑太です。普段は、Cycloud の KaaS 基盤である AKE(Astro Container Engine)の開発運用を担当しています。

今回は、Cycloud の新基盤の全貌に迫る連載シリーズの第 7 回として、AKE の新リージョン対応についてお届けします。

本稿では、Cycloud 新基盤における最大の技術的挑戦の一つである「VPC 隔離環境下でのクラスタ構築」に焦点を当てます。AKE が Cluster API(CAPI) を活用してどのようにクラスタを動的にプロビジョニングしているのか、そして管理系統とユーザーワークロードの分離という制約下で、セキュリティと運用性をいかに両立させたのか、その設計の裏側を深掘りします。

AKE(Astro Container Engine)の概要

AKE は、CyberAgent の開発基盤である Cycloud において、Kubernetes クラスタを動的に提供する KaaS(Kubernetes as a Service)基盤です。開発者の体験(Developer Experience)を向上させるため、現在では国内 2 拠点に展開しており、多様なインフラ環境を抽象化して提供しています。

このマルチクラウド・マルチリージョン化を支える重要な基盤技術として、CAPI を活用しています。CAPI は、Kubernetes クラスタの構成をリソースとして定義することで、その状態に合わせてシステムが自動的にクラスタを構築・維持してくれる仕組みです。

Cycloud のリージョンでは、拠点ごとに LoadBalancer の筐体仕様や Cloud Provider の API が異なるため、CAPI の「Infrastructure Provider」を拠点毎に独自実装し、それぞれのインフラ差異を抽象化しています。この宣言的な管理手法により、AKE は多様なインフラ環境を抽象化し、単一のコントロールプレーンからシームレスにクラスタを管理することを可能にしています。

AKE 全体像

今回解説する新リージョンは、既存構成を踏襲した単なる拡張ではありません。昨今のより高度なセキュリティ要件に応えるため、従来の枠組みを超えたネットワークアーキテクチャの刷新を断行した、AKE の「次世代環境」です。本稿では、その設計思想を具現化するプロセスで直面した課題と、それをいかに克服したのかを詳述します。

課題:VPC 隔離環境における疎通のジレンマ

以前の Cycloud 基盤では、Bootstrap Cluster と Workload Cluster はルータを介して接続されたフラットなネットワーク上に配置されており、相互の通信は透過的でした。しかし新基盤では、セキュリティ要件に基づき VPC によるネットワーク分離を導入したため、管理系統(mgmt-vpc)からユーザーワークロード(user-vpc)への到達性が遮断され、対比図が示す通り、従来のデプロイフローでは構築が完遂できなくなりました。

Network 比較図

この制約を突破し、セキュリティと運用効率を両立させるアーキテクチャへの刷新が急務となりました。以下では、本課題を解決するために検討した3つのアプローチとその評価を解説します。

VPC隔離環境下における接続方式の比較検討

VPC隔離環境下において、管理基盤(mgmt-vpc)とワークロード(user-vpc)をいかに安全かつ効率的に接続するか。私たちは以下の3つのアプローチを比較・検討しました。

Kubernetes API のインターネット公開 (Global Endpoint)

Kubernetes API のエンドポイントをそのままインターネットへ公開する構成です。経路は次のとおり最も単純です:

Kubernetes API のインターネット公開

構築の簡便さでは群を抜きますが、インターネット経由でのアクセスは攻撃対象領域(Attack Surface)の拡大に直結します。セキュリティ要件を満たすことが困難であるため、採用を見送りました。

VPC Peering による相互接続

mgmt-vpc と user-vpc を直接ルーティングで繋ぐ手法です。到達性は確実に担保できる一方で、図中の双方向矢印が示す通り、VPC Peering は原則として双方向の通信を許可する仕様です。運用面では、ユーザーワークロードから管理系統への意図しない通信を厳格に制限するために、全ノードに対して複雑かつ膨大なセキュリティグループ(SG)設定を維持し続ける必要があり、いわゆる「SG地獄」による運用負荷の増大が懸念されました。また、論理的な接続が全域に及ぶため、万が一のインシデントにおける Blast Radius(被害範囲)が管理基盤全体に波及するリスクを排除できず、十分なセキュリティ強度を担保できないと判断し、見送りました。

VPC Peering

Multi-NIC 構成による物理分離

Workload Cluster の各ノードに対して、user-vpc と mgmt-vpc の計2つのネットワークインタフェースをアタッチする手法です。図のように管理通信の経路(緑)とユーザートラフィックの経路(青)がノード内で完全に分かれており、『管理通信は mgmt-vpc 側 NIC に限定する』『ユーザー網からの管理系アクセスは遮断する』という設計をルーティングテーブルで実現しました。インフラ構成およびルーティング制御の複雑化という代償は伴いますが、「セキュリティ強度の担保」と「管理系統の到達性確保」を両立させるアプローチとして、最も合理的であると考え、このアプローチを採用しました。

Multi-NIC 構成

アーキテクチャ詳細:Cluster API と Custom Controller による動的ルーティング

今回の Multi-NIC 構成を実現するにあたり、私たちは「CAPI によるインフラプロビジョニング」と「Custom Controller による動的なルーティング制御」という二層構造を採用しました。

1. Cluster API による基盤構築

CAPI のリソース構造とプロバイダの役割

AKE は、Kubernetes クラスタのライフサイクルを Kubernetes API を通じて宣言的に管理するフレームワーク CAPI を活用しています。CAPIでは図中黄色で示した、クラスタを示す Cluster リソース、NodePool を表現する MachineDeployment、Node を表現する Machine などのリソースで表現しています。また、CAPIを各種クラウドプロバイダーに対応する「Provider」の仕組みを利用し、Cycloud 特有のインフラ要件を抽象化しています。

AKE では、以下の三種類のプロバイダを組み合わせて運用しています。

  • Control Plane Provider(図中赤): Control Plane の構築・設定を担当します。AKE では Kubeadm を使用しており、リソースとして KubeadmControlPlane を使用します。
  • Bootstrap Provider (図中青): 仮想マシンの初期化処理(cloud-init スクリプトの生成)を担当します。AKE では Kubeadm を使用しており、設定テンプレートとして KubeadmConfigTemplate を使用します。
  • Infrastructure Provider (図中緑): 仮想マシンやネットワーク等の IaaS リソースのプロビジョニングを担当します。AKE では独自実装を使用しており、インフラ全体の定義として InfraCluster、マシン設定のテンプレートとして InfraMachineTemplate、仮想マシンの実体として InfraMachine を実装しています。

Cluster API コンポーネントの構成図

インフラ階層と FailureDomain のマッピング

CAPI には、ControlPlane の Node を分散配置する仕組みがあり、この場所の単位を FailureDomain と呼びます。1つの場所で障害が起きても他の場所には影響しないよう、ノードを FailureDomain ごとに分散させる、という考え方です。AKE ではこの FailureDomain を、Cycloud の AvailabilityZone(AZ)として扱っています。AZ 配下には Subnet が存在するため、Subnet を AZ に正しく紐付けることで、FailureDomain 単位での冗長配置が正しく機能します。 

NodePool(MachineDeployment)における静的マッピング

Kubernetes の NodePool を追加するには、MachineDeployment という CAPI の抽象化リソースを定義することで達成します。

MachineDeployment の作成時には、InfraMachineTemplate という Infrastructure Provider が読み取る独自の設定を渡すリソースを参照させるのと、FailureDomain を設定してどの AZ に設定するのか決定します。

apiVersion: cluster.x-k8s.io/v1beta2
kind: MachineDeployment
metadata:
  name: sample-cluster
spec:
  ...
  template:
    spec:
      ...
      failureDomain: az-a
      infrastructureRef:
        apiGroup: infrastructure.cluster.x-k8s.io
        kind: InfraMachineTemplate
        name: sample-infra-machine-a
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: ComputeMachineTemplate
metadata:
  name: sample-infra-machine-a
spec:
  template:
    spec:
      ...
      subnet:
        id: subnet-a1

このマニフェストを適用すると、MachineDeployment が基点となって Machine リソースが作成されます。これに追従して、InfraMachineTemplate の定義に基づき InfraMachine リソースが作成・紐付けられることで、最終的に所望の NodePool(Worker Node 群)が構成される仕組みです。

NodePool 構成

ControlPlane (KubeadmControlPlane) における動的マッピング

ControlPlane はクラスタの基幹であるため、冗長性の観点から複数の AZ(FailureDomain)に展開する設計が必須となります。ここで課題となるのが、単一の FailureDomain を静的に指定できる Node Pool(MachineDeployment)とは異なり、ControlPlane では「複数の AZ に対して、適切なサブネットを動的に割り当てる必要がある」という点です。

これを解決するため、私たちは Infrastructure Provider 側で FailureDomain を受け取り、動的に Subnet を決定するロジックを実装しました。この動的配置は、以下のリソース連携によって実現しています。

  1. Subnet 定義の集約 (InfraCluster)

まず、基盤の定義元である InfraCluster リソースにて、クラスタが利用可能な Subnet のリストを定義します。Infrastructure Provider はこの定義から AZ ごとの Subnet を解決し、Status に反映させます。

apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: InfraCluster
metadata:
  name: sample-cluster-a
spec:
  network:
    subnets:
    - id: subnet-a1
    - id: subnet-b1
status:
  # subnet から対応する FailureDomains を解決
  failureDomains:
  - controlPlane: true
    name: az-a
  - controlPlane: true
    name: az-b
  1. 定義の伝搬 (Cluster)

次に、InfraCluster が算出した failureDomains が Cluster リソースの status に伝搬されます。この Cluster リソースがクラスタの全体像を定義する上位概念となります。

apiVersion: cluster.x-k8s.io/v1beta2
kind: Cluster
metadata:
  name: sample-cluster-a
spec:
  controlPlaneRef:
    apiGroup: controlplane.cluster.x-k8s.io
    kind: KubeadmControlPlane
    name: sample-cluster-a
  infrastructureRef:
    apiGroup: infrastructure.cluster.x-k8s.io
    kind: InfraCluster
    name: sample-cluster-a
status:
  failureDomains:
  - controlPlane: true
    name: az-a
  - controlPlane: true
    name: az-b
  1. 動的配置の実行 (KubeadmControlPlane)

最後に、KubeadmControlPlane が Machine を作成する際、この Cluster リソースの failureDomains を参照します。これにより、InfraMachineTemplate に直接 Subnet を記述せずとも、Infrastructure Provider が実行時に動的な AZ/Subnet 決定を行えるようになります。

apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: KubeadmControlPlane
metadata:
  name: sample-cluster-a
spec:
  machineTemplate:
    spec:
      infrastructureRef:
        apiGroup: infrastructure.cluster.x-k8s.io
        kind: InfraMachineTemplate
        name: sample-controlplane-infra-machine
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: ComputeMachineTemplate
metadata:
  name: sample-controlplane-infra-machine
spec:
  template:
    spec:
      # ここでは subnet を指定せず、Infrastructure Provider 側で動的決定する

ControlPlane 構成

ネットワーク構成における「非対称性」の設計

Multi-NIC 構成においては、セキュリティ要件とクラスタとしての疎通性を両立させるため、CAPI 上の論理定義と Cycloud の物理インフラとの間で、意図的に「非対称なマッピング」をとる設計を採用しました。

Multi NIC 構成(FQDN)

図にある通り、user-vpc 側には Primary NIC と Secondary 用の LB を mgmt-vpc 側には Secondary NIC と管理通信用(Primary)の LB を配置しています。これは、管理系統(mgmt-vpc)とユーザーワークロード(user-vpc)それぞれのネットワークから、適切に API Server へのアクセス経路を確保する必要があるためです。具体的には、リソースを以下のように配置しています。

  • ControlPlaneEndpoint: CAPI の管理系統(mgmt-vpc)からの疎通性を確保するため、ControlPlaneEndpoint には mgmt-vpc 側の LB を指定しました。一方で、user-vpc 側からの Kube API Server へのアクセスも許容する必要があるため、user-vpc 側の LB も利用します。この際、Kube API Server が有効な接続先として応答できるように、TLS 証明書に両エンドポイントを含める必要がありました。そのため、CertSANs に user-vpc 側のエンドポイントを明示的に追加しています。
apiVersion: cluster.x-k8s.io/v1beta2
kind: Cluster
metadata:
  name: sample-cluster
spec:
  controlPlaneEndpoint:
    host: sample-cluster-mgmt.network.loadbalancing.cycloud.jp
    port: 6443
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: KubeadmControlPlane
metadata:
  name: sample-cluster
spec:
  kubeadmConfigSpec:
    clusterConfiguration:
      apiServer:
        certSANs:
        # user-vpc 側のエンドポイントを CertSANs に追加。
        # mgmt-vpc 側の ControlPlaneEndpoint はデフォルトで自動追加されるため、
        # ここでは user-vpc 側のみを明示的に指定している。
        - sample-cluster-user.network.loadbalancing.cycloud.jp
  • NIC の優先順位: ワークロードの通信を user-vpc へ集約させるため、Default Route を担う Primary NIC は user-vpc 側に接続し、管理通信用の mgmt-vpc 用 NIC は Secondary として扱う構成としました。

このように、Infrastructure Provider 側で「CAPI 上の論理定義」と「Cycloud 側の物理構成(VPC や NIC)」との関係を意図的に非対称にマッピングすることで、管理系統とワークロード網の物理分離と、Kubernetes クラスタとしての疎通性を両立させています。

2. Custom Controller による動的なルーティング制御

この構成では、Secondary NIC が接続される mgmt-vpc 側に管理クラスタが配置されています。Next hop の先に存在する管理クラスタへ通信を届けるためには、Primary NIC のデフォルトルートとは別に、Secondary NIC を経由させるためのルーティング制御が不可欠です。これを実現するため、私たちは起動時の初期ルーティングを cloud-init で設定し、以降の動的な制御を自作の Custom Controller で管理する二段構えのアプローチをとりました。

この動的制御を実現するために、以下の独自リソースを定義しています。

  • RouteEntry: ノードに対してどのネットワークへ、どのインタフェース(NIC)を使用して通信すべきかを宣言的に定義するカスタムリソース。
  • NodeRouteConfig: RouteEntry に基づき、実際に各ノードのルーティングテーブルを書き換えるための制御リソース。

RouteEntry の適用例

図中の pool-1-route-a と pool-1-route-b という名前の RouteEntry リソースは互いに pool-1 という Node に対してのルートを定義しています。そのため pool-1 に配置している NodeRouteConfig では二つの RouteEntry の内容(図中の赤字と緑字)を結合したリソースとなっています。この NodeRouteConfig を介して Controller はルーティングテーブルをオンメモリで動的に書き換えます。この仕組みにより、プロビジョニング完了後も、インフラ構成の変更を安全かつ自動的に反映することが可能となりました。

Multi-NIC Cluster における技術的挑戦

Multi-NIC 構成を採用したことで、「管理通信を維持しつつ、ユーザーのネットワーク分離を担保する」という目的は達成できました。しかし、実装フェーズにおいて、ネットワークルーティングとライフサイクル管理に関わる二つの大きな課題に直面しました。

1. LB ヘルスチェックのルーティング問題 (Source Routing)

Workload Cluster は mgmt-vpc と user-vpc の双方に LB を配置します。この時、LB のヘルスチェックパケットが同一のネットワークレンジから到達するため、Node は「どの Network Interface に対して応答を返すか」を正しく判別できず、非対称ルーティングによるパケットドロップが発生するという課題がありました。

これを解決するため、Source Routing(送信元 IP に応じた経路制御)を採用しました。パケットの流入元 Network Interface を識別し、それに対応する Interface から応答を返すことで、経路の整合性を維持しました。

パケット判定フロー

Primary NIC のトラフィック(緑)は標準の default ルーティングテーブルを使用し、Secondary NIC 経由のトラフィック(青)は別途定義したルーティングテーブル(例: table 100)を参照する構成としました。これにより、パケットの送信元 NIC に応じて適切なルーティングテーブルが適用されるため、非対称ルーティングによる応答パケットの消失を防止しています。

2. 構築時における「鶏卵問題」の解消

上記のルーティング設定を動的に制御するために、我々は Kubernetes 上で動作する `Custom Controller` を開発しました。しかし、Custom Controller をデプロイして動的に Routing を設定するには、その Controller をデプロイするための Kubernetes API 通信が必要です。

しかし、Kube API Server の起動時に Secondary Network Interface 側の  Routing が設定されている必要があるため、Custom Controller とは別の設定を入れる必要があるという問題が存在しました。

CAPI の Kubeadm Provider には、kubeadm の処理を流す前の preKubeadmCommands を使用することで cloud-init に特定の処理を入れることが出来ます。

apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: KubeadmControlPlane
metadata:
  name: sample-cluster-a
spec:
  kubeadmConfigSpec:
    # ここに流し込んだコマンドがそのまま cloud-init に渡される
    preKubeadmCommands:
  • 初期化の流れ
    • cloud-init により、Secondary Network Interface の Routing 設定
    • kubeadm を実行し、管理 LB と Kube API Server のデプロイ
    • Kube API Server の疎通が完了したので RouteEntry、NodeRouteConfig を適用
    • Controller 経由で経路を注入

まとめ

本稿では、新基盤への移行に際し、AKE がいかにして VPC 隔離とクラスタ構築の自動化を両立させたか、その設計思想と技術的アプローチを詳述しました。

Multi-NIC 構成と Custom Controller によるルーティング制御は、単なるネットワークの技術的工夫にとどまりません。将来的なインフラ要件の変化や構成変更に対し、後からルーティング設定を柔軟に動的更新可能にすることで、ユーザーのワークロードの独立性を保ちながら、システム基盤としての長期的な管理性を高めるための重要な技術選択となりました。

次回予告

次回は最後の記事となる FrontEnd 編の紹介をします。

連載記事一覧

本シリーズでは、各チームが新基盤構築における技術的挑戦を公開しています。他のレイヤーの知見については、ぜひ連載の他の記事も併せてご覧ください。

Cycloud 新基盤の全貌 第一回 新リージョン基盤の全体像
Cycloud 新基盤の全貌 第二回 HW 編
Cycloud 新基盤の全貌 第三回 NW 編
Cycloud 新基盤の全貌 第四回 IaaS 編
Cycloud 新基盤の全貌 第五回 ComputeController 編
Cycloud 新基盤の全貌 第六回 LB 編
Cycloud 新基盤の全貌 第七回 KaaS 編
Cycloud 新基盤の全貌 第八回 FrontEnd 編

 

各レイヤの Cycloud の新基盤に関して、より詳細な仕様や設計思想を知りたい方は、上記各チームの Developers Blog 記事をぜひご一読ください。