inductor's blog

nothing but self note :)

リソース制限をかけたKubernetes Podの中でhtopをしてもホスト上のリソースが表示されるのはなぜか

はじめに

これは、Kubernetesアドベントカレンダー2の20日目の記事です、と思っていたら1の17日目が空いていたのでそっちに移します。

結論

htopはホストのリソースを取得できるカーネルの情報を見に行っているから!

  終
制作・著作
━━━━━
 ⓃⒽⓀ

終わりではない

これだけだとアドベントカレンダーの記事としては内容があまりにも薄すぎるのでもうちょっと書きます。

この話を進める前に揃えておくべき前提知識の話。コンテナとVMの違いを説明する方法はいくつかありますが、決定的な違いとしては根本的にリソースの分離の仕方がちがうところにあります(細かいこというのもアレですが、ここではruncなどの標準的なコンテナ実装を用いた場合の話をしています)。

仮想マシンはホストマシンのリソースを(USBやPCIなどをパススルーする場合を除いて)基本的には全て隠蔽し、少ないリソースでデバイスのエミュレーションをすることでOS実際のハードウェアと同じ振る舞いをしています。そのためOS起動の時点でCPUやメモリはゲストマシンのものを使っていることになります。一方でコンテナは本質的にはただの違うユーザーが動かすプロセスなので、動作するカーネル自体はホストのものを使うことになります。実際にそれを確かめる例として、/proc/meminfo/proc/cpuinfo/proc/loadavgを確認することが挙げられます。

$ kubectl describe node kind-worker
...
Capacity:
  cpu:                4
  ephemeral-storage:  527322552Ki
  memory:             2031328Ki
  pods:               110
...

このワーカーには4CPUが割り当てられています。リソース制限をかけた状態でPodを起動してみましょう。

$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: debian
spec:
  containers:
  - name: debian
    image: debian:bookworm-slim
    command: ["sleep", "1000"]
    resources:
      requests:
        memory: "500Mi"
        cpu: "1"
      limits:
        memory: "500Mi"
        cpu: "1"
EOF
pod/debian created

起動したPodでcpuinfoを見てみます。

$ kubectl exec -it debian -- cat /proc/cpuinfo
processor   : 0
processor   : 1
processor   : 2
processor   : 3

htopを実行すると...

# apt update && apt install -y htop
# htop

/proc/cpuinfoはホストカーネルの情報を表示するものなので、たとえコンテナ越しであってもコンテナ単位の正しいリソース利用状況を取得することはできません。したがって、htopでも同様にホストの情報を取得していることがわかります。

CPUやメモリの制限値はどこをみたらわかるのか

さて、ここまででホストの情報は取得できました。Pod固有の値を見に行く場合は、以下のファイルを参照すればよいです。ただし、ここで確認しているのはcgroup v2です。古いOSを使っている場合は若干取得するメトリクスが異なります。

$ kubectl exec -it debian -- cat /sys/fs/cgroup/cpu.max
100000 100000   # コンテナが指定期間内で使用できる CPU 時間の最大量(マイクロ秒単位)と、その期間(マイクロ秒単位)
$ kubectl exec -it debian -- cat /sys/fs/cgroup/cpu.stat
usage_usec 75571      # コンテナが消費した総 CPU 時間(マイクロ秒単位)
user_usec 30228       # コンテナのユーザーモードでの CPU 時間(マイクロ秒単位)
system_usec 45342     # コンテナのカーネルモードでの CPU 時間(マイクロ秒単位)
nr_periods 7          # モニタリング期間の数(CPU クォータの計算が行われた回数)
nr_throttled 0        # コンテナが CPU クォータによって制限(スロットル)された回数
throttled_usec 0      # コンテナがスロットルされた総時間(マイクロ秒単位)

思ったよりいろんな情報がコンテナ内でとれますね。とはいえ、正直こんなものを内部で表示してもまともに運用で使えるわけではないので、現実的にはkube-state-metricsやmetrics-serverなど、より幅広く一般的に使われる便利なメトリクスツールを介して情報を取得すべきです。Podの中で情報を参照するのはそもそもセキュリティ的にもアレなので・・・。

ところで、これを読んでいるみなさんの中には、「CPU 時間(マイクロ秒単位)」という表現が突然出てきて混乱している人もいるかもしれません。我々がKubernetesのマニフェストに制限として指定する値はCPUの数なのに、なぜcgroupsの値は秒なのか、と。

気になった方は、自分が2年前に発表したKubeConの発表とその補足資料も併せてご覧いただけるといいと思います。

blog.inductor.me

www.youtube.com

というわけで今日はこのへんで。