Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kubernetesでアプリの安定稼働と高頻度のアップデートを両立するためのプラクティス / Best Practices for Applications on Kubernetes to Achieve Both Frequent Updates and Stability

Kubernetesでアプリの安定稼働と高頻度のアップデートを両立するためのプラクティス / Best Practices for Applications on Kubernetes to Achieve Both Frequent Updates and Stability

hhiroshell

April 04, 2024
Tweet

More Decks by hhiroshell

Other Decks in Technology

Transcript

  1. Cloud Native Developers JP Cloud Native Developers JP 【PR】自作キーボードはいいぞ •

    自キ専門店「遊舎工房」へGo! 2 ←遊舎工房さんの店舗はこちら ↓@hhiroshellのキーボード Timothy 自己紹介 @hhiroshell 早川 博 (はやかわ ひろし) • Cloud Nativeなプラットフォー ムを開発するエンジニア
  2. 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 \ 参加申込はこちらから /
  3. Cloud Native Developers JP Cloud Native Developers JP 近年のアプリケーションに求められるもの •

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

    頻繁な再起動と安定性の両立に加え… – サービスレベルの維持 – 大規模運用 – 8時間睡眠 / 日 6
  5. Cloud Native Developers JP Cloud Native Developers JP 具体的には何をどうすれば…? •

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

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

    「PodがReadyである」とはそのPodにトラフィックが流入してくるこ とを意味する • Pod内のコンテナは、「Ready」になる前にリクエストを受け付ける ための準備を済ませておく必要がある 10
  8. Cloud Native Developers JP Cloud Native Developers JP 具体的には何をすれば… •

    アプリケーションによりけり • 例: – データベースとのコネクションを作成しておく – 初期ヒープメモリを十分に確保しておく – キャッシュを読み込んでおく – 暖機運転を行ってJITコンパイラによる最適化を進めておく 11
  9. 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)
  10. 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」になり、 トラフィックが流入する ここで十分な準備を行っておく
  11. Cloud Native Developers JP Cloud Native Developers JP Readyになる前の準備に利用できる手段 •

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

    Pod(の中のコンテナ)がクラッシュする理由も様々 • コンテナが安全に終了しないとリクエストがエラーになる • とくに高負荷を引き金にコンテナの停止が起こると、残りのPod群に その高負荷がかかり、連鎖的にPodが停止してしまうこともある 17
  14. 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外に依存している場合
  15. 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 負荷試験とチューニングを十分に 行いましょう
  16. 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の実使用量
  17. Cloud Native Developers JP Cloud Native Developers JP Kubernetesのリソース制御 -

    2/2 • コンテナのリソース消費が「limits」に設定された値を超えようとし たとき – CPU: スロットリング(CPU使用量が抑えられた状態で動作を継続) – Memory: OOM Kill(コンテナの強制停止) • OOM Killでコンテナが強制終了されるとGracefulなシャットダウンが 行えない 21
  18. 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
  19. 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
  20. 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の特性を知って適切な 設定を行いましょう
  21. 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
  22. 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)
  23. Cloud Native Developers JP Cloud Native Developers JP GCとメモリ設定におけるプラクティス •

    リソース割り当て量の変更は慎重に – GCアルゴリズムが変わる、Full GCの所要時間が伸びるなど、いろいろな挙動 の変化を引き起こす場合がある • G1GCなどコンカレント型のGCの利用を検討する – Full GCの所要時間が問題になる場合は、停止時間が短いアルゴリズムを使用す ると改善する • failureThresholdの引き上げを検討する – failureThreshold = livenessProbeの連続失敗の許容回数。これを引き上げること でlivenessProbeの失敗によるコンテナの再起動を抑制できる 27
  24. 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を設定しよう
  25. 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
  26. 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
  27. 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内にだけ 依存するようにしましょう
  28. Cloud Native Developers JP Cloud Native Developers JP livenessProbeで注意すべきこと その2

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

    Podの終了時には、主に2つのことが起きる – Podの中のコンテナが終了する – Podへのトラフィックが停止する • これら2つの動作には決まった順序がない – トラフィックが停止する前にコンテナが終了してしまうと、そのPodに届いた リクエストがエラーになる 36
  30. 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にリクエストが届かなくなる
  31. 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にリクエストが届かなくなる
  32. Cloud Native Developers JP Cloud Native Developers JP PodのGracefulシャットダウンのために •

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

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