Slide 1

Slide 1 text

Cloud Native Developers JP Kubernetesでアプリの安定稼働と高頻度の アップデートを両立するためのプラクティス 早川 博 | @hhiroshell LINEヤフー株式会社 SIグループ クラウド統括本部

Slide 2

Slide 2 text

Cloud Native Developers JP Cloud Native Developers JP 【PR】自作キーボードはいいぞ • 自キ専門店「遊舎工房」へGo! 2 ←遊舎工房さんの店舗はこちら ↓@hhiroshellのキーボード Timothy 自己紹介 @hhiroshell 早川 博 (はやかわ ひろし) • Cloud Nativeなプラットフォー ムを開発するエンジニア

Slide 3

Slide 3 text

Cloud Native Developers JP Cloud Native Developers JP 【PR】Cloud Native Community Japan主催イベントのお知らせ • KubeDayやKubeConのCfP採択の秘訣とは…!? • 4/23 Thu. 18:30〜21:00 @紀尾井町LODGE 3 \ 参加申込はこちらから /

Slide 4

Slide 4 text

Cloud Native Developers JP 近年のアプリケーションに 求められるもの

Slide 5

Slide 5 text

Cloud Native Developers JP Cloud Native Developers JP 近年のアプリケーションに求められるもの • アプリケーションは様々な理由で再起動される – 頻繁なアップデート – 自動/手動でのスケーリング – アプリケーションの実行基盤ラのメンテナンス • アプリケーションはいつでも再起動されうるものと捉える必要があ る • 頻繁な再起動があっても安定して稼働し続けるには…? 5

Slide 6

Slide 6 text

Cloud Native Developers JP Cloud Native Developers JP さらなる挑戦 • 頻繁な再起動と安定性の両立に加え… – サービスレベルの維持 – 大規模運用 – 8時間睡眠 / 日 6

Slide 7

Slide 7 text

Cloud Native Developers JP Cloud Native Developers JP 具体的には何をどうすれば…? • KubernetesのPod群には様々なイベントが起きる 7 最初のデプロイ v1 Pods v2 Pods v3 Pods ローリングアップデート 意図しない理由での再起動 負荷の上昇に伴うスケールアウト

Slide 8

Slide 8 text

Cloud Native Developers JP Cloud Native Developers JP 具体的には何をどうすれば…? • 3つの観点に分けて考えると整理しやすい 8 v1 Pods v2 Pods v3 Pods 十分な準備をして安全に Podを起動しよう GracefulにPodを終了しよう 意図しないクラッシュを予防しよう

Slide 9

Slide 9 text

Cloud Native Developers JP Podの起動におけるプラクティス 十分な準備をして安全にPodを起動しよう

Slide 10

Slide 10 text

Cloud Native Developers JP Cloud Native Developers JP 十分な準備をして安全にPodを起動しよう • 「PodがReadyである」とはそのPodにトラフィックが流入してくるこ とを意味する • Pod内のコンテナは、「Ready」になる前にリクエストを受け付ける ための準備を済ませておく必要がある 10

Slide 11

Slide 11 text

Cloud Native Developers JP Cloud Native Developers JP 具体的には何をすれば… • アプリケーションによりけり • 例: – データベースとのコネクションを作成しておく – 初期ヒープメモリを十分に確保しておく – キャッシュを読み込んでおく – 暖機運転を行ってJITコンパイラによる最適化を進めておく 11

Slide 12

Slide 12 text

Cloud Native Developers JP 12 Podの起動の流れ time InitContainers run sequentially Containers starts and runs ENTRYPOINT command startup probe readiness probe liveness probe … … … It also runs postStart lifecycle hook • On control plane nodes • A new pod is persisted to the API Server • The scheduler decides which Node to place the pod • On worker nodes • The kubelet on the Node starts to make an environment for containers in the pod Pre-start process Containers start up (Our Concern)

Slide 13

Slide 13 text

Cloud Native Developers JP 13 Podの起動の流れ time InitContainers run sequentially Containers starts and runs ENTRYPOINT command startup probe readiness probe liveness probe … … … It also runs postStart lifecycle hook • On control plane nodes • A new pod is persisted to the API Server • The scheduler decides which Node to place the pod • On worker nodes • The kubelet on the Node starts to make an environment for containers in the pod Pre-start process Containers start up (Our Concern) サービスイン Podが「Ready」になり、 トラフィックが流入する ここで十分な準備を行っておく

Slide 14

Slide 14 text

Cloud Native Developers JP Cloud Native Developers JP Readyになる前の準備に利用できる手段 • initCotainers – メインのコンテナとは別のコンテナを実行する – メインコンテナが動き出す前の準備に利用する(例: メインコンテナ用の設定 ファイルを動的に作成する) • postStart lifecycle hook – メインコンテナ内で任意のコマンドを実行できる – この処理が終了するまで、各種probeは実行されない – アプリケーションの本体に対して準備処理を行いたい場合に利用する(例: Javaアプリの暖機リクエストを送る) 14

Slide 15

Slide 15 text

Cloud Native Developers JP 15 例 – postStart lifecycle hookによる暖機運転 apiVersion: v1 kind: Pod metadata: name: java-app spec: containers: - name: java-app image: java-app-image:v1.0.0 ...(snip)... ports: - containerPort: 8080 name: http readinessProbe: failureThreshold: 3 httpGet: path: /health/readiness port: http periodSeconds: 1 lifecycle: postStart: exec: command: - sh - -c - |- sleep 10 for i in $(seq 10000); do curl -s http://localhost:8080/ ∖ > /dev/null done preStop: ...(snip)... ✓Wait for the java-app to start. ✓Send requests to the app in the same pod.

Slide 16

Slide 16 text

Cloud Native Developers JP Podの稼働中におけるプラクティス 意図しないクラッシュを予防しよう

Slide 17

Slide 17 text

Cloud Native Developers JP Cloud Native Developers JP 意図しないクラッシュを予防しよう • Pod(の中のコンテナ)がクラッシュする理由も様々 • コンテナが安全に終了しないとリクエストがエラーになる • とくに高負荷を引き金にコンテナの停止が起こると、残りのPod群に その高負荷がかかり、連鎖的にPodが停止してしまうこともある 17

Slide 18

Slide 18 text

Cloud Native Developers JP 18 Podがクラッシュするシナリオ High Traffic High Memory Usage High CPU Usage Bottlenecks outside the pod Full GC Can‘t respond to liveness probe The kubelet terminates the pod OOM Kill * livenessProbeの成否がPod外に依存している場合

Slide 19

Slide 19 text

Cloud Native Developers JP * livenessProbeの成否がPod外に依存している場合 19 Podがクラッシュするシナリオ Bottlenecks outside the pod Full GC High Traffic High Memory Usage High CPU Usage Can‘t respond to liveness probe The kubelet terminates the pod OOM Kill 負荷試験とチューニングを十分に 行いましょう

Slide 20

Slide 20 text

Cloud Native Developers JP Cloud Native Developers JP Kubernetesのリソース制御 - 1/2 • コンテナ毎に「requests」と「limits」を設定できる – .spec.containers[].resources.requests.[cpu|memory]: • コンテナが利用できることが保証されるリソース量 – .spec.containers[].resources.limits.[cpu|memory]: • requestsを超えてリソースを利用した場合の上限リソース量 20 resources.limits resources.requests コンテナのCPU / Memoryの実使用量

Slide 21

Slide 21 text

Cloud Native Developers JP Cloud Native Developers JP Kubernetesのリソース制御 - 2/2 • コンテナのリソース消費が「limits」に設定された値を超えようとし たとき – CPU: スロットリング(CPU使用量が抑えられた状態で動作を継続) – Memory: OOM Kill(コンテナの強制停止) • OOM Killでコンテナが強制終了されるとGracefulなシャットダウンが 行えない 21

Slide 22

Slide 22 text

Cloud Native Developers JP Cloud Native Developers JP Kubernetesのリソース制御とJava VMのメモリ • Java VMにはヒープメモリ、スレッドスタックなど複数のメモリ領域 がある • メモリパラメータを調整して「limits」に収まるようにする 22 resource limit Javaアプリケーションのメモリ消費量 = Heap + Thread Stack + Code Cache + Metaspace + Direct Memory + Native Memory resource request Heap other areas

Slide 23

Slide 23 text

Cloud Native Developers JP Cloud Native Developers JP Java VMのメモリ設定 • デフォルトに任せるとヒープメモリの最大値が「limits」の約25%と なる。これだと多くの場合「requests」や「Limits」に設定したメモ リを使いきれない → ある程度引き上げるとよい ヒープメモリの最大値 23 • 「-XX:MaxRAMPercentage=50.0」のように設定すると「Limits」に対す る割合で指定できる resources.limits resources.requests Heap other areas

Slide 24

Slide 24 text

Cloud Native Developers JP * livenessProbeの成否がPod外に依存している場合 24 Podがクラッシュするシナリオ High CPU Usage Bottlenecks outside the pod OOM Kill High Traffic High Memory Usage Full GC Can‘t respond to liveness probe The kubelet terminates the pod GCの特性を知って適切な 設定を行いましょう

Slide 25

Slide 25 text

Cloud Native Developers JP Cloud Native Developers JP コンテナ化したJavaアプリにおけるGC • JVMは「Limits」に指定したCPU、メモリ量に応じて自動的にGCアル ゴリズムを変更する – デフォルトでは、CPUが2コア以上、メモリが1.9GBを超えるとG1GCが選択さ れ、それ以外の場合はSerial GCとなる • アルゴリズムが同じでもFull GCの所要時間が変わる場合がある – ヒープメモリの容量が多いほど、CPU割り当て量が少ないほど時間がかかる 25

Slide 26

Slide 26 text

Cloud Native Developers JP Cloud Native Developers JP Full GCとlivenessProbe • Full GCの間、アプリケーションはリクエストに応答できないため、 多くの場合livenessProbeが失敗する • livenessProbeが指定した回数連続して失敗すると、コンテナが再起動 される 26 Java app container http livenessProbe request fails TERMINATE kubelet Stop the world (Full GC)

Slide 27

Slide 27 text

Cloud Native Developers JP Cloud Native Developers JP GCとメモリ設定におけるプラクティス • リソース割り当て量の変更は慎重に – GCアルゴリズムが変わる、Full GCの所要時間が伸びるなど、いろいろな挙動 の変化を引き起こす場合がある • G1GCなどコンカレント型のGCの利用を検討する – Full GCの所要時間が問題になる場合は、停止時間が短いアルゴリズムを使用す ると改善する • failureThresholdの引き上げを検討する – failureThreshold = livenessProbeの連続失敗の許容回数。これを引き上げること でlivenessProbeの失敗によるコンテナの再起動を抑制できる 27

Slide 28

Slide 28 text

Cloud Native Developers JP * livenessProbeの成否がPod外に依存している場合 28 Podがクラッシュするシナリオ Bottlenecks outside the pod OOM Kill High Traffic High Memory Usage High CPU Usage Full GC Can‘t respond to liveness probe The kubelet terminates the pod 不安定にならないように livenessProbeを設定しよう

Slide 29

Slide 29 text

Cloud Native Developers JP Cloud Native Developers JP livenessProbe • 「コンテナは動作しているが再起動が必要な状況」とは… 29 The kubelet uses liveness probes to know when to restart a container. For example, liveness probes could catch a deadlock, where an application is running, but unable to make progress. “ “ -- Kubernetes documentation

Slide 30

Slide 30 text

Cloud Native Developers JP Cloud Native Developers JP livenessProbeで注意すべきこと • 高負荷時によってlivenessProbeに応答できなくなると、トラフィック が多い状況にも関わらずコンテナが再起動されてしまう • デフォルトならプロセスが生きていれば成功となるので、この方が 良い場合も 30 Java app container http livenessProbe request fails too many requests (and some of them fails) TERMINATE kubelet users

Slide 31

Slide 31 text

Cloud Native Developers JP 31 Podがクラッシュするシナリオ High Memory Usage High CPU Usage Full GC OOM Kill High Traffic Bottlenecks outside the pod Can‘t respond to liveness probe The kubelet terminates the pod * livenessProbeの成否がPod外に依存している場合 livenessProbeの成否がPod内にだけ 依存するようにしましょう

Slide 32

Slide 32 text

Cloud Native Developers JP Cloud Native Developers JP livenessProbeで注意すべきこと その2 • livenessProbeの成否がPodの外に依存しないようにする – これをすると、Podの外で起きた事象がPodの再起動を引き起こす作りになる – 多くの場合Podを再起動しても状況が改善するわけではない 32

Slide 33

Slide 33 text

Cloud Native Developers JP 33 様々なPodの停止シナリオ

Slide 34

Slide 34 text

Cloud Native Developers JP 34 様々なPodの停止シナリオ

Slide 35

Slide 35 text

Cloud Native Developers JP Podの終了におけるプラクティス GracefulにPodを終了しよう

Slide 36

Slide 36 text

Cloud Native Developers JP Cloud Native Developers JP PodをGracefulに終了しよう • Podの終了時には、主に2つのことが起きる – Podの中のコンテナが終了する – Podへのトラフィックが停止する • これら2つの動作には決まった順序がない – トラフィックが停止する前にコンテナが終了してしまうと、そのPodに届いた リクエストがエラーになる 36

Slide 37

Slide 37 text

Cloud Native Developers JP 37 Podの終了の流れ preStop lifecycle hook starts kube-proxy stops traffic to the pod deletionGracePeriodSeconds (default: 30s) kubelet sends SIGKILL to the container Some kube-apiserver client adds a deletionTimestamp annotation to the pod kubelet sends SIGTERM to the container サービスアウト この時点からPodにリクエストが届かなくなる

Slide 38

Slide 38 text

Cloud Native Developers JP 38 Podの終了の流れ preStop lifecycle hook starts kube-proxy stops traffic to the pod deletionGracePeriodSeconds (default: 30s) kubelet sends SIGKILL to the container Some kube-apiserver client adds a deletionTimestamp annotation to the pod kubelet sends SIGTERM to the container アプリでSIGTERMをハンドリング してGracefulに終了する ここでトラフィックの停止を待つ サービスアウト この時点からPodにリクエストが届かなくなる

Slide 39

Slide 39 text

Cloud Native Developers JP Cloud Native Developers JP PodのGracefulシャットダウンのために • トラフィックの停止を待つ – preStop lifecycle hookが終了すると、SIGTERMシグナルがコンテナに送られる – preStop lifecycle hookの中で ”sleep xx” などのコマンドを実行してトラ フィックの停止まで待機する • アプリケーションをGracefulに終了する – SIGTERMシグナルをハンドリングして、Gracefulシャットダウンを行う – 多くのプログラミング言語のSDKやフレームワークがサポートしているので、 それを利用する 39

Slide 40

Slide 40 text

Cloud Native Developers JP 40 例 – preStop lifecycle hookによる待機 apiVersion: v1 kind: Pod metadata: name: java-app spec: containers: - name: java-app image: java-app-image:v1.0.0 ...(snip)... ports: - containerPort: 8080 name: http readinessProbe: failureThreshold: 3 httpGet: path: /health/readiness port: http periodSeconds: 1 lifecycle: postStart: ...(snip)... preStop: exec: command: - sh - -c - sleep 10 ✓Wait for kube-proxy to stop requests

Slide 41

Slide 41 text

Cloud Native Developers JP Cloud Native Developers JP 例 - Spring BootでのGracefulシャットダウン • Spring Bootの設定ファイルにプロパティを追加することで、Graceful シャットダウンを有効化できる 41 server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=15s

Slide 42

Slide 42 text

Cloud Native Developers JP Cloud Native Developers JP まとめ • 3つの観点から、Podを安定的に動作させる方法をお伝えしました 42 v1 Pods v2 Pods v3 Pods 十分な準備をして安全に Podを起動しよう GracefulにPodを終了しよう 意図しないクラッシュを予防しよう

Slide 43

Slide 43 text

Cloud Native Developers JP Cloud Native Developers JP Thank you • I hope you sleep well 43