Skip to main content

テストを増やすほど本番は壊れる。Kubernetes運用で『機能フラグ』が監視より効く理由

なぜ「テストを増やす」という発想は本番障害を減らせないのか

section01

Kubernetes運用で本番障害が起きるたびに、多くのチームが取る対策は同じだ。「テストを増やす」「レビューを厳しくする」「ステージング環境を増やす」。しかし近年注目されているのは、テストの積み増しではなく機能フラグによる「壊れることを前提にした設計」だ。一見まっとうに見える従来の発想に、実は効果がないという指摘が開発現場から相次いでいる。

理由は単純だ。本番環境で起こりうる例外パターンは、事前にすべて洗い出せない。決済機能のような複雑な機能では、2回連続のボタン操作や無効なカード入力、モバイルブラウザ特有の挙動など、想定外の組み合わせが無数に存在する。QAがどれだけ徹底的にテストしても、実際のトラフィックパターンをラボ環境で再現するのは原理的に不可能に近い。

実際、DORA(DevOps Research and Assessment)の調査でも、デプロイ頻度と変更失敗率は必ずしもトレードオフの関係にないことが示されている。小さな変更単位で頻繁にデプロイするチームほど問題の特定と回復が容易になり、失敗率が下がる傾向があるという(DORA Metrics Guide)。「テストで完璧に防ぐ」より「小さく壊して、すぐ気づいて、すぐ戻す」仕組みの方が合理的だということだ。

Kubernetesの標準的なデプロイフロー、Rolling Updateも、この思想の上に成り立っている。Kubernetes公式ドキュメントによれば、Rolling Updateは新しいPodを段階的に起動しながら古いPodを置き換えダウンタイムなしの更新を実現し、maxUnavailablemaxSurgeで置き換えのペースを制御できる(Kubernetes公式ドキュメント)。しかしこれだけでは「デプロイした瞬間に全ユーザーへ影響が及ぶ」課題は解決されない(監視基盤の自前構築は時間がかかるが、Kuboのような標準搭載環境という選択肢もある。詳しくは後半で触れる)。ここで登場するのが、機能フラグという発想だ。

「壊れることを前提にする」設計への転換 — 機能フラグという安全弁

section02

機能フラグ(Feature Flag)とは、コードのデプロイと機能の有効化を分離する仕組みだ。Martin Fowlerの解説によれば、機能フラグはもともと「リリースサイクルより開発に時間がかかる機能を、コードの統合を止めずに隠しておく」ために使われ始めた手法で、A/Bテストフラグやユーザー属性ごとに機能を出し分ける権限フラグなど、複数の用途に発展している(Martin Fowler "FeatureFlag")。

この発想をKubernetes運用に当てはめると何が変わるか。従来は「コードを本番サーバーに配置する」ことと「ユーザーがその機能を使える状態にする」ことが同時に起きていた。機能フラグを使えば、新しいコードは先に全Podへデプロイしておき、実際に機能を有効にするユーザーの割合だけを外部から制御できる。

具体的には、まず対象ユーザーの1%だけに新機能を見せ、エラー率や応答時間に異常がないか確認する。問題がなければ10%に拡大し、最終的に100%まで引き上げる。この段階的な拡大は、GoogleのSREチームが提唱する「カナリアリリース」の考え方とも共通している。SREワークブックでは、カナリア対象が全体の5%でエラー率20%を記録しても、全体のエラー率は1%に抑えられるため、サービス全体の信頼性目標(エラーバジェット)への影響を大幅に抑制できると説明されている(Google SRE Workbook: Canarying Releases)。

つまり機能フラグは、「テストで100%の安全を事前に証明する」のではなく「小さな影響範囲で実際に試し、問題があればすぐ引っ込める」という、壊れる前提の設計思想を実装レベルで支える仕組みだ。

監視と自動ロールバックが「テストの代わり」になる仕組み

section03

機能フラグによる段階的な公開はまだ半分の仕組みだ。もう半分は、公開後に「異常をどう検知し、どう戻すか」という監視と自動ロールバックの仕組みである。

代表的な自動化ツールが、Kubernetes上で動くFlaggerと、Argo Projectが提供するArgo Rolloutsだ。Flaggerは、Prometheusなど複数の監視バックエンドにクエリを投げてリリースを分析し、定義したメトリクス閾値を下回れば自動的に前のバージョンへロールバックする、CNCF傘下のプログレッシブデリバリー用Kubernetesオペレーターだ(Flagger 公式サイト)。

Argo Rolloutsも同様の思想を持つ。AnalysisTemplateという仕組みで、Prometheusから取得した成功率などの指標に成功・失敗条件を定義し、満たさなければロールアウトを自動中止できる(Argo Rollouts公式ドキュメント: Analysis)。判定の元になる監視ルールは、PromQL式で閾値を定義し一定時間継続した場合のみ発火させる、Prometheusのアラートルール機能で構築するのが一般的だ(Prometheus公式ドキュメント: Alerting Rules)。

この判断基準が「エラーバジェット」という考え方だ。Google Cloudの解説によれば、エラーバジェットとはSLO(サービスレベル目標)に対して許容される失敗の余地であり、可用性目標99.9%なら0.1%がエラーバジェットとなる。余裕があれば新機能のリリースを進め、消費し尽くしていればリリースを止める、という客観的な意思決定の基準になる(Google Cloud Blog: SRE エラーバジェット)。

こうして見ると、「機能フラグ→段階的公開→メトリクス監視→自動ロールバック」という流れは、QAによる事前テストの代替として機能していることがわかる。少数のユーザーに影響を限定して検証し、異常があれば即座に戻す。これは「テストの量」ではなく「気づいて戻す速さ」の設計問題なのだ。

K3s/マネージドKubernetes環境でどこから始めるべきか

section04

ここまで読むと、「今すぐArgo RolloutsとFlaggerを導入しなければ」と焦る読者もいるかもしれない。しかし、これは早合点だ。成熟度に見合わない複雑な仕組みを性急に導入することは、かえって運用負荷を増やし、チームの疲弊を招く(DORA Metrics Guide)。

現実的な導入順序は次のようになる。

  • 第1段階: Kubernetes標準のRolling Updateを正しく設定する。maxUnavailablemaxSurgeを意識するだけでも、ダウンタイムなしの更新とロールバックの土台ができる(Kubernetes公式ドキュメント
  • 第2段階: PrometheusとGrafanaによる監視基盤を整え、エラー率やレイテンシのアラートルールを定義する
  • 第3段階: リリース頻度の高い機能から機能フラグを導入し、段階的な公開の運用に慣れる
  • 第4段階: チームの規模とリリース頻度が増えてきたら、Argo RolloutsやFlaggerによる自動化されたプログレッシブデリバリーへ移行する

多くの中小規模チームにとって、実は第1・第2段階を確実に押さえるだけで本番障害への耐性は大きく向上する。K3sベースのマネージドKubernetesサービスであるKuboは、Rolling Updateがデフォルトで機能し、Prometheus+Grafanaの監視基盤も標準搭載されているため、第1・第2段階を自前で構築する手間なくスタートできる。GitOps対応も標準化済みで、第3・第4段階へ進む準備が整った時の追加構築も最小限で済む。

まとめ

本番障害を減らす鍵は、テストの量を増やすことではない。事前にすべての例外パターンを洗い出すのは原理的に不可能であり、デプロイ頻度と安定性は対立しない。

代わりに有効なのが「壊れることを前提にした設計」だ。機能フラグでコードのデプロイと機能の有効化を分離し、1%→10%→100%と段階的にユーザーへ公開しながら、Prometheusベースの監視とエラーバジェットの考え方で異常を検知し、必要なら自動ロールバックする。この仕組みが、事前テストの限界を補う「後からでも間に合う安全網」として機能する。

すべてのチームが最初から高度な仕組みを導入する必要はない。Rolling Updateと監視基盤という土台を固め、成熟度に合わせて段階的にレベルを上げていくのが現実的だ。KuboのようにK3sベースで監視・GitOps対応が標準搭載されたマネージドKubernetes環境なら、その土台を最初から持った状態でスタートでき、必要になったタイミングで次の段階へも無理なく進める。まずは自分たちのチームが今どの段階にいるのか、Rolling Updateの設定とアラートルールの有無から確認してみてはどうだろうか。お問い合わせから、Kuboの監視・GitOps標準搭載の詳細を相談することもできる。

Related articles

← Back to all posts