CNPA — クラウドネイティブプラットフォームエンジニアリングアソシエイト
塞ぐ前に知らせることが開発者体験だ
一言でいうと
ガードレールは、「止める」ものと「知らせる」ものを分けて配置する必要があります。ルールは1か所で定義して複数の場所で実行し、各ルールには識別子と直し方が一緒に付いていてこそ、メトリクスにも案内にもなります。
なぜ必要なのか
プラットフォームチームがルールを強制することにしたとき、たいていはアドミッションWebhookがまず答えとして出てきます。ところが、アドミッションはいちばん最後の場所です。開発者は、コードを書き、コミットし、パイプラインを回し、イメージを作り、デプロイを押して、そこで初めて「リソースのrequestがありません」と聞かされます。直すのに1分かかる問題を、見つけるのに30分かかります。
逆に、すべてを手前で止めると、別の問題が起きます。ルール1つ1つがすべて必須になると、試作品を1日で立ち上げたかったチームは、プラットフォームを迂回する方法を探します。迂回が始まると、ルールの数は増えるのに実際の準拠率は下がり、プラットフォームチームはその事実さえ知りません。
そこで、質問を2つに分ける必要があります。このルールは、破ったときに何が危険なのか、そしてどこで知らせるのがいちばん安いのかです。
どう動くのか
ルールには識別子・重大度・直し方が付きます
ルールを文章でしか書かないと、数えることも、案内することもできません。ルール1つを、次の3つと一緒に定義します。
| 項目 | なぜ必要なのか |
|---|---|
識別子(例: PR001) |
準拠率をルールごとに数えられ、例外をルール単位で与えられます |
| 重大度(block・warn) | 止めるものと知らせるだけのものを分けておくと、迂回が減ります |
| 直し方 | 診断結果を読んだ人が、次に何をすべきかがわかります |
重大度を分ける基準は、好みではありません。破ったときに、ほかの人に被害が及ぶかを見ます。タグがlatestのイメージは、ロールバックを不可能にし、クラスター全体の再現性を壊すため、止める価値があります。一方、readinessプローブがないのは、そのサービス自身の可用性の問題なので、知らせてチームに判断させるほうがよいです。
同じルールを複数の場所で実行します
ルールの定義は1か所に置き、実行は複数の場所で行います。
- エディタとローカルコマンド: 最も安く、最も速いです。ただし、実行するかどうかが人に任されています。
- コミットフック: コミットの直前に動きます。ローカルのファイルなので迂回でき、最後の防衛線にはなれません。
- CI: リポジトリの外で動く最初の場所です。ここからは、迂回が記録に残ります。
- アドミッション: クラスターに入る最後のゲートです。ここだけで止めると、フィードバックがいちばん遅くなります。
手前の場所は速いフィードバックを担当し、後ろの場所は保証を担当します。手前だけに置くと守られず、後ろだけに置くと知るのが遅れます。
出力は人向けと機械向けの両方を出します
診断ツールが、人には読みやすい行を、プログラムにはJSONを出せば、同じツールが複数の場所で使えます。終了コードも規約です。違反が1つでもあれば、0以外の値で終了してこそ、パイプラインがそれをシグナルとして使えます。こうした契約がないと、場所ごとに別のツールを作ることになり、場所ごとにルールが少しずつ違ってきます。
準拠率は採用メトリクスになります
同じ診断ツールをリポジトリ全体に回すと、ルールごとの準拠率が出ます。この数字は、プラットフォームチームが次に何を作るかを決める根拠になります。特定のルールだけ特に準拠率が低いなら、そのチームが怠けているのではなく、そのルールを守りにくくした私たちの落ち度である可能性が高いです。デフォルト値として入れる、スキャフォールドに最初から埋めておく、文言を直す、といった対応が必要なシグナルです。
ただし、分母には注意が必要です。診断対象の一覧が変わると、準拠率は誰も何もしなくても動きます。数字を見るときは、分母が何だったかも一緒に記録する必要があります。
現場での姿
LabHubの採点ツールは、まさにこの構造になっています。採点スクリプトは、失敗したときに「確認に失敗しました」と書くのではなく、いまの値が何で、何であるべきかを一緒に伝えるというルールが定められています。理由は同じです。何が間違っているのかわからない人に残された選択肢は推測だけで、推測が2、3回外れると、その人は離れていきます。
反対方向のトラブルもありました。このリポジトリには、かつて「どんな答えを入れても通る」採点ツールがいくつもありました。誰も報告しなかったため、長く残っていました。診断ツールも同じです。何も検出できない診断ツールは、ないより悪いものです。準拠率が100%に見えるのに、実際には何も検査していないからです。そのため、診断ツールは、わざと違反させたサンプルで、反対方向まで試す必要があります。
ガードレールを壁にしない方法
プラットフォームのガードレールは事故を防ぐ装置ですが、誤って作ると人々は迂回する方法を 先に覚えてしまいます。そうなると、統制もなくなり、信頼も失います。
止めるときは、代替案を同じ画面で示します。「privilegedは使えません」で終わると、 ユーザーは別のクラスターを探します。「privilegedの代わりにこのcapabilityを使い、 本当に必要ならこの手順で例外を申請してください」まで書いてあるべきです。拒否メッセージは、 ドキュメントへのリンクではなく、次の行動であるべきです。
警告から始めます。新しいポリシーは、まず監査モードで回して何が引っかかるかを数え、 その一覧をチームに見せたあとでブロックします。いきなりブロックすると、その日のデプロイがすべて失敗し、 その記憶が長く残ります。
例外を手順にします。例外がなければポリシーが崩れ、例外が簡単すぎるとポリシーが ないのと同じになります。答えは期限付きの例外です。90日後に自動で期限切れになり、 期限切れの前に通知が届きます。
セルフサービスが統制より先です。人々が欲しいものを簡単に手に入れられるなら、 迂回する理由がありません。ネームスペースを1つ作るのに承認が3日かかる組織では、 誰もが他人のネームスペースに相乗りして使います。
診断をユーザーに返します。「なぜ自分のPodが起動しないのか」をプラットフォームチームに尋ねる必要があるなら、 そのチームがボトルネックになります。イベント・ポリシーの拒否理由・リソース不足を1つの画面にまとめて 見せれば、ほとんどは自分で解決します。
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -20
kubectl describe pod <파드> | sed -n '/Events:/,$p'
成功を測るメトリクスは、「止めた回数」ではありません。その数字は、ポリシーがどれだけ煩わしいかを 測るだけです。測るべきものは、拒否されたあと、ユーザーが自分で直して成功した割合です。 その割合が低ければ、メッセージが悪いということです。
次のラボですること
ゴールデンパスのルール5つを、識別子と重大度が付いた値として定義し、そのルールを実際に判定する診断ツールを作ります。準拠するサンプルと、特定のルールだけを破ったサンプルをそれぞれ作って両方向から試し、結果をJSONでも出力したあと、サービス全体の準拠率を計算します。最後に、コミットフックに組み込んで、blockルールだけが実際に止まるかを確認します。