はじめに
docker statsコマンドとkubectl top podコマンドで表示されるメモリー使用量に差異があることに疑問を持った方もいるのではないでしょうか?
その差は、kubectl top コマンドのみ、メモリー使用量にPage Cache(active)が含まれているためらしいのですが、検証も含め少し深掘りしたいと思います。
kubectl topのメモリー使用量
kubectl top コマンドで取得できるメモリー使用量は、各Nodeのkubelet(内のcAdviser)が収集した値です。
ここで、メモリー使用量と言っても具体的に何の指標を利用しているのでしょうか?
公式ドキュメントを見ると、以下の記載があります。
Memory is reported as the working set, in bytes, at the instant the metric was collected.
ふむふむ、working set
らしいです。
少し飛ばして、以下のような記載があります。
However, calculation of the working set varies by host OS, and generally makes heavy use of heuristics to produce an estimate. It includes all anonymous (non-file-backed) memory since kubernetes does not support swap. The metric typically also includes some cached (file-backed) memory, because the host OS cannot always reclaim such pages.
includes some cached (file-backed) memory
とあるので、cacheも含まれてそうですね。
Working setってなんぞ?
ところで、working set
って、分かるような分からない指標です。
Kubernetes issueを見ると、working setの計算式は以下の通りのようです。
$cgroupfs/memory.usage_in_bytes - total_inactive_file
さらに同ページにて、cgroupfs/memory.usage_in_bytes
の詳細もkernel.orgのドキュメントから引用されています。
All mapped anon pages (RSS) and cache pages (Page Cache) are accounted.
まとめると、working setの計算式は、以下のようになります。
RSS + Page Cache - total_inactive_file
docker statsのメモリー使用量
一方、docker statsコマンドのメモリー使用量については、公式ドキュメントに以下の記載があります。
Note: On Linux, the Docker CLI reports memory usage by subtracting page cache usage from the total memory usage.
Linuxおいては、memory usageからpage cacheを引いた値を算出しているようです。
まとめると?
kubectl top および docker statsのメモリー使用量の計算式は、おそらく以下のようになるはずです。
kubectl top : RSS + Page Cache - total_inactive_file
docker stats: RSS - Page Cache
activeなfile cacheがkubectl topでカウントされているので、差異があるということのようです。
検証してみる
file cacheで差異がでるってことは、大きいサイズのファイルをreadすれば、kubectl topのみメモリー使用量が増えるはずです。
ということで検証してみます。
環境
検証で利用する環境は、以下の通りです(AWS EKSを利用しています)。
OS: Amazon linux 2 (RedHat/CoreOSベース)
kubernetes version: 1.14
kubectl version : v1.17.0
docker version: 18.06.1-ce
前準備 - kubelet insecure portの解放
kubeletが収集した結果も参照したいので、kubeletのinsecure port(認証なしでAPI実行可能なport)を解放します。
$ vim sudo vi /etc/kubernetes/kubelet/kubelet-config.json
<省略>
"maxPods": 35,
"readOnlyPort": 10255 <- 追加
}
$ sudo systemctl restart kubelet
$ sudo systemctl status kubelet
● kubelet.service - Kubernetes Kubelet
Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/kubelet.service.d
└─10-kubelet-args.conf, 30-kubelet-extra-args.conf
Active: active (running) since 月 2020-02-03 06:52:35 UTC; 19s ago
ダミーPodの作成
linuxコマンドを実行可能なPodを作成します。
$ kubectl run busybox --image busybox --restart Never --command sleep 99999999
pod/busybox created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 5s
ダミーファイルの作成
ダミーPodへログインし、100MBのファイルを作成します。
$ kubectl exec -it busybox /bin/sh
以下、コンテナ内で実行
# dd if=/dev/zero of=file100 bs=1M count=100
# ls -lh
total 100M
drwxr-xr-x 2 root root 12.0K Dec 23 19:21 bin
drwxr-xr-x 5 root root 360 Feb 3 05:05 dev
drwxr-xr-x 1 root root 66 Feb 3 05:05 etc
-rw-r--r-- 1 root root 100.0M Feb 3 05:12 file1
<以下、省略>
メモリー使用量の計測(ファイル読み込み前)
ファイルを読み込む前に、メモリー使用量を計測しておきます。
以下の通り、使用量は1M程度と微量です。
$ kubetl top po
NAME CPU(cores) MEMORY(bytes)
busybox 0m 1Mi
$ docker stats --no-stream db369d0c34bd
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
db369d0c34bd k8s_POD_busybox_<省略> 0.00% 620KiB / 7.69GiB 0.01% 0B / 0B 0B / 0B 1
$ curl http://localhost:10255/stats/summary
<省略>
"memory": {
"time": "2020-02-03T05:12:23Z",
"usageBytes": 798720,
"workingSetBytes": 798720,
"rssBytes": 102400,
"pageFaults": 1459,
"majorPageFaults": 0
},
<省略>
メモリー使用量の計測(ファイル読み込み後)
コンテナ上で、100MBのファイルをlessコマンドで読み込みます。
# less file100
読み込み後のメモリー計測結果が以下の通りです。
kubectl topコマンド結果およびkubelet apiのworking setの値が大きくなっていることがわかります。
一方、docker statsコマンドには変化がありません。
また、kubelet apiのレスポンスをみると、rssは小さいままなので、working setにはactivなpage cacheが含まれていることが、推察されます。
$ kubetl top po
NAME CPU(cores) MEMORY(bytes)
busybox 0m 101Mi <- 1Mが101Mになった
$ docker stats --no-stream db369d0c34bd
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
db369d0c34bd k8s_POD_busybox_<省略> 0.00% 620KiB / 7.69GiB 0.01% 0B / 0B 0B / 0B 1
$ curl http://localhost:10255/stats/summary
<省略>
"memory":
"time": "2020-02-03T05:30:57Z",
"usageBytes": 105971712,
"workingSetBytes": 105963520, <-サイズが大きくなった
"rssBytes": 106496, <-サイズは(ほぼ)変化なし
"pageFaults": 32905,
"majorPageFaults": 0
},
<省略>