はじめに
Spring Bootの次期リリースである2.3.0ではKubernetes関連の機能追加が行われているようです。
今回はSpring Boot 2.3.0 M3で追加された Graceful Shutdown 機能を試してみました。
2.3.0の正式リリース時には挙動が変わっている可能性もあるので参考程度にどうぞ。
Graceful Shutdown とは
停止要求が行われた時点で処理中のリクエストがあった場合でも、猶予期間(Grace Period)まで完了を待機することでアプリケーションを安全に停止する仕組みです。
たとえば、下記のような処理があったとして
public void update() {
updateSystemA();
updateSystemB();
updateSystemC();
}
Aの更新直後に処理が中断されてしまった場合、B・Cへの更新は行われず不整合が発生してしまいます。
Graceful Shutdownでは猶予時間の範囲内でB・Cへの更新も待って停止されるため、このような不整合を回避できます。
(ただし通常 Graceful Shutdown が働くのはあくまでも正常な停止要求が来た場合であり、Ctrl+CやOS自体の強制終了などでは処理続行できず普通に打ち切られます)
Spring Boot 2.3.0 で追加された Graceful Shutdown 機能
server.shutdown.grace-period
プロパティが追加されており、このプロパティで猶予期間を設定するだけです。
下記の例では秒(s)単位で指定していますが、単位を指定しなかった場合はミリ秒での指定になるようです。
※ 2.3.0 の正式リリースではプロパティ名が変わっていました。
・server.shutdown=graceful
・spring.lifecycle.timeout-per-shutdown-phase=30s
機能自体に変更はなさそうでした。
server.shutdown.grace-period=30s
たとえば、下記のような25秒後に"OK"を返すだけのAPIが Kubernetes 上で動いていたとします。
@RestController
public class TestController {
@GetMapping("/")
public ResponseEntity<String> doTest() throws Exception {
Thread.sleep(25_000L);
return new ResponseEntity<>("OK", HttpStatus.OK);
}
}
猶予期間を設定しなかった場合、リクエストの処理中に Pod の停止要求を行うと Spring Boot アプリケーションは実行途中で停止してしまい 502 Bad Gateway
や 504 Gateway Time-out
が返ってきました。
>curl http://192.168.99.100/
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>openresty/1.15.8.2</center>
</body>
</html>
一方、猶予期間を適切に設定していれば、リクエスト処理中に Pod の停止要求を行っても処理中のリクエストは中断されずに結果が返るまで待機してくれることが確認できました。
>curl http://192.168.99.100/
OK
猶予期間を設定した場合でも、実行中の処理がない状態であれば待機はせずに停止してくれるようでした。
おまけ
Spring Boot アプリケーションのプロパティは application.properties (application.yaml) ファイルで設定するのが一般的ですが 環境変数でも設定できます。(詳細は Spring Boot Features#Externalized Configuration 参照)
猶予期間は Kubernetes側でも調整が必要(terminationGracePeriodSeconds:デフォルト 30秒) になるため、下記のように設定を deployment.yaml にまとめておくとやりやすいかもしれません。
apiVersion: apps/v1
kind: Deployment
metadata:
name: shutdown
namespace: test
spec:
replicas: 2
selector:
matchLabels:
app: shutdown
template:
metadata:
labels:
app: shutdown
spec:
terminationGracePeriodSeconds: 60
containers:
- name: shutdown
image: satrbeta/shutdown:latest
env:
- name: SERVER_SHUTDOWN_GRACE_PERIOD
value: 60s
ports:
- containerPort: 8080
まとめ
Spring Boot 2.2.x まではこのような待機処理を自前で実装する必要があったのですが、Spring Boot 2.3.0ではほぼ自動でやってくれるようになり、だいぶ楽になっていました。
今回は組み込み tomcat 上での動作しか試していませんが、他の Webサーバでもだいたい同じ挙動になるのではないかと思います。
(2.3.0.M4時点のリファレンスによればUndertowのみリクエストを受け付けなくなるのではなく 503 Service Unavailable
を返す仕様とのこと)