Chaos MeshによるKubernetesのカオスエンジニアリング:実践的な障害注入ガイド

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

Quick start (5 min): Getting Chaos Mesh Up and Running

本番環境の分散システムは、複雑かつ予測不可能な形で障害を起こします。クラウドノードが警告なしに突然停止したり、サードパーティの決済ゲートウェイで突然450msのレイテンシスパイクが発生したり、高負荷時に認証サービスがメモリ不足(OOM)に陥ったりします。ダウンストリームコンポーネントに適切なタイムアウトや障害の隔離(アイソレーション)が設定されていない場合、こうした局所的な問題が連鎖し、プラットフォーム全体の完全な停止へと発展します。

カオスエンジニアリング(Chaos Engineering)はこのアプローチを根本から変えます。深夜3時のインシデントによって弱点が露呈するのを待つのではなく、業務時間内に制御された障害を意図的に注入します。Chaos Meshは、Kubernetesネイティブなカスタムリソース定義(CRD)ときめ細かなカーネルレベルの注入メカニズムを利用して、この実験をシンプルに実現します。

Step 1: Install Chaos Mesh with Helm

Helm 3を使用して、既存のKubernetesクラスタ(v1.19以上)にChaos Meshをインストールします。まずは公式チャートリポジトリを追加し、ローカルのインデックスを更新します:

helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update

次に、専用のchaos-meshネームスペースにChaos Meshをインストールし、Webダッシュボードを有効化します。containerdを実行しているクラスタ(標準的なEKS、GKE、k3sなど)では、ランタイムソケットのパスを指定します:

kubectl create namespace chaos-mesh

helm install chaos-mesh chaos-mesh/chaos-mesh \
  --namespace=chaos-mesh \
  --set dashboard.create=true \
  --set chaosDaemon.runtime=containerd \
  --set chaosDaemon.socketPath=/run/containerd/containerd.sock

すべてのコアコンポーネント(コントローラーマネージャー、Chaos DaemonのDaemonSet、ダッシュボード)がRunning状態に遷移したことを確認します:

kubectl get pods -n chaos-mesh -o wide

Step 2: Deploy a Demo Target Application

障害モードを安全に検証するために、分離されたサンドボックス用ネームスペースにレプリカ数3の軽量なNginxワークロードをデプロイします:

kubectl create namespace app-test

kubectl create deployment web-target \
  --image=nginx:alpine \
  --replicas=3 \
  --namespace=app-test

Step 3: Run Your First Pod Termination Experiment

サービスのフェイルオーバー体制をテストします。以下のマニフェストは、app: web-targetラベルに一致するPodをランダムに1つ強制終了(kill)します:

apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: pod-kill-demo
  namespace: app-test
spec:
  action: pod-kill
  mode: one
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target

マニフェストを適用し、Kubernetesが即座に代替のレプリカをプロビジョニングする様子を監視します:

kubectl apply -f pod-kill-demo.yaml

# Podのライフサイクルイベントとリカバリを監視
kubectl get pods -n app-test -w

Deep dive: Simulating Real-World Failure Scenarios

1. The Cascading Timeout Problem (Network Latency)

Problem: マイクロサービスチェーンにおいて、チェックアウトAPIが在庫サービス(Inventory Service)に対して同期呼び出しを行っています。データベースの激しいロック競合により、在庫サービスのレスポンス時間が15msから2,400msに跳ね上がったとします。チェックアウトAPIに適切なクライアントタイムアウトとサーキットブレーカーが設定されていない場合、スレッドプールは数秒で枯渇し、フロントエンド全体が応答不能になります。

Root Cause: アップストリームサービスが低遅延なLAN環境を前提として設計されている点です。サーキットブレーカーの省略、フォールバックレスポンスの未設定、無制限のHTTPコネクションプールの設定などが原因となります。

Practical Solution: NetworkChaosを使用して対象サービスに意図的なレイテンシを注入します。これにより、アップストリーム側の呼び出し元がサーキットブレーカーを作動させるか、ユーザーリクエストを落とさずに適切にグレースフルデグラデーション(縮退運転)できるかをテストします。

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: network-delay-experiment
  namespace: app-test
spec:
  action: delay
  mode: all
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target
  delay:
    latency: '800ms'
    jitter: '150ms'
    correlation: '25'
  duration: '5m'

この実行中はGrafanaダッシュボードを監視してください。無関係なエンドポイントを含めてアップストリームのp99レイテンシが全体的に上昇している場合、クライアントのタイムアウト設定をチューニングする必要があります。

2. The Intermittent Packet Loss Problem

Problem: アベイラビリティゾーン(AZ)間のネットワーク混雑やCNIオーバーレイ(CiliumやCalicoなど)の高負荷により、断続的に10〜15%のパケットロスが発生します。ユーザー側では散発的に502 Bad Gatewayや504 Gateway Timeoutエラーが発生するものの、Kubernetesの標準的なLiveness Probe(生存確認)は正常(グリーン)のまま維持されてしまいます。

Root Cause: 定期的なLiveness Probeは10秒ごとに単一のTCPハンドシェイクを実行するだけであるため、アクティブで高スループットなgRPCやHTTP/2の多重化ストリーム全体で発生するランダムなパケットロスを検知できないことがほとんどです。

Practical Solution: パケットロスプロファイルを指定したNetworkChaosを特定の2つのレプリカに適用し、アプリケーションレベルのリトライ処理が正しく機能するか検証します:

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: packet-loss-experiment
  namespace: app-test
spec:
  action: loss
  mode: fixed
  value: '2'
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target
  loss:
    loss: '15'
    correlation: '50'
  duration: '3m'

3. Memory Pressure and OOM Evictions (Resource Stress)

Problem: クエリ負荷の急増によってメモリ使用量がコンテナの上限を超過し、LinuxカーネルのOOM-killerがコンテナを即座に強制終了します。複数のレプリカが同時にこのしきい値に達すると、Horizontal Pod Autoscaler (HPA) が新しいPodをスケジュールする前にサービス全体がダウンしてしまいます。

Root Cause: 厳格すぎるコンテナのcgroupメモリ制限に加え、HPAのスケーリングメトリクスの遅延(例:60秒の評価ウィンドウ)や、メモリ消費の大きいJVM/Node.jsランタイムの特性が組み合わさっていることです。

Practical Solution: StressChaosを適用し、2つのワーカースレッドでメモリ消費量を512MBまで引き上げます。これにより、Podが強制終了される前にHPAがスケールアウトできるかどうかを確認します:

apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
  name: memory-stress-test
  namespace: app-test
spec:
  mode: one
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target
  stressors:
    memory:
      workers: 2
      size: '512Mi'
  duration: '2m'

Advanced usage: Orchestrating Chaos Workflows and CI/CD Automation

Automated Experiment Pipelines with Workflow CRD

単発のカオステストはデバッグに有用ですが、継続的なレジリエンストレーニングには構造化された実行手順が必要です。Chaos MeshのWorkflow CRDを使用すると、リカバリ用の待機時間を挟みながら、複雑な障害シーケンスを直列・並列で連結して実行できます。

apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
  name: microservice-disaster-drill
  namespace: app-test
spec:
  entry: main-pipeline
  templates:
    - name: main-pipeline
      templateType: Serial
      children:
        - network-delay-step
        - recovery-pause
        - pod-kill-step
    - name: network-delay-step
      templateType: NetworkChaos
      deadline: 2m
      networkChaos:
        action: delay
        mode: all
        selector:
          namespaces:
            - app-test
          labelSelectors:
            app: web-target
        delay:
          latency: '500ms'
    - name: recovery-pause
      templateType: Suspend
      deadline: 1m
    - name: pod-kill-step
      templateType: PodChaos
      deadline: 1m
      podChaos:
        action: pod-kill
        mode: one
        selector:
          namespaces:
            - app-test
          labelSelectors:
            app: web-target

Integrating Chaos Experiments into CI/CD Pipelines

プルリクエスト(PR)をマージする前にレジリエンスを検証します。GitHub Actions、GitLab CI、Argo Workflowsなどからステージング環境のカオスワークフローを直接トリガーし、負荷テストと並行して実行できます:

# 1. 自動統合テストの実行中にカオスワークフローをトリガー
kubectl apply -f workflows/microservice-disaster-drill.yaml

# 2. 障害発生中のサービスに対して負荷テストを実行
k6 run --vus 50 --duration 90s loadtests/checkout-flow.js

# 3. ワークフローが正常に完了したかを確認
kubectl get workflow microservice-disaster-drill -n app-test \
  -o jsonpath='{.status.conditions[?(@.type=="Accomplished")].status}'

# 4. ワークフローリソースをクリーンアップ
kubectl delete -f workflows/microservice-disaster-drill.yaml

Practical tips: Running Chaos Tests Safely

  • Define steady-state metrics upfront: クラスタを操作する前に、明確なサービスレベル指標(SLI)を設定します。例えば、「500msの遅延を注入している間も、エラー率は0.5%未満、p95レイテンシは750ms以下を維持する」といった基準です。オブザーバビリティ環境でこれをリアルタイムに測定できない場合は、実験を中止してテレメトリの整備を優先してください。
  • Isolate chaos targets with RBAC: クラスタ全体への障害注入権限を安易に付与してはいけません。Kubernetesのロールベースアクセス制御(RBAC)と明示的なnamespaceSelectorsを活用し、Chaos Meshコントローラーの対象を特定のステージングやテスト用ネームスペースに限定します。
  • Prepare an emergency abort command: 実験を開始する前に、必ずロールバック手順を確認しておきます。カオスリソースを削除すると、基盤となるカーネルのiptablesルールやトラフィック制御(TC)qdiscが即座にフラッシュされます:
# 実行中のすべてのNetworkChaos実験を即座に終了
kubectl delete networkchaos --all -n app-test
  • Test during office hours with team coverage: ステージング環境やカナリア本番環境で実験を行う際は、オンコール担当エンジニアやドメインエキスパートがテレメトリを監視できる体制が整っている日中に実施してください。
Share: