案件紹介・ 登録はこちら

Datadogトレーサを追加したら永遠にLiveness Probeが成功しなくなった話。

『Datadogトレーサを追加したら永遠にLiveness Probeが成功しなくなった話。』のサムネイル

はじめまして。
Javaプログラマ兼K8s初心者の植木と申します。
 
最近業務でオンプレJavaアプリケーションをKubernetes(K8s)に移行する作業をしており、その中で直面した問題について1つご紹介したいと思います。
具体的には、DatadogにAPMを表示させるためにトレーサを追加したら、それまで成功していたLiveness Probeが成功しなくなったという悲しい問題です。
 
DatadogにAPMを表示させる方法、Liveness Probeの仕組み、JavaアプリケーションをK8sで動かすために注意するポイント等にも触れながらこの問題の原因と解決策を紹介したいと思います。

環境

最初に私たちの環境を記載しておきます。

  • 言語:Java 1.8.0_312
  • フレームワーク:SpringBoot 1.5.22 RELEASE
  • インフラ:Amazon EKS(AWSでKubernetesを実行するマネジメントサービス)
  • モニタリング:Datadog

DatadogでAPMを表示する方法

本題に入る前に、APMとはどのようなもので、DatadogにAPMを表示させるために何が必要かを記載しておきます。
APMとはApplication Performance Managementの略称で、CPU、メモリ、レイテンシ、Javaランタイムメトリクス(ヒープやGC等)などをモニタリングする機能であり、Datadogにもこの機能があります。

K8s上にデプロイしたJavaアプリケーションのAPMをDatadogで表示するためには、以下の3つが必要です。

Datadog Agent

方法は色々ありますが、私たちはDaemonSetを利用して、すべてのノードにDatadog Agentをデプロイしました

トレーサ

 Java起動時、-javaagentオプションにdd-java-agent.jarを指定します

インスツルメンテーション(スパン)

 Spring Boot等のDatadogがサポートしているフレームワークであれば自動インスツルメンテーションされるので意識する必要はありません

 Datadogがサポートしているフレームワークは以下を参照して下さい
https://docs.datadoghq.com/ja/tracing/compatibility_requirements/java/

Datadogがサポートしていない場合はカスタムインスツルメンテーションが必要です

カスタムインスツルメンテーションの方法は以下参照して下さい
https://docs.datadoghq.com/ja/tracing/custom_instrumentation/java/

余談ですが、WebアプリケーションはSpring Bootを使っていたので自動インスツルメンテーションされたのですが、バッチはK8sのCronJobでスケジューリングするように実装したのでSpringの管理下になく、カスタムインスツルメンテーションする必要がありました

トレーサを追加したらLiveness Probeが成功しなくなった話

事象

私たちはまず、トレーサを追加しない状態でk8s上でアプリケーションを起動しました。(Datadog Agentも導入済み)
この時はReadiness Probe、Liveness Probeともに問題なく成功しました。
なお、詳しくは後述しますがReadiness ProbeとLiveness ProbeはK8sの機能の一つで、アプリケーションの起動監視、死活監視のようなものです。
 
その後、トレーサを追加して、APMを表示出来るようにしたのですが、なぜかLiveness Probeが失敗し続けるようになりました。(Liveness Probeは一定時間毎に定期的に実行されます)
なお、Readiness ProbeはアプリケーションログやAPMから成功を確認出来ていました。

原因(設定内容の説明)

Liveness Probeが失敗するとアプリケーションを再起動させる(正確にはコンテナを再起動させる)ので、Liveness Probeが実行される度に、フェニックスの如く消滅と復活を繰り返していたわけですが、その原因はK8sのマニフェストファイルの設定内容にありました。
 
この時のK8sのマニフェストファイルを一部抜粋します。

Readiness Probeの設定

Liveness Probeの設定

Resourcesの設定

TerminationGracePeriodSecondsの設定

さていかがでしょうか。
上記の設定のどこかに原因があるのですが、分かりますでしょうか?
 
まどろっこしくて申し訳ございませんが、答え合わせの前に上記設定内容の説明をさせて下さい。
 
Readiness Probeはアプリケーションの準備が整っているかどうかを確認します。
今回の場合、/member/v4/heartbeatにリクエストして200が返ってくれば準備OKと認識します。(※1)
逆に200が返ってこなかった場合は、1秒おき(periodSecondsで定義された秒数)にチェックを繰り返します。

Liveness Probeはアプリケーションが生きているかどうかを確認します。
今回の場合、/member/v4/pingにリクエストして200が返ってくれば生きていると認識します。(※1)
逆に200が返ってこなかった場合は、1秒おき(periodSecondsで定義された秒数)にチェックを繰り返して、一定回数チェックしても200が返ってこない場合はコンテナを再起動させます。
チェック回数はfailureThresholdで定義していて、今回は1なので、1回でも失敗したら即再起動させます。
また、initialDelaySecondsで初回チェックまでの待機時間を決めています。
まとめると今回の場合、コンテナが起動してから20秒後に/member/v4/pingにリクエストして200が返ってくればOK、返ってこなければコンテナを再起動させます。

ResourcesはPodのCPUやMemoryの設定です。
Podとは簡単に記載すると、アプリケーションが起動するためにK8sから割り当てられたHostマシンの一部分のことです。

TerminationGracePeriodSecondsはコンテナが停止するまでの猶予期間です。
K8sがコンテナを停止させる時はSIGTERMというシグナルをコンテナに送信します。
SIGTERMを送信してから、TerminationGracePeriodSecondsで設定した秒数だけ待機した後にコンテナを強制終了(SIGKILLの送信)させます。
なお、SIGTERMやSIGKILLはK8s独自の機能ではなく、Linux標準の機能です。
Linuxにkillコマンドがありますが、これはプロセスに対してSIGTERMやSIGKILLを送信しています。

原因(答え合わせ)

引き延ばして申し訳ございませんでした。答え合わせします。
 
Liveness Probeが失敗するようになった原因は、トレーサを追加したことで、アプリケーションの起動に20秒以上時間がかかるようになったことでした。
Liveness ProbeのinitialDelaySecondsが20秒なので、アプリケーションの起動前にLiveness Probeを実行してしまったので失敗していたというわけです。
 
正確には覚えていないのですが、トレーサ追加前は18,19秒だったのが、追加後は23,24秒になっていたと思います。
ちょっと惜しいですね。

対策

対策は以下の2つです。
l Liveness ProbeのinitialDelaySecondsを伸ばす
l Resourcesを増やす
 
1つ目は分かりやすいと思います。
20秒では足りないので伸ばせば解決します。今回は60秒に伸ばしてみました。(※2)
 
2つ目はそもそもJavaアプリケーションを動かすにあたり、定義しているCPUやMemoryが小さすぎました。今回はResourcesを以下のように修正したところ起動時間が9秒になりました。(※2)

深堀

答え合わせをしたところで、次に、なぜLiveness Probeは失敗していたのに、Readiness Probeは成功していたのかを深堀したいと思います。
 
以下に今回の事象を時系列で整理しました。
正確にログ等でトレースしたわけではないので微妙に違うところもあると思いますがご了承下さい。
 
1.    コンテナ作成 & Spring Boot起動開始
2.    Readiness Probe(失敗)← 1秒おきに繰り返す
※initialDelaySecondsの設定がないのでコンテナ作成後すぐ実行される
3.    Liveness Probe(失敗) & SIGTERM送信
4.    Spring Boot起動完了
※コンテナ作成から23,4秒後
5.    Readiness Probe(成功)← 1秒おきに繰り返しているのでSpring Boot起動直後に成功する
6.    コンテナ停止
※SIGTERMから35秒後
7.    コンテナ再作成
 
つまり、Liveness Probe失敗とPod停止の間にタイムラグがあるので、その間にSpringBootが起動し、Readiness Probeが成功したということです。

補足

※1:正確には200以上400未満であればOKと認識します。
※2:実際には負荷試験でチューニングする必要があります。

まとめ

最後に今回のまとめをしたいと思います。
手短にまとめますのでもう少しお付き合い下さい。
 
今回、Datadogのトレーサを追加したことで問題が発生したので、Datadogのトレーサに原因があるのではないかと思い、最初はDatadogに焦点を当てて調査していました。
ただ、いくら調べてもDatadogに問題は見つからず、途方に暮れました。
 
実際、Spring Boot起動前にLiveness Proveが実行されていたという非常にシンプルな原因だったわけですが、これはJava(特にSpring Boot)の基礎的な知識(起動に必要な時間やCPU、Memory等)とK8sの基礎的な知識(Liveness Proveの仕組み等)が分かっていればすぐに解決出来た問題でした。
また、Linuxの基礎的な知識(SIGTERMやSIGKILL等)も必要でした。
 
これらを踏まえて感じたことは、表面的な知識(JavaのコーディングやK8sのマニフェストファイルの書き方等)が分かっているだけでは問題が起きた時に解決することは難しく、基礎的な知識をもっと深めていくべきだということです。
 
今回の問題を通して色々と勉強することが出来たので、これをきっかけに基礎的な知識を身に着けていこうと思いました。
 
以上です。お読み下さりありがとうございました。


エンジニアのみなさまへ

フリーランスとしてより良い職場環境に行きたい・会社員だけどフリーランスになりたい等のお悩みはありませんか?
エンジニアファーストを運営している株式会社グラントホープでは転職の相談を受け付けております。

  1. 1.スキルに見合った正当な報酬を
  2. 2.忙しく働く方へ自分と向き合う時間を
  3. 3.キャリア形成のサポートを

みなさまへ新しい働き方を提案し、オンラインや対面のご相談でご希望に沿ったキャリア形成を全力でサポートいたします!

登録・応募はページ
チャットボットから!

植木 宥登(うえき ゆうと)

植木 宥登(うえき ゆうと)

新卒でSIerに入社してITの基礎知識や運用保守の知識を身に着ける。 その後、SESに転職してJavaやAWSなどの開発経験を積む。 現在はフリーランスとして幅広く活躍中。