👩‍💻

【mNi Cloud】11月~2月の開発状況【国産クラウド】

に公開

はじめに

こんにちは。mNi Cloud開発チームです。
2024年7月に開発を開始した国産クラウドプラットフォーム「mNi Cloud」は、引き続き開発を進めています。
前回の記事では、5月〜10月にかけてのバックエンドアーキテクチャのリファクタリングについてお伝えしました。
11月〜2月にかけては、リファクタリング後のアーキテクチャの上に実運用に必要な機能を載せるフェーズに入りました。
本記事では、その中でも特に設計上の判断が大きかったポイントを中心にご報告します。

mNi Cloudとは

mNi Cloudの概要については、以下の記事に詳しくまとめています。
【mNi Cloud】国産クラウドの開発、始めました

5月〜10月の開発状況

以前の開発経過は以下の記事をご参照ください。

【mNi Cloud】5月~10月の開発状況【国産クラウド】

Operator(バックエンド)の開発状況

概要図

以下は、現在開発中のmNi Cloudバックエンドアーキテクチャ図です。

Auth Controller(新規作成)

概要

これまでのmNi Cloudには、ユーザを識別する仕組みがありませんでした。
すべてのリソースが単一のNamespaceに作成されるため、テナント分離ができず、誰がどのリソースを所有しているかも管理できない状態でした。

認証基盤の設計にあたり、以前はKeycloakを利用していましたが、mNi Cloudでは認証情報自体をKubernetes CRDとして管理する方針を採りました。
UserTenantという2つのクラスタスコープCRDを導入し、Auth Controllerがこれらを監視します。

この設計の利点は、認証基盤が他のOperatorと同じKubernetes APIの上で動作する点にあります。
テナント作成時にNamespaceを自動プロビジョニングし、以降そのテナントのリソースはすべてこのNamespace内で管理されます。
外部サービスへの依存が不要で、既存のOperatorアーキテクチャにそのまま組み込むことができました。

機能詳細

  • Userリソースの作成時、Mutating Webhookが平文パスワードをbcryptで自動ハッシュ化。CRDの保存フローの中で完結するため、アプリケーション側でのハッシュ処理が不要。
  • Tenant作成時にUUID形式のNamespaceを自動生成し、ユーザの隔離されたワークスペースを提供。
  • ユーザとテナントの関連をStatusで双方向に追跡し、1人のユーザが複数テナントに所属するケースにも対応。

VPC Controller ― ネットワーク機能の拡充

概要

前回のリファクタリングでVPC ControllerはVpcとSubnetの管理を担うようになりましたが、実際のクラウド運用ではこれだけでは不十分でした。
VPC内のVMにグローバルIPを割り当てる手段がなく、プライベートSubnetからインターネットへ出ることもできず、VPC内でのDNS解決もできない状態でした。

今回の開発ではExternalIP・FloatingIP・NATGatewayの3つのCRDを新たに追加し、これらの課題を解決しました。
いずれもKube-OVNのリソースを抽象化して管理する設計を踏襲しています。
たとえばFloatingIPは内部的にKube-OVNのIptablesFIPRuleを操作しますが、ユーザはmNi CloudのCRDだけを意識すればよく、Kube-OVN側の複雑な設定項目を露出させません。

リソース例

以下はFloatingIPリソースのYAML例です。ExternalIPで払い出したグローバルIPを、VMに紐付けることができます。

apiVersion: vpc.mnicloud.jp/v1alpha1
kind: FloatingIP
metadata:
  name: my-fip
  namespace: tenant-a
spec:
  externalIP: my-eip
  target:
    type: virtualMachines
    name: my-vm

機能詳細

  • ExternalIP:プロバイダネットワークからグローバルIPを払い出し、Kube-OVNのIptablesEIPへ投影。
  • FloatingIP:ExternalIPと内部リソースをNAT変換で紐付け。ターゲットのIP変動を検知するためKube-OVNのIPオブジェクトをWatchし動的に追従。
  • NATGateway:Subnet単位でアウトバウンド通信を有効化。作成時にVPCのデフォルトルート(0.0.0.0/0)を自動追加。
  • VPC-DNS:最初のSubnetがReadyになった時点で自動作成し、VPC内からのKubernetes DNS解決を有効化。

VPN Controller(新規作成)

概要

mNi CloudではVPC内のリソースはプライベートネットワーク上に配置されるため、外部から直接アクセスする手段がありませんでした。
開発・運用時にVPC内のVMへ接続するにはVPNが必要ですが、既存のVPNソリューションをKubernetes上で宣言的に管理する方法が確立されていません。

実装にはTailscaleのOSS版であるIonscaleを採用しましたが、IonscaleにはKubernetes向けのOperatorが存在しませんでした。
そこで、Ionscaleの管理を自動化するOperator「Kodiak」を自作しました。
mNi CloudのVpnリソースを作成すると、KodiakがTailnet・AuthKey・Connectorといったリソース群を自動生成し、VPCのCIDRをTailscale経由で広告します。

これにより、ユーザはVpnリソースをひとつ作成するだけで、VPC内部へのセキュアなアクセス経路が自動的に構築されます。

技術詳細

  • VpnリソースからKodiakリソース群(Tailnet、AuthKey、Connector)を自動生成し、Tailscaleネットワークを構築。
  • 参照するVPCのCIDRおよびオプションのextraNetworksをTailscale経由で広告し、クライアントからVPC内部への到達性を確保。
  • クライアント認証用のシークレットを自動管理し、Tailnetへの安全な参加を実現。
  • Statusに接続状態・Tailscale IP・コントロールプレーンURLを反映し、CLIやUIから接続情報を参照可能に。

フロントエンドの開発状況

バックエンドの構造変更に合わせ、フロントエンドも全面的に再構築しました。

マイクロフロントエンド化

前回のリファクタリングでMonorepoからPolyrepoへ転換しましたが、UIについてもこの方針を徹底しました。
VPC・VM・CTR・BS・VPN・Authの6つのサービスUIを完全に独立したアプリケーションとして実装しています。

開発フローとしては、共通のテンプレートリポジトリをフォークし、サービス固有のページを追加していく形を採っています。
テンプレートにはルーティング構成、認証処理、UIコンポーネント群があらかじめ組み込まれているため、新しいサービスUIの立ち上げが短期間で行えます。
実際、6つのUIは約10日間で構築しました。

リバースプロキシがパスベース(/vpc//vm/など)でルーティングを行うため、ユーザにはひとつのアプリケーションとして見えます。
一方で各UIは独立してビルド・デプロイできるため、あるサービスの更新が他に影響を与えることはありません。

フォームとYAMLの二重編集

リソースの作成・編集画面では、フォーム入力とYAMLマニフェスト編集をタブで切り替えられるようにしました。

API GatewayがYAMLベースの宣言的管理をサポートしているため、UIから操作するリソースもそのままYAMLとして表現可能です。
フォームの入力内容はリアルタイムにYAMLへ変換され、逆も同様です。

この仕組みにより、普段はGUIで操作しつつ、構成をGitで管理したいときにはYAMLをそのままコピーして使う、というワークフローが可能になりました。

通常のGUI

マニフェスト編集

Kube-OVN依存からの脱却 ― 自作CNIの開発

背景

前回の記事でも触れましたが、mNi CloudのネットワークはKube-OVNに依存していました。
Kube-OVNは高機能なCNIですが、OVS(Open vSwitch)ベースであるため、以下のような問題に繰り返し直面していました。

  • VPC・Subnet削除時にリソースが残留する不具合
  • EIP割り当ての不安定な挙動
  • OVSのデータパスがブラックボックス化し、障害時のデバッグが困難

これらの問題を根本的に解決するため、自作CNIの開発を進めています。

アーキテクチャ

自作CNIはController・Daemon・BGP Speakerの3コンポーネントで構成されています。

VPC / Subnet

Kube-OVNのVpc/Subnetを置き換える、自前のVPC・Subnet機能を実装しました。

VPCリソースはRouteTable(ルーティングテーブル)を管理する論理的なコンテナです。
Subnetリソースはそのvpc内にCIDRブロックを定義し、Controllerが一意なVXLAN VNI(Network Identifier)を自動的に割り当てます。
ゲートウェイIPはCIDRの先頭アドレスから自動生成され、ゲートウェイMACアドレスもランダムに割り当てられます。

Subnet間のテナント分離はVXLANヘッダのVNIで実現しています。
同一Subnet内のPodはL2レベルで通信でき、異なるSubnet間の通信はRouteTableに基づくL3ルーティングを経由します。

IPAM(IPアドレス管理)

Pod作成時、ControllerはNetworkInterfaceリソースを生成し、IPLeaseを通じてSubnet内のIPアドレスを払い出します。
IPLeaseはTTL(有効期限)をサポートしており、Podの削除後にIPアドレスを再利用可能です。

eBPFによるデータプレーン

Daemonは各ノード上でCNIのADD/DEL/CHECKリクエストを処理し、vethペアの作成やIPの割り当てを行います。
パケット処理にはeBPFプログラム(TC)を使用し、カーネル空間で完結させます。

eBPFプログラムは3つあり、それぞれ以下を担当します。

  • pod_egress:Podからの送信を処理。L2スイッチング、L3ルーティング、ARP応答を実行し、リモートノードへはVXLANでカプセル化して転送。
  • host_egress:ホストインターフェースからのトラフィックを処理。デフォルトSubnetのPodとの通信を担当。
  • vxlan_ingress:VXLANパケットを復号し、VNIからSubnetを特定して宛先Podへ転送。

ルーティングはSubnetごとのFIB(Forwarding Information Base)テーブルをeBPF Map(LPM Trie)上に構築しており、iptablesやOVSを一切使用しません。

BGPによるEIP / FIPの実現

外部接続にはBIRDを利用したBGP Speakerを実装しました。
各ノード上でDaemonSetとして動作し、以下の3つのCRDを監視してBIRDの設定を動的に生成します。

  • AddressPool:広報対象の外部IPアドレス帯を定義。
  • BGPPeer:上流ルーターとのBGPピアリング設定(ASN、ピアアドレス)。
  • BGPAdvertisement:どのAddressPoolをBGPで広報するかを指定。

ExternalIP(EIP)はAddressPoolから払い出され、BGPAdvertisementで広報対象に指定すると、BIRDがそのプレフィックスを上流ルーターへ広報します。
FloatingIP(FIP)でEIPとPodのIPを紐付けると、eBPFのFIBテーブルが更新され、外部から到達したトラフィックが該当Podへ転送されます。

この仕組みにより、BGPで広報されたグローバルIPを通じてVMやコンテナを外部に公開することに成功しました。
Kube-OVNのIptablesEIP/IptablesFIPRuleに依存していた従来の構成と異なり、iptablesを一切使わず、eBPFとBGPだけでEIP/FIPを実現しています。

おわりに

ここまでお読みいただきありがとうございます。
11月〜2月の開発では、認証基盤をKubernetes CRDとして設計し、VPCネットワーク機能を拡充し、Ionscale向けのOperatorを自作してVPNを実装しました。
フロントエンドもテンプレート駆動のマイクロフロントエンドとして再構築し、バックエンドとの統合が完了しています。
そして自作CNIでは、VPC・Subnetの独自実装、eBPFによるデータプレーン、BIRDによるBGP広報を組み合わせ、EIP/FIPの動作を確認しました。

今後は自作CNIへの本格移行、監視・メトリクス基盤の整備、クラスタ管理機能の拡張などを予定しています。

引き続きmNi Cloudをよろしくお願いいたします。

Discussion