Datadogとkubectl top podで取得できるpodのメモリ関連情報の差異について


概要

Kubernetesの監視にDatadogを利用しており、podごとのメモリ使用率を監視していたところ、想定していたメモリ使用率を大幅に超えているpodがあったため調査しました。

異変に気づいた時のエビデンス

今回はCluster Autoscalerのpodのメモリ使用率が他のpodよりも目についたため、これを中心に調査しました。
kubectl top podで見えるメモリ使用率が42Mi程度、Limitで設定しているメモリ割り当ては256Miのため、メモリ使用率は16%程度のはずが、Datadogから取得できるメモリ使用率は99%以上となっていました。

kubectl top podで見えるメモリ使用量

$ kubectl top pod -n kube-system cluster-autoscaler-xxxx
NAME                                  CPU(cores)   MEMORY(bytes)
cluster-autoscaler-xxxx               2m           42Mi


$ kubectl describe node ip-000-000-000-000.ap-northeast-1.compute.internal
~~~~中略
  Namespace                   Name                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                   ----                                   ------------  ----------  ---------------  -------------  ---
  kube-system                 cluster-autoscaler-xxxx                200m (5%)     200m (5%)   256Mi (3%)       256Mi (3%)     4d22h

Datadogモニターで見えるメモリ使用量

Datadogではkubernetes.memory.usage_pctのメトリクスを利用しています。
以下はメモリ使用率のグラフなのですが、ほぼ100%に張り付いていることがわかります。

コンテナのメモリ使用量を見ても252Mib使っており、メモリ使用率と同様に想定を大きく上回る値を計測していました。

調査内容

Datadogはpodのメモリ使用率をどのように算出しているか

kubectl top podで見えているメモリ使用率がおかしいという可能性も0ではなかったのですが、上記のkubectl get nodeの結果と、時間が経過してもOOMKillerでpodが死ぬことはなかったことから、Datadogで見えているメトリクスがおかしいと仮定して調査を行いました。

kubernetes.memory.usage_pctがどのようにメモリ使用量を算出しているかをDatadogのサポートデスクに問い合わせたところ、Datadogはkubelet API('/metrics/cadvisor')をみているとの回答を頂きました。

cAdvisorのmemory_usageがどのようにメモリ使用率を算出しているかについては、issueで議論されていることとソースコード中のコメントから、今現在アクティブであるかどうかにかかわらず、コールドキャッシュやSWAPなども含むアクティブではないメモリも使用率としてカウントしていることがわかりました。

また、issue内の以下のコメントから working_set というメトリクスを利用することで、非アクティブなメモリを除外したメモリ使用量を取得することができそうだということがわかります。

memory_usage = RAM usage include pages that have not been accessed in a long time.
working set = memory usage - inactive memory.

対応

上記の調査から代替メトリクスとしてworking_setを利用したところ、kubectl top podで取得した時のメモリ使用量と同じ値を取得することができました。

ただし、ここで確認した値はあくまでメモリ使用量で、監視したいメトリクスはメモリ使用率であるため、変換する必要あります。
メモリ使用率は、メモリ使用量とkubernetesマニフェストで指定しているメモリのリソースリミットから算出できるので、以下の計算結果の値を設定することでメモリ使用率が取得できそうです。

memory.working_set / MemoryのLimitの設定値 * 100

上記の計算式(memory.working_set / MemoryのLimitの設定値 * 100)を元にDatadog上で以下のように設定したところ、約16%と適切なメモリ使用率を取得することができました。