きっかけは、会社の新規プロジェクトでシステムのベンチマークを行っていたときに遭遇した Unknown Known だった。経験を積んだ SRE なら誰でも解決方法を知っているのに、誰もそれが起きていることに気づいていなかった問題だ。
エンジニア数人分の年収に匹敵する請求書
私たちは Cloud Kubernetes 上に大規模な分散データシステムを構築していた。ベンチマークの際、persistent disk(PD)の IOPS が不足していたため、分散データベースシステム全体を何度も何度も再構築することになった。しかし Kubernetes の PD にはある特性がある。データベースを削除しても PD 自体は削除されない。システムは、その上に重要なデータが残っているかどうかを判断できないからだ。
テストを繰り返すうちに、高 IOPS の PD が大量に積み上がっていった。これらの PD は費用が高く、それが発覚したのは数週間後 — その月の請求額だけで、エンジニア数人分の年収に匹敵していた。
Kubernetes の運用を数年経験した SRE であれば、これが何の問題かも、その解決策も即座にわかる。どの instance にも attach されていない PD を洗い出すシンプルなスクリプトがあれば十分だ。だが本当に問うべきは「なぜ誰もそのスクリプトを書かなかったのか」ではなく、これが監視体制の分散と脆弱さを露呈させたという事実そのものだ。
ふたつのインフラ統治モデル
多くの企業は、ふたつのモデルのあいだのどこかに位置している。
中央集権型:会社の IT 部門がすべての開発システムを維持し、統一された管理基準を定める。最小限の人員ですべてのシステムをカバーできる。副作用は権限の閉鎖性だ — すべての開発・デプロイが単一の維持管理チームを経由するため、緊急時や部門が分散している場面では大きな足かせになる。もうひとつの問題は技術的な閉鎖性で、マルチクラウドであっても各プラットフォームの運用方法はまったく異なる(GCP Kubernetes と AWS ECS など)。人員に限りのある維持管理チームは、通常ひとつのシステムに収束させ、その運用強化に力を注ぐ傾向がある — しかし一部の特化型サービスにとっては、それが最適解とは限らない。たとえば社内の休暇申請システムは、AWS Lambda + API Gateway + DynamoDB で構築すれば月額数ドルで済むが、無理に Kubernetes に乗せると数十〜数百ドルの固定コストが発生する。中央集権型の利点は、会社全体でひとつの規則を共有できるため、監査・権限管理・システム安定性のリスクを最小化できる点にある — サービス構成が一貫しているため、監視も継続的に再利用できる。
分散開発型:各チームが独自に AWS Account や GCP Project を作成してプロジェクトを管理する。アカウントに対する裁量が最も大きく、初期段階では急速に前進でき、特定の技術に縛られないため柔軟性が高い — まるで小さなスタートアップの集まりのようだ。しかし問題も同様に明確だ。各チームが専門的な運用人員を必要とし、プロジェクトごとに技術が異なるため、監視・管理・監査がほとんど使い回せない。実質的に、プロジェクトごとにゼロから作り直すことになる。
なぜこの種の事故は分散開発型でしか起きないのか
先に述べた請求事故は、まさにこの分散開発型の事例で起きた。維持管理チームが問題発生の瞬間にすぐ原因を把握できたとしても、監視の構築速度がリソース作成の速度に追いつかなければ、この種の問題は起こる。さらにエンジニアの離職率の高さ、動的に立ち上がるプロジェクトチーム、バックエンド開発や QA からロールチェンジしてきたメンバーが新しい技術に憧れて未知の技術スタックで管理システムを組みたがること — これらすべてが監視のカバレッジをさらに追いつきにくくする。
数十チームを統括するマネジメント層の視点から見れば、同じ種類の事故は繰り返し発生する。年に十数件の P0、その多くは基本的すぎるほど基本的なものだ — 証明書の更新忘れ、動作しない Auto Scaling、実装ミスのある Rate Limit や Cache、監視されないまま使い果たされてシステムが落ちて初めて気づくリソース。新しいチームが新しい技術を使いたがること自体は問題ではない。問題は、同じ種類の事故がチームをまたいで何度も繰り返されることだ。
解決策は RCA の精緻化ではなく、リソースに追従して自動で現れる監視
私の見解では、ここで本当にすべきことは RCA をより丁寧に行うことでも、表面的に監視をひとつ追加することでもない。ひとつの管理方式を強制的に適用することだ — リソースが作成された瞬間に、監視もその場で生成されるようにする。
Kubernetes 上ではすでに参考にできる仕組みがある。Prometheus の resource discovery のように、新しい Pod や Service が現れれば監視も自動的についてくる。しかし GCP や AWS 上の Kubernetes ではないリソース — RDS、Cache Server、Queue など — については、それらを検出する自動化スクリプトを書く必要がある。これはリアルタイムである必要はない。リソースが作成されてから実際に稼働するまでには、通常少なくとも 24 時間の猶予がある。だから毎日一回リソースをスキャンし、新しく現れたものを監視システムに自動で追加するスケジュールで十分だ。DNS についても同じ理屈が成り立つ — 新しい HTTPS エンドポイントが DNS に登録された瞬間、証明書の有効期限監視と health check が自動で作成されればいい。
華やかではないが、実際に機能する
この種の仕組みは決して華やかではないが、非常に実用的だ。このシステムを使うチームがひとつでも P0 incident を経験し、汎用的な監視をひとつ追加すれば、以降はほかのすべてのチームがその恩恵を受けられる。ただしひとつ前提がある — 追加する監視のひとつひとつが、ほかのチームとの互換性を考慮している必要がある。
だが各プロジェクトが再びそれぞれ独自にやり始めた瞬間、この仕組みは崩壊し、それぞれに特化した監視システムがまた個別に生まれてしまう。管理技術の要点は、特定の技術スタックに全員を縛りつけることでは決してない。新しく開発する管理システムが、すべてのプロジェクトに目を配れるか、ほかのチームの適合性を考慮できるか、そしてこれまで苦労して築き上げてきたすべての監視が、そのなかにきちんと含まれているかどうかだ。