この記事では、CrashLoopBackOff状態を再現する方法と、それをトリガーに待機していた別Podを自動でスケールアウト(replicas=1)する構成を、Python × Flask × Kubernetes環境で構築する手順をまとめます。
この仕組みは、障害発生時の自己復旧ロジックやフェイルオーバー実装のベースとしても活用できます。
🎯 目的
- CrashLoopBackOffをテスト用トリガーとして確実に発生させる
- Flaskアプリがクラッシュしたら、standby-app を自動で1レプリカ起動する
- 障害検知から自己修復までの自動監視&スケーリングを組み込む
🧱 構成イメージ
- crash-app(replicas:1、起動後30秒で強制終了→CrashLoopBackOff)
- standby-app(replicas:0で待機)
- crash-appのPodがCrashLoopBackOffになると、standby-appをreplicas=1で起動
本番環境を止めずに、予備Podへフェイルオーバーさせる簡易構成です。
1. CrashLoopBackOff発生用Flaskアプリ例
✅ app.py
import os
import threading
import time
from flask import Flask
CRASH_DELAY = int(os.getenv("CRASH_DELAY", "30"))
app = Flask(__name__)
@app.route("/")
def index():
return "Hello from crash-app!"
@app.route("/healthz")
def healthz():
return "OK", 200
def crash_after():
time.sleep(CRASH_DELAY)
os._exit(1) # 即時終了でCrashLoopBackOffを誘発
if __name__ == "__main__":
threading.Thread(target=crash_after, daemon=True).start()
app.run(host="0.0.0.0", port=5000)
ポイント:環境変数 CRASH_DELAY
秒後に os._exit(1)
でコンテナを異常終了させ、CrashLoopBackOff状態を確実に作ります。
2. crash-app Deployment設定
apiVersion: apps/v1
kind: Deployment
metadata:
name: crash-app
spec:
replicas: 1
selector:
matchLabels:
app: crash-app
template:
metadata:
labels:
app: crash-app
spec:
containers:
- name: crash-app
image: your-registry/crash-app:latest
imagePullPolicy: IfNotPresent
env:
- name: CRASH_DELAY
value: "30"
ports:
- containerPort: 5000
livenessProbe:
httpGet:
path: /healthz
port: 5000
initialDelaySeconds: 10
periodSeconds: 15
failureThreshold: 1
-
livenessProbe が
/healthz
に1回でも失敗すると再起動 → CrashLoopBackOffへ移行します。
3. standby-app Deployment設定
apiVersion: apps/v1
kind: Deployment
metadata:
name: standby-app
spec:
replicas: 0 # ← 初期は待機状態
selector:
matchLabels:
app: standby-app
template:
metadata:
labels:
app: standby-app
spec:
containers:
- name: standby-app
image: your-registry/crash-app:latest
imagePullPolicy: IfNotPresent
env:
- name: CRASH_DELAY
value: "120" # 長めに設定してCrashLoopBackOffになりにくく
ports:
- containerPort: 5000
readinessProbe:
httpGet:
path: /healthz
port: 5000
initialDelaySeconds: 5
periodSeconds: 10
replicas: 0
のまま待機し、必要に応じて監視スクリプトでスケールアウトします。
4. 監視&自動スケーリングロジック
✅ scale.py
from kubernetes import client, config, watch
config.load_incluster_config()
apps_v1 = client.AppsV1Api()
core_v1 = client.CoreV1Api()
def scale_deployment(name: str, replicas: int, namespace: str = "default"):
patch = {"spec": {"replicas": replicas}}
apps_v1.patch_namespaced_deployment_scale(name, namespace, patch)
print(f"{name} を {replicas} レプリカにスケールしました")
def monitor_crash_app():
w = watch.Watch()
for event in w.stream(core_v1.list_namespaced_pod,
namespace="default",
label_selector="app=crash-app"):
pod = event["object"]
for cs in pod.status.container_statuses or []:
if cs.state.waiting and cs.state.waiting.reason == "CrashLoopBackOff":
scale_deployment("standby-app", 1)
w.stop()
return
if __name__ == "__main__":
monitor_crash_app()
-
kubernetes-client の Watch 機能で
crash-app
PodのCrashLoopBackOffを検出 - 検出時に
standby-app
を1レプリカにスケール
5. crash-app に監視スクリプトを組み込む
crash-app
のPod内でSidecarとして動かす場合、以下のようにDeploymentへ追加します。
containers:
- name: crash-app
# …(省略)…
- name: crash-monitor
image: your-registry/crash-monitor:latest
command: ["python", "/opt/scale.py"]
volumeMounts:
- name: kube-config
mountPath: /root/.kube
volumes:
- name: kube-config
configMap:
name: kube-config # ServiceAccount不要でアクセス可能なら省略可
※上記は例です。別PodとしてデプロイしてもOKです。
6. RBAC設定
crash-monitor
にDeploymentスケール権限を与えるRoleとRoleBindingを作成します。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-scaler
namespace: default
rules:
- apiGroups: ["apps"]
resources: ["deployments/scale"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: bind-deployment-scaler
namespace: default
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: Role
name: deployment-scaler
apiGroup: rbac.authorization.k8s.io
🔁 実行フローまとめ
-
crash-app
起動 → 30秒後にos._exit(1)
で異常終了 - KubernetesがCrashLoopBackOff判定
-
crash-monitor
(scale.py) が検知 →standby-app
を1レプリカにスケール - standby-appが立ち上がり、あとはモニタリング継続
✅ 補足:CPU飽和によるCrashLoopBackOff例
# cpu_burn.py
data = []
while True:
data.extend([0] * 10**6) # CPUとメモリを使い切る
Deploymentに組み込んで resources.limits.cpu: "100m"
などを設定すると、CPU制限超過でOOMKilled → CrashLoopBackOff もテスト可能です。
✅ まとめ
方法 | 再現性 | 備考 |
---|---|---|
os._exit(1) + threading |
◎ | 最も直接的で失敗率ゼロ |
livenessProbe失敗 | ○ | Probe 設定次第 |
CPU/メモリ負荷(OOM) | △ | リソース制限が必要 |
CrashLoopBackOffはトリガーとして活用し、自動スケールやフェイルオーバー機能のテストと設計に役立てましょう。
これにより、本番環境での高可用性・自己修復構成をシンプルに実現できます。