カタログは登録時ではなく、時間とともに腐る
一言でいうと
カタログが崩れるのは、最初に登録するときではなく、半年後です。腐敗は、ほぼいつも3つの形で現れます。必須フィールドが欠けたエンティティ、存在しない対象を指す参照、そして互いを指し合う依存関係です。3つとも、ポータルを起動しなくても、静的に見つけられます。
なぜ必要なのか
ポータルをはじめて開くときは、たいていきれいです。人が手で入れて、入れた人がまだそのサービスを覚えているからです。問題は、そのあとです。
- チームが統合されて
team-paymentsがなくなったのに、そのチームをオーナーとして書いたエンティティが12個残ります。 - サービスが別のシステムに移ったのに、
spec.systemは以前の名前のままです。 - 一時的に作ったデータベースを削除したのに、
dependsOnには、まだそのResourceがあります。
ここで重要なのは、Backstageがこれらをエラーとして知らせてくれないという点です。スキーマに違反したエンティティは、カタログがエラーとして表示しますが、存在しない対象を指す参照は、ただ「関係が計算されなかった」ことになります。画面には空白が見えるだけで、赤い文字はありません。
そして、カタログが死ぬ経路は、いつも同じです。参照が切れ、グラフが断片化し、画面が空に見え、人々が見なくなり、誰も直しません。空の画面は障害のように見えないので、誰も報告しません。
どう動くのか
診断すべきことは4つ
1つ目は、必須フィールドの欠落です。必須フィールドは、kindごとに違います。ComponentとAPIは、spec.type、spec.lifecycle、spec.ownerがすべてある必要があり、Resourceはspec.typeとspec.owner、SystemとDomainはspec.ownerが必要です。ここに、すべてのkindに共通して、apiVersion、kind、metadata.nameが必要です。「必須フィールド3つ」とまるごと暗記すると、kindが変わったときに間違えます。
2つ目は、切れたオーナーです。最も高くつく欠陥です。オーナーが切れると、障害時のページ、脆弱性のチケット、コストの帰属、廃止の判断のすべてが、行き場を失います。そして、原因がエンティティのファイルではない場合が多いです。組織データの同期が止まって、Group自体がカタログに入ってこなかったのです。ファイルだけを直すと、来週、同じことがまた起きます。
3つ目は、切れた参照です。spec.system、spec.domain、spec.dependsOn、spec.providesApisが対象です。オーナーと分けて見る理由は、対応が違うからです。オーナーは組織の問題で、残りはたいてい、名前が変わったか、エンティティをまだ作っていないかです。
4つ目は、依存の循環です。A dependsOn Bで、B dependsOn Aという状態です。こうなると、影響度分析が意味を失います。「これを止めたら何が壊れるか」と問うたときに、答えが自分自身に戻ってきます。原因は、たいてい誤解です。AがBを呼び出すから、B側にもAを書く必要があると考えることですが、関係は、片側の宣言で、カタログが双方向を計算します。
比較する前に、参照を正規化する
これを抜かすと、検査ツールが、問題のない参照を欠陥として報告します。
team-orders → group:default/team-orders
group:team-orders → group:default/team-orders
group:default/team-orders → group:default/team-orders
3つは、同じものを指しています。kindはフィールドごとにデフォルト値が決まっていて、ネームスペースのデフォルト値はdefaultです。文字列をそのまま比べると、3つの表記がすべて別のものになってしまいます。正規化してから比べることが、この作業の最初の段階です。
直す順序がある
- 先に組織データ: 存在しないGroupとUserを埋めます。
- その次にまとまり: 存在しないSystemとDomainを作るか、移った先の名前に参照を直します。
- 個別のエンティティは最後: 欠けた必須フィールドを埋めて、残った参照を整理します。
順序をひっくり返すと、同じエラーを2回直すことになります。個別のエンティティのオーナーを1つずつ直しておいてからGroupを作ると、直したばかりの値が、また不自然になります。
そして、必ず守るべきことが1つあります。欠陥のあるエンティティを削除して、一覧をきれいにすることは、直したことではありません。カタログの目的は、私たちが持っているものを、漏れなく見せることです。見えないようにしたサービスも、午前3時には壊れます。
ゲートで止める
catalog-info.yamlがコードのリポジトリにあるので、検査をPRで動かせます。ファイルがマージされる前に止めれば、カタログが腐る速度が大きく下がります。ゲートは、きれいな入力には0を、欠陥のある入力には0以外の値を出す必要があります。
現場での姿
このリポジトリには、ゲートに関する痛い記録があります。「ドキュメントに平文のパスワードがない」という検査項目が、長い間緑のままでしたが、実は1つの形しか見ていませんでした。その間、複数のツールの管理者パスワードが、2つのドキュメントに平文で存在し、3つともそれぞれ違う形だったので、どのパターンにも引っかかりませんでした。
教訓は、正規表現ではありませんでした。ゲートを作ったら、止めたいものを実際に入れてみて、赤信号が点くかを確認する必要があります。通過するものだけを確認すると、そのゲートは、何でもないまま数か月が過ぎます。カタログのlintも、まったく同じです。きれいなディレクトリで0が出ることだけを見て満足すると、欠陥が入っても0を出すゲートを、デプロイすることになります。
同じ感覚が、このクラスターで繰り返し得た教訓である、「状態がReadyであることと、実際に動作することは別の命題」とつながります。カタログの画面が正常に見えることと、その中の関係が実際につながっていることは、別の事実です。後者は目で見られないので、必ず計算して確かめる必要があります。
次のラボですること
/root/cba-audit/incoming/に、引き継いだカタログの8件を、そのまま再現します。その中には、欠陥の4つの種類が、わざと入っています。そのあと、必須フィールドの欠落、切れたオーナー、切れた参照、依存の循環を、それぞれ診断して一覧に取り出し、直したコピーを作って、4つの診断がすべて0になるようにします。最後に、同じ検査を行うlintスクリプトを書いて、きれいな入力と欠陥のある入力の両方でテストし、監査の結果をクラスターに記録します。