Kubernetes サービスのグレースフルシャットダウンの必要性
ある本番環境でのデプロイ中に、支払い処理中の注文データが永続化されない事象が発生した。この問題は、外部決済システムへのリクエスト後に注文レコードを保存する直前に Pod が強制終了されたことに起因する。システムは 24 時間稼働しており、メンテナンスウィンドウが存在しないため、ゼロダウンタイムでのローリングアップデートが必須となった。
検証環境構成
検証は以下の環境で実施された:
- Kubernetes クラスタ(Master ×1、Worker ×3)
- アプリケーション:支払いサービス(replicas=2)
- サービス公開ポート:NodePort 31553
- 負荷テストツール:Apache JMeter
Deployment 設定によるグレースフルシャットダウン実装
以下の Deployment 定義では、preStop フックを用いて、Pod 終了前にサービスディスカバリ(Nacos)へのステータス通知と十分な処理時間の確保を実現している。
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
labels:
app: payment
spec:
replicas: 2
selector:
matchLabels:
app: payment
template:
metadata:
labels:
app: payment
spec:
terminationGracePeriodSeconds: 120
containers:
- name: payment-app
image: payment:v2.1-beta.76
ports:
- containerPort: 8011
env:
- name: TZ
value: Asia/Shanghai
- name: REGISTER_HOST
value: nacos
# ... その他の環境変数(省略)
volumeMounts:
- name: localtime
mountPath: /etc/localtime
readOnly: true
- name: app-jar
mountPath: /app/app.jar
readinessProbe:
httpGet:
path: /actuator/health
port: 8011
initialDelaySeconds: 30
periodSeconds: 10
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
wget -qO- --post-data '' \
'http://127.0.0.1:8011/actuator/service-registry?status=DOWN' \
--header 'Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8' \
&& sleep 120
volumes:
- name: localtime
hostPath:
path: /etc/localtime
- name: app-jar
hostPath:
path: /app/backend/pay/app.jar
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
JMeter による負荷テストと結果分析
ローリングアップデート中、JMeter は継続的にリクエストを送信し、HTTP ステータスコードと応答時間を記録した。テスト結果の要点は以下の通り:
- 新規 Pod が
readinessProbeを通過するまで、トラフィックは旧 Pod のみにルーティングされる。 - 旧 Pod は
preStopフックによりサービスレジストリから登録解除され、新規リクエストを受け付けなくなる。 - その後、120 秒間(
terminationGracePeriodSeconds)をかけて既存リクエストを処理し、安全にシャットダウンされる。
テストログには 4 件の「失敗」として記録されたリクエストが存在したが、HTTP ステータスコードはすべて 200 OK であり、アプリケーションレベルでのエラーは確認されなかった。ログの一部を以下に示す:
1599805677891,15,HTTP Request,200,OK,Thread Group 1-7,...
1599805677902,4,HTTP Request,20HTTP Request,200,O0,OK,Thread Group 1-1,... ← ログ破損の可能性
1599805677902,5,HTTP Request,200,OK,Thread Group 1-10,...
2 行目のログには 20HTTP Request,200,O0 という不正な形式が含まれており、これは JMeter 自体のスレッド競合やログ書き込み時のバッファリング問題によるものと推測される。実際のサービス応答は正常であり、グレースフルシャットダウンは意図通り機能していた。