TT Lab
はじめる
学ぶ 学習パス コース

CBA — Backstage認定アソシエイト

エンティティと関係 — 所有権がカタログの心臓である理由

TT Labで続きを見る

一言でいうと

カタログは、サービスの一覧ではなく関係のグラフです。エンティティの種類を暗記するよりも、各種類がどんな質問に答えるために存在するのかを理解するほうが、試験にも実務にも役立ちます。

なぜ必要なのか

「サービスの一覧」だけでは、次のような質問に答えられません。

そのため、Backstageはエンティティを複数の種類に分け、その間の関係を保存します。

どう動くのか

エンティティの8つの種類

kind 答える質問 代表的なフィールド
Component 私たちが作ってデプロイするソフトウェアの断片 spec.type(service/website/library)、spec.lifecycle、spec.owner、spec.system
API コンポーネントが公開または利用するインターフェース spec.type(openapi/asyncapi/graphql/grpc)、spec.definition
Resource コンポーネントが必要とするインフラ spec.type(database/s3-bucket/queue)
System 一緒に動作するエンティティのまとまり spec.owner、spec.domain
Domain 複数のシステムを包括する事業領域 spec.owner
Group チーム・組織の単位 spec.type(team)、spec.children、spec.profile
User 人 spec.memberOf
Location ほかのエンティティがある場所を指す道しるべ spec.type、spec.targets

ここに、Template(スキャフォルダー用)が加わります。そして、spec.lifecycleは自由な文字列ですが、慣例的にexperimental / production / deprecatedを使います。廃止予定のサービスを一覧からふるい落とすのに使われます。

関係(relations)は計算されるもの

重要なポイントです。catalog-info.yamlに書くのは、spec.owner、spec.system、spec.providesApisのような宣言で、カタログがそれを読み取って、双方向の関係を計算して保存します。

spec.owner: group:team-checkout      →  ownedBy / ownerOf
spec.system: commerce                →  partOf / hasPart
spec.providesApis: [checkout-api]    →  providesApi / apiProvidedBy
spec.consumesApis: [payments-api]    →  consumesApi / apiConsumedBy
spec.dependsOn: [resource:checkout-db] →  dependsOn / dependencyOf

そのため、providesApisを片側だけに書いても、APIエンティティのページに「このAPIを提供するコンポーネント」が表示されます。反対方向を手で書く必要はありません。試験で「関係を両方のファイルにすべて書く必要があるか」と問われたら、答えはいいえです。

エンティティ参照の形式

関係を書くとき、ほかのエンティティを指す文字列の形式が決まっています。

[<kind>:][<namespace>/]<name>

3つの部分のうち、kindとnamespaceは省略でき、省略すると、コンテキストに応じたデフォルト値が適用されます。namespaceのデフォルト値はdefaultです。

書いたもの 解釈
team-checkout コンテキストのデフォルトのkind + defaultネームスペース
group:team-checkout group:default/team-checkout
group:payments/team-checkout ネームスペースまで明示

spec.ownerのようなフィールドは、デフォルトのkindが決まっているので、team-checkoutとだけ書いても動作しますが、明示的にgroup:を付けるほうが、レビューでははるかに安全です。人が読むときに、それがチームか個人かがすぐにわかるからです。

なぜcatalog-info.yamlはコードの隣に置くのか

カタログを埋める方式は、3つあります。

  1. 静的登録: Backstageの設定ファイルにURLのリストを書きます。小規模なら使えますが、増えると管理できなくなります。
  2. Locationエンティティ: 道しるべのエンティティが、ほかのファイルを指します。階層的にまとめるときに便利です。
  3. ディスカバリー(discovery): 組織のリポジトリを走査してcatalog-info.yamlを見つけ、自動で登録します。実務での答えです。

3つ目が成り立つには、ファイルがコードと同じリポジトリになければなりません。この配置がもたらす効果は大きいです。

逆に、カタログを中央のリポジトリ1か所にまとめておくと、そのファイルを直す動機のある人が誰もいなくなります。カタログが腐る、最もよくある経路です。

所有権が心臓部である理由

カタログで、たった1つだけ正確でなければならないとしたら、それはオーナーです。

そのため、オーナーは人(User)ではなく、チーム(Group)でなければなりません。人は退職し、チームは引き継がれます。試験でよく出るポイントであり、実務でカタログが崩れる最初の原因でもあります。

現場での姿

著者のホームラボで、Kubernetesのワークロードにapp.kubernetes.io/name、app.kubernetes.io/part-ofのような標準ラベルを付ける習慣は、このカタログの考え方とまったく同じものです。Helmチャートのベストプラクティスでも、app.kubernetes.io/name、instance、version、component、part-of、managed-byの6つを標準として付けるよう勧めています。

つまり、同じ所有権・所属の情報を、クラスターのラベルとカタログのエンティティの2か所に一貫して表現することが、実務の姿です。そして、この2つをつなぐのが、BackstageのKubernetesプラグインです。エンティティにアノテーションbackstage.io/kubernetes-idを付けておくと、その値と同じラベルを持つワークロードをクラスターから探して、エンティティページに表示します。

ホームラボのGPUノードに付けたgpu.homelab/tier=xlargeのような、意味に基づくラベルも、同じ精神です。物理的な事実を、人が判断に使える語彙に翻訳することです。カタログがシステム・ドメインでサービスをまとめる理由と、変わりません。

次のラボですること

/root/cba-catalog/に、Component、API、Resource、System、Domain、Group、User、Locationのエンティティファイルを自分で書きます。そのあと、同じ所有権の情報を実際のクラスターのラベルで表現してkubectlで確認し、最後にすべてのエンティティの参照文字列を、正規化された形式で取り出してみます。