Skip to content

アーキテクチャ

全体像

Cozystack は 1 つのバイナリではない。少数の Go コントローラと大量の Helm chart カタログの集合体で、既存の Kubernetes クラスタの上に載る。Go コードは cmd/ 以下、カタログは packages/ 以下にある。テナントが実際に話しかけるコントローラは cozystack-api で、apps.cozystack.iocore.cozystack.io の API グループを提供する集約 apiserver だ。テナントがこの API で作ったものはすべて Flux の HelmRelease になり、実作業 (chart のインストール) は Flux の helm-controller が行う。

コンポーネント

cozystack-api

集約 apiserver。kube-apiserverAPIService 経由で apps.cozystack.io グループをこれに委譲し、PostgresKubernetesVMInstance などのテナント向け kind を提供する。エントリポイントは cmd/cozystack-api/main.go:27 で、options を組んで generic apiserver を起動するだけだ。起動時にクラスタから ApplicationDefinition リソースを読み、kind ごとに REST storage を 1 つ登録する (内部実装 参照)。

cozystack-operator と cozystack-controller

cozystack-operator はプラットフォーム自身のパッケージをリコンサイルする。platform パッケージを受け取り、システムコンポーネントの HelmRelease を生成する。cozystack-controller はプラットフォーム横断のコントローラを動かす。どちらも cmd/ 以下にあり、補助バイナリの backup-controllerbackupstrategy-controllerflux-shard-operator (Flux の負荷を shard 分割する)・flux-plungerkubeovn-plungerlineage-controller-webhookcheck-readiness、そして chart パッケージング CLI の cozypkg が並ぶ。

パッケージカタログ

packages/ は Helm chart を保持し、3 つに分かれる。packages/apps/ はテナント向けカタログで、kubernetespostgresmariadbmongodbredisclickhousekafkarabbitmqnatsopensearchqdrantfoundationdbharborvm-instancevm-diskvpcvpntenanthttp-cachetcp-balancerbucketopenbao を含む。packages/system/ はプラットフォームを構成する CSI・CNI・operator の chart (130 以上) を持つ。-rd 接尾辞の chart はテナント向け kind を記述する ApplicationDefinition リソースを配る。packages/core/installerplatformtalosflux-aio を持つ。

同梱スタック

システム chart は Cozystack が土台にするプロジェクトを同梱する。Flux (リコンサイルのバックエンド)、KubeVirt + CDI (VM)、Cluster API + Kamaji (テナントのコントロールプレーン)、CloudNativePG (Postgres)、LINSTOR / Piraeus + SeaweedFS (ストレージ)、Cilium + Kube-OVN (ネットワーク)、MetalLB (ロードバランサ)、Talos Linux (OS)、Keycloak (OIDC)、VictoriaMetrics + Grafana (可観測性) だ (README, 出典 2)。

リクエストの流れ

テナントが Postgres を作る流れを追う。

  1. テナントは apps.cozystack.io/v1alpha1Postgres オブジェクトを自分の namespace に kubectl で適用する。
  2. kube-apiserverapps.cozystack.io グループが cozystack-api に委譲されているのを見て、create を転送する。
  3. cozystack-apiREST.Create (pkg/registry/apps/application/rest.go:166) で受ける。名前の形式と長さを検証し、_ 始まりの予約キーを弾き、検証 admission チェーンを明示的に実行する (pkg/registry/apps/application/rest.go:210)。
  4. ConvertApplicationToHelmRelease (pkg/registry/apps/application/rest.go:216) で変換する。生成される HelmRelease<prefix><app-name> という名前で、固定の ChartRefpackages/apps/postgres chart を指し、必ず Secret/cozystack-values を mount し、Values: app.Spec を設定する。つまりテナントの spec がそのまま chart の Helm values になる (pkg/registry/apps/application/rest.go:1605)。
  5. HelmRelease をテナント namespace に作成する (pkg/registry/apps/application/rest.go:238)。Cozystack の別 store は存在せず、HelmRelease が永続化された状態そのものだ。
  6. Flux の helm-controller が HelmRelease をリコンサイルし chart をインストールして、実際の CloudNativePG クラスタを立ち上げる。
  7. 読み取り時、cozystack-apiHelmReleaseApplication に逆変換し status を合成するので、テナントには裏の HelmRelease ではなく Postgres オブジェクトが見える。

主要な設計判断

API は翻訳層であってデータストアではない。Cozystack はテナントリソースを自前の etcd テーブルに持たず、各 Application を Flux の HelmRelease に射影し、リコンサイル・履歴・リトライを Flux に委ねる (pkg/registry/apps/application/rest.go:1605)。この選択によって、1 つの汎用 REST 実装で全 kind を支えられる。

kind はコードでなくデータだ。新しいマネージドサービスは packages/apps/ の Helm chart と (-rd chart が配る) ApplicationDefinition で成り立つ。cozystack-api は起動時にそれらの定義を読み、各 kind を動的に登録する (pkg/apiserver/apiserver.go:229)。そのためサービスの追加に Go バイナリの再ビルドは要らない。詳細は 内部実装 を参照。

Flux への負荷は shard 分割される。あらゆるテナントリソースが HelmRelease になるため、賑わうプラットフォームでは大量に生まれる。flux-shard-operator はそのリコンサイル負荷を複数の helm-controller shard に分散するために存在する。

拡張ポイント

  • ApplicationDefinition リソースはコード変更なしで新しいテナント向け kind を定義する。-rd chart が配送手段だ。
  • packages/apps/ カタログは Helm chart なので、事業者は chart を fork/追加して kind がプロビジョニングする内容を変えられる。
  • kind ごとの挙動 (install/upgrade のタイムアウト、リトライ、wait 無効化) は ApplicationDefinition のアノテーションで設定し、変換時に読まれる (pkg/registry/apps/application/rest.go:1553)。
  • プラットフォームは標準の Kubernetes と Flux の拡張面を再利用する。admission webhook・CRD・Flux HelmRelease の values だ。