カオスエンジニアリング入門:サービス障害に強いシステムを作る

統計では、Uptime Instituteの調査で重大障害の過半が10万ドル超の損失に達し、なかには100万ドルを超えるケースも一定割合存在すると報告されています¹²。公開されているレポートやホワイトペーパーの一般的な試算では、ダウンタイムのコストは1分あたり数千ドル規模とされることが多く⁴、ITICの年次調査でも1時間あたりの損失が30万ドル以上に達すると見積もる企業が少なくないことが示されています³。クラウド時代のマイクロサービス増加と依存関係の複雑化が影響範囲を拡大させていることは、SRE実務でも繰り返し指摘されています⁷。こうした背景で、障害は「起きないように祈る」対象ではなく、起きても壊れない能力(レジリエンス)を設計・検証する対象へと再定義されました。その中心にあるのがカオスエンジニアリングです。原則はシンプルで、まずシステムの定常状態(通常時に満たすべきふるまい)を定義し、現実的な故障を意図的に起こし、観測された振る舞いを仮説と比較して学習します⁵⁶。目標は破壊ではなく、仮説検証によって回復力に定量的な確信を持つことです。投資対効果の視点でも、MTTR(平均復旧時間)の短縮やSLO(サービスレベル目標)違反の抑制、リリース頻度の安定化は、直接的な機会損失の低減とチーム生産性向上に跳ね返ります。DORAの四指標が示すとおり、デリバリー性能と可用性はトレードオフではなく両立可能です⁸。
カオスエンジニアリングの基本とビジネス価値
カオスエンジニアリングは、運用環境に近い条件で、限定された影響範囲のもとに実験を行い、設計上の仮説を検証するアプローチです⁵⁶。研究や事例では、定常状態をSLOで表現し、実験の停止条件をガードレールとして明示することが安全性と学習効率を高めると示されています⁷。たとえば、APIのp95レイテンシ(全リクエストの95%がこの時間以内)を300ms未満、エラーレートを1%未満というSLOに定義し、片系のデータベース停止やネットワーク遅延を与えたときにも定常が維持される、といった仮説を立てます。ここで重要なのは、失敗を設計に還元するループを持続させることです。
ビジネス価値を考えるなら、目標は単に障害の件数を減らすことではありません。高額な障害を発生させない確率を上げ、起きた場合の復旧時間を最小化することがROIに直結します³。エラーバジェットの消費パターンが平準化されれば、プロダクトのリリース計画は乱れにくくなり、開発と運用の摩擦コストが下がります⁷。結果として、機能開発のスループットが上がり、同じ運用人員でより高い価値を届けられます。DORAの4指標が示すように、デリバリー性能と可用性はトレードオフではありません。リスクを可視化し、実験で安全に触って学ぶことで、両者を同時に引き上げることが可能です⁸。
実践設計:仮説、計測、ガードレール
まず前提として、実験は監視とアラートが整備された環境で行います。メトリクスでは可用性、レイテンシ、スループット、エラーレートを、分散トレーシングではクリティカルパス(ユーザー価値に直結する経路)のスパン遅延を、ログではデグレード時のエラーパターンを捉えます。SLOを定常状態の言語として使うのが設計の肝で、目標値とエラーバジェット、計測窓を明確にしてから故障仮説を与えます⁷。停止条件は、顧客影響やエラーレートの急増、p99遅延の急伸など、具体的な指標で即時中断できるように宣言しておきます。段階的に爆発半径(影響範囲)を広げるために、最初はテスト環境や一部のトラフィックに限定し、次にカナリア(段階的リリース)やオフピーク(閑散時間帯)での本番小規模実験へと進めます⁶¹⁴。
観測設計では、定常からの逸脱を定量で捉えることが不可欠です。Prometheusを用いる場合、レートや分位数が実験の成功判定に有効です⁹。以下のクエリはHTTPエラー率とレイテンシ分位数を可視化する例です。
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
このような可視化をダッシュボードに用意し、ガードレールを超えたときに即座に実験を打ち切る運用を整えます。どこまでなら安全かを事前に合意することで、学習の速度と安全性を両立できます¹⁴。
ツールとコード例:クラウドからKubernetesまで
現実の障害は多様ですが、注入する故障は代表的なカテゴリに集約されます。ネットワークの遅延と損失、プロセスやノードの停止、依存サービスのスローダウン、リソース枯渇が主なものです。ここでは本番相当の条件で扱いやすいツールを使い、ローカルからクラウドまで一貫した実験ができるように、具体的なコードを提示します。
Kubernetesでのネットワーク遅延注入(Chaos Mesh)
KubernetesではChaos MeshのNetworkChaosを用いると、特定のポッド間に遅延やパケットロスを与えられます。対象と遅延量を明示し、ネームスペースで爆発半径を限定します¹⁰。
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: api-to-redis-latency
namespace: chaos-testing
spec:
action: delay
mode: all
selector:
namespaces:
- prod-app
labelSelectors:
app: api
direction: to
target:
selector:
namespaces:
- prod-app
labelSelectors:
app: redis
mode: all
delay:
latency: "200ms"
jitter: "50ms"
correlation: "25"
duration: "5m"
適用の前後でp95レイテンシやキャッシュのヒット率を確認し、タイムアウトやリトライの設定が意図通りに働くかを観測します。事前にNetworkPolicyやHPAのしきい値が実験中に想定外の振る舞いをしないよう、変更点を記録しておくと再現性が高まります。
AWS Fault Injection Simulatorでのインスタンス停止
マルチAZ冗長の健全性を検証するには、Auto Scaling Group配下の一部インスタンス停止が有効です。AWS FISの実験テンプレートでは、対象をタグで絞り込み、停止割合や最大同時数を制御できます¹¹。
{
"description": "Stop 20% of ASG instances in one AZ",
"targets": {
"Instances": {
"resourceType": "aws:ec2:instance",
"resourceTags": {"Chaos": "true"},
"filters": [{"path": "State.Name", "values": ["running"]}]
}
},
"actions": {
"stopInstances": {
"actionId": "aws:ec2:stop-instances",
"parameters": {"duration": "PT5M"},
"targets": {"Instances": "Instances"}
}
},
"stopConditions": [
{"source": "aws:cloudwatch:alarm", "value": "arn:aws:cloudwatch:...:alarm/api-5xx-rate-high"}
],
"roleArn": "arn:aws:iam::123456789012:role/FIS-ExperimentRole"
}
CloudWatchのアラームを停止条件に設定し、エラーレートやスループットの急変で自動的に実験が中断されるようにします。ガードレールを自動化することで人的介入の遅延を排除できます¹¹。
Goのリトライとサーキットブレーカー実装
依存サービスがスローダウンしたとき、クライアントの防御が脆弱だとカスケード障害が起きます。Goでタイムアウト、指数バックオフ+ジッター、サーキットブレーカーを組み合わせた堅牢なHTTPクライアントを実装します¹²。
package main
import (
"context"
"errors"
"fmt"
"math/rand"
"net/http"
"time"
"github.com/sony/gobreaker"
)
func withJitter(base time.Duration) time.Duration {
jitter := time.Duration(rand.Int63n(int64(base / 2)))
return base + jitter
}
func main() {
rand.Seed(time.Now().UnixNano())
cb := gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "api",
MaxRequests: 5,
Interval: 30 * time.Second,
Timeout: 10 * time.Second,
ReadyToTrip: func(counts gobreaker.Counts) bool {
return counts.ConsecutiveFailures >= 5
},
})
client := &http.Client{Timeout: 2 * time.Second}
url := "https://api.internal.svc/resource"
attempt := 0
for attempt < 5 {
attempt++
ctx, cancel := context.WithTimeout(context.Background(), 2500*time.Millisecond)
res, err := cb.Execute(func() (interface{}, error) {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode >= 500 {
return nil, errors.New("server error")
}
return resp, nil
})
cancel()
if err == nil {
fmt.Println("success", res.(*http.Response).StatusCode)
break
}
sleep := withJitter(time.Duration(attempt) * 200 * time.Millisecond)
time.Sleep(sleep)
}
}
ここでは失敗の連続回数で回路を開き、タイムアウトで遅延を伝播させないようにしています。クライアントの回復力は実験の成否を大きく左右します¹²。
ローカルでの遅延注入(Toxiproxy + Docker Compose)
開発環境でもネットワークの不安定さを再現できると、回帰防止に役立ちます。Toxiproxyを使えば、依存先への遅延やスループット低下を手軽に試せます¹³。
version: "3.8"
services:
toxiproxy:
image: shopify/toxiproxy:2.5.0
ports: ["8474:8474", "8666:8666"]
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: pass
ports: ["5432:5432"]
api:
build: ./api
environment:
DATABASE_URL: postgres://postgres:pass@toxiproxy:8666/postgres?sslmode=disable
depends_on: [toxiproxy, postgres]
起動後にプロキシを作成し、遅延を付与します。
curl -sX POST localhost:8474/proxies -d '{"name":"pg","listen":"0.0.0.0:8666","upstream":"postgres:5432"}'
curl -sX POST localhost:8474/proxies/pg/toxics -d '{"name":"latency","type":"latency","attributes":{"latency":200,"jitter":50}}'
アプリケーションのタイムアウトやコネクションプールの設定が過負荷時に適切かどうか、ユニットテストでは見えない振る舞いを確かめます。
CIに組み込む実験(GitHub Actions)
実験を定期的に回すには、CIに小規模の故障注入とスモークテストを組み込みます。これは継続的検証(Continuous Verification)の一形態としても位置づけられます¹⁵。GitHub Actionsの例では、KubernetesにNetworkChaosを適用し、Prometheusのメトリクスでガードレールを判定します。
name: chaos-smoke
on:
schedule:
- cron: '0 3 * * 1-5'
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Kubectl
uses: azure/setup-kubectl@v4
- name: Apply chaos
run: kubectl apply -f k8s/chaos/network-latency.yaml
- name: Wait and verify
run: |
sleep 120
ERR_RATE=$(curl -s "http://prometheus/api/v1/query?query=sum(rate(http_requests_total%7Bstatus%3D~%225..%22%7D%5B5m%5D))%2Fsum(rate(http_requests_total%5B5m%5D))" | jq -r .data.result[0].value[1])
echo "error_rate=$ERR_RATE"
awk -v e=$ERR_RATE 'BEGIN{ if (e>0.02) exit 1 }'
- name: Revert chaos
if: always()
run: kubectl delete -f k8s/chaos/network-latency.yaml --ignore-not-found
結果はダッシュボードに集約し、しきい値を超えた場合はCIを失敗させて検知と回帰防止の両方を満たします。定常的な小さな摩擦が大きな事故を未然に防ぐという設計思想です。
組織への定着:ガバナンスとROI測定
実験を継続するには、ガバナンスの枠組みが必要です。まず、誰がいつどの範囲で実験できるかを明確にし、停止条件と責任の所在を共有します¹⁴。変更管理の一環として、CAB(Change Advisory Board)や運用レビューに実験計画を持ち込み、影響評価とリスク承認を行います。次に、学習を組織の資産に変える仕組みを用意します。実験ごとに目的、仮説、手順、観測、結果、設計反映をドキュメント化し、ランブックやSLO定義に反映します⁷。定期的なゲームデイを開催し、オンコールを含む関係者が同じ画面で可視化と対応手順を確認できるようにします¹⁴。
ROIの測定では、MTTR、インシデントの重症度分布、エラーバジェットの消費パターン、SLO違反件数、デプロイの中断率などを時系列で追います⁷⁸。高額障害の発生確率を下げることが最も大きな節約であるため、上位パーセンタイルの被害額を下げられたかを評価軸に置きます¹²。あわせて、開発のスループットやサイクルタイム、リリース信頼度の改善も観測しておきます⁸。人的負荷の観点では、オンコールの睡眠妨害回数やアラートノイズの減少が、リテンションや生産性に与える正の効果として現れます。最後に、セキュリティとプライバシーの観点での境界を設け、顧客データに触れない実験範囲を定義します。可観測性のデータ保持期間やアクセス権限も、実験ログの共有に先立って整理しておきましょう¹⁴。
まとめ:小さく、計測し、学び続ける
障害のないシステムは現実に存在しませんが、障害に強いシステムは設計と検証で作れます。重要なのは、小さく始め、計測で語り、学習を設計に還元するループを絶やさないことです。まずはSLOを定常状態の言語として整え、最小の爆発半径で一つの故障仮説を試し、観測結果からクライアントや冗長化、運用の改善を反映してみてください⁶⁷。ひとつの成功体験が、ゲームデイの文化やCIへの自動化へとつながり、結果として高額な障害の確率を下げ、復旧時間を短縮します¹²¹⁴。次にどの仮説を検証しますか。今日の30分を、未来の長い深夜対応を減らす投資に変えていきましょう。
参考文献
- Uptime Institute Journal. The growing cost of data center outages. 2022. https://journal.uptimeinstitute.com/the-growing-cost-of-data-center-outages/
- Uptime Institute Journal. Annual outage analysis 2023. 2023. https://journal.uptimeinstitute.com/annual-outage-analysis-2023/
- Information Technology Intelligence Consulting (ITIC). 2024 Hourly Cost of Downtime Survey (Part 2). 2024. https://itic-corp.com/itic-2024-hourly-cost-of-downtime-part-2/
- Gatling. The Cost of Downtime: How to Calculate the Cost for Your Business. 2023. https://gatling.io/blog/the-cost-of-downtime/
- CNCF Chaos Engineering WG. Glossary. https://github.com/cncf/chaosengineering-wg/blob/main/glossary.md
- Principles of Chaos Engineering. https://principlesofchaos.org/
- Beyer B, Jones C, Petoff J, Murphy N (eds.). Site Reliability Engineering: How Google Runs Production Systems. O’Reilly; 2016. https://sre.google/sre-book/table-of-contents/
- Google Cloud. 2021 Accelerate State of DevOps Report. 2021. https://cloud.google.com/blog/products/devops-sre/announcing-2021-accelerate-state-of-devops-report
- Prometheus Documentation. Histograms and summaries. https://prometheus.io/docs/practices/histograms/
- Chaos Mesh Docs v2.5.2. Simulate Network Chaos on Kubernetes. https://chaos-mesh.org/docs/2.5.2/simulate-network-chaos-on-kubernetes/
- AWS Fault Injection Simulator User Guide. Stop instances tutorial. https://docs.aws.amazon.com/fis/latest/userguide/fis-tutorial-stop-instances.html
- Nygard MT. Release It! Design and Deploy Production-Ready Software. 2nd ed. Pragmatic Bookshelf; 2018.
- Shopify. Toxiproxy GitHub Repository. https://github.com/Shopify/toxiproxy
- AWS Well-Architected Framework. Reliability Pillar. https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/welcome.html
- Publickey. JASST’23 Tokyo: 「カオスエンジニアリング」は…(講演記事). 2023. https://www.publickey1.jp/blog/23/jasst23_tokyo.html