Kubernetes Resource Limits の挙動確認

# はじめに

Kubernetesでは、Podに割り当てるリソースの制限が出来ます。主に2種類の指定方法があります。自分たちのワークロードに応じて、適切なものを選択すると良いでしょう。

**Resource Requests**
Podを作成する時に必要なリソースをリクエストする指定。実際にPodが使用するリソースは制限しない。Requestsで指定したリソースを超えて無制限にリソースを利用可能

**Resource Limits** 
Podが実際に資料するリソースを制限する仕組み。CPUを指定すると、指定した数以上は使用されない。メモリを指定すると、指定した数を超えた場合は、OOM Killer でPodを強制終了する(OOM Killer)

今回の記事では、Resource Limit の挙動確認します。Kubernetes は、Oracle Cloud の Kubernetesサービス(OKE)を使っていますが、一般的な環境と変わらないはずです。



# Resource Limits

Resource Limits 有りの Pod を作成します。マニフェストファイル内に、`cpu: "1"` や `memory: "200Mi"` を指定します。余談ですがコンテナイメージの`sugimount/toolbox` は、便利なツール群を導入済みの Ubuntu 18.04 の Image です。自作のものです。Dockerfile は [Docker Hub](https://hub.docker.com/repository/registry-1.docker.io/sugimount/toolbox/builds/07c2d87a-cb41-4e41-8a92-9fa4a2f068a8) 経由でたどれるので興味があればどうぞ。

- `cpu: "1"` : 1秒間に、1個の vCPU を1000ミリ秒使用可能な制限
- `memory: "200Mi" ` : Memory を 200MiB の制限

```
cat <<'EOF' > /home/testuser01/manifests/resourcelimit.yaml
apiVersion: v1
kind: Pod
metadata:
  name: limited-pod
spec:
  containers:
  - image: sugimount/toolbox:release-0.0.3
    name: limited-pod
    command: ["sleep", "1800m"]
    resources:
      limits:
        cpu: "1"
        memory: "200Mi"
EOF
```



Apply して作成します。

```
kubectl apply -f /home/testuser01/manifests/resourcelimit.yaml
```



Resource Limits 無し。

```
cat <<'EOF' > /home/testuser01/manifests/no_resourcelimit.yaml
apiVersion: v1
kind: Pod
metadata:
  name: no-limited-pod
spec:
  containers:
  - image: sugimount/toolbox:release-0.0.3
    name: no-limited-pod
    command: ["sleep", "1800m"]
EOF
```



Apply

```
kubectl apply -f /home/testuser01/manifests/no_resourcelimit.yaml
```



2個の Pod が稼働しています

```
$ kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
limited-pod      1/1     Running   0          38m   10.244.3.18   10.0.10.6   <none>           <none>
no-limited-pod   1/1     Running   0          33s   10.244.3.19   10.0.10.6   <none>           <none>
```



# 動作確認 Resource Limits あり

Resource Limits が指定されているPod 内で負荷を掛けるために、bash を起動します。

```
kubectl exec -it limited-pod bash
```



なお、Pod 内からCPUの詳細情報を確認すると、Hostで見えているCPUがすべての数見えています。特に Resource Limits の指定は関係ないようです。(下の例だと2vCPU分)

```
root@limited-pod:/# cat /proc/cpuinfo 
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 85
model name      : Intel(R) Xeon(R) Platinum 8167M CPU @ 2.00GHz
stepping        : 4
microcode       : 0x1
cpu MHz         : 1995.308
cache size      : 16384 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology cpuid tsc_known_freq pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat umip pku ospke md_clear
bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit
bogomips        : 3990.61
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 85
model name      : Intel(R) Xeon(R) Platinum 8167M CPU @ 2.00GHz
stepping        : 4
microcode       : 0x1
cpu MHz         : 1995.308
cache size      : 16384 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1
apicid          : 1
initial apicid  : 1
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology cpuid tsc_known_freq pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat umip pku ospke md_clear
bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit
bogomips        : 3990.61
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

root@limited-pod:/# 
```



メモリについても同様で、Host側に搭載されているメモリが全て見えています。こちらも、特に Resource Limits の指定は関係なく、すべてが見えています。

```
# free -m
              total        used        free      shared  buff/cache   available
Mem:          14761         607       12669          25        1485       13848
Swap:             0           0           0
```



stress コマンドで CPU に負荷を掛けます

```
stress -c 2
```



Host側で dstat コマンドを実行し、消費したリソースを確認します。左列の `usr` の部分が、50% 前後になっています。Resource Limits で `cpu: "1"` 指定しているため、2個中の1個が使用されたと考えると、数値は一致しています。正常に制限されていますね。(なお、Resource Limits の指定が無い場合は、100% になります。後で説明します)

```
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]# dstat
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw
  6   2  92   0   0   0| 151k  230k|   0     0 |   0     0 |1005  2150
  2   1  98   0   0   0|   0    20k|4276B 5411B|   0     0 | 782  1516
  2   1  98   0   0   0|   0     0 |3139B 3357B|   0     0 | 684  1451
  3   1  96   0   0   0|   0  8192B|8588B   13k|   0     0 | 737  1605
 37   0  63   0   0   0|   0     0 |2939B 3176B|   0     0 |1255  1298
 52   1  47   0   0   0|   0     0 |2566B 2886B|   0     0 |1655  1584
 52   1  48   0   0   0|   0  4096B|2658B 2978B|   0     0 |1592  1379
 53   1  45   0   0   0|   0     0 |2966B 3352B|   0     0 |1892  2590
 52   0  48   0   0   0|   0     0 |4321B 5125B|   0     0 |1394   986
```



同様にメモリにも負荷を掛けましょう。Resource Limits の範囲に収まる指定です。

```
stress --vm 1 --vm-bytes 190M --vm-hang 1
```



Host側のメモリ消費量を見ると、`used` の部分が 190M 程増えています。

```
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]# dstat -tpcdrmngy
Terminal width too small, trimming output.
----system---- ---procs--- ----total-cpu-usage---- -dsk/total- --io/total- ------memory-usage----- -net/total- ---paging-->
     time     |run blk new|usr sys idl wai hiq siq| read  writ| read  writ| used  buff  cach  free| recv  send|  in   out >
05-07 07:42:24|  0   0 9.5|  5   2  93   0   0   0| 107k  178k|1.96  6.81 | 717M 3108k 1382M 12.4G|   0     0 |   0     0 >
05-07 07:42:25|  0   0   0|  1   1  98   0   0   0|   0     0 |   0     0 | 717M 3108k 1382M 12.4G|5354B 4794B|   0     0 >
05-07 07:42:26|  0   0  12|  3   2  96   0   0   0|   0     0 |   0     0 | 717M 3108k 1382M 12.4G|6188B 6736B|   0     0 >
05-07 07:42:27|  0   0 4.0|  3   1  96   0   0   0|   0    16k|   0  4.00 | 717M 3108k 1382M 12.4G|4541B  973B|   0     0 >
05-07 07:42:28|  0   0   0|  4   1  96   0   0   0|   0     0 |   0     0 | 717M 3108k 1382M 12.4G|5445B 6106B|   0     0 >
05-07 07:42:29|  0   0 3.0|  2   3  95   0   0   0|   0     0 |   0     0 | 907M 3108k 1382M 12.2G|6244B   11k|   0     0 >
05-07 07:42:30|  0   0   0|  2   3  95   0   0   0|   0     0 |   0     0 | 907M 3108k 1382M 12.2G|5655B 5914B|   0     0 >
05-07 07:42:31|  0   0  23|  5   4  92   0   0   0|   0     0 |   0     0 | 908M 3108k 1382M 12.2G| 287B  875B|   0     0 >
05-07 07:42:32|  0   0 6.0|  3   3  94   0   0   0|   0  8192B|   0  1.00 | 908M 3108k 1382M 12.2G|3982B 4502B|   0     0 >
05-07 07:42:33|  0   0   0|  2   3  96   0   0   0|   0     0 |   0     0 | 908M 3108k 1382M 12.2G|2254B 2806B|   0     0 >^C
```



では今度は、Resource Limits を超える負荷を掛けてみます。すると、bash が強制終了して、Pod から排出されました。

```
root@limited-pod:/# stress --vm 1 --vm-bytes 250M --vm-hang 1
stress: info: [22] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
command terminated with exit code 137
testuser01@cloudshell:manifests (ap-tokyo-1)$ 
```



Pod が強制終了して、Restartしている様子が見えます。

```
$ kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
limited-pod      1/1     Running   4          50m   10.244.3.29   10.0.10.6   <none>           <none>
no-limited-pod   1/1     Running   0          48m   10.244.3.25   10.0.10.6   <none>           <none>
```



`output=yaml` を指定して、詳細な情報を確認しましょう。

```
kubectl get pod limited-pod --output=yaml
```



lastState の欄に、`reason: OOMKilled` が見えます。Resource Limits の指定を超えて利用したため、コンテナプロセスが強制終了されましたね。

```
省略
    lastState:
      terminated:
        containerID: docker://263b2ef2a1ee4cfdd86e37fb88ddecdcc2db92c05794c5b096a6b2cf756e3a83
        exitCode: 137
        finishedAt: "2020-07-05T07:43:13Z"
        reason: OOMKilled
        startedAt: "2020-07-05T07:38:59Z"
省略
```



Host側メモリを見ると、特段変化はありません。すぐにメモリが Limits を超えたことを検知して、OOM Killed されました。

```
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]# dstat -tpcdrmngy
Terminal width too small, trimming output.
----system---- ---procs--- ----total-cpu-usage---- -dsk/total- --io/total- ------memory-usage----- -net/total- ---paging-->
     time     |run blk new|usr sys idl wai hiq siq| read  writ| read  writ| used  buff  cach  free| recv  send|  in   out >
05-07 07:43:10|  0   0 9.5|  5   2  93   0   0   0| 106k  177k|1.95  6.77 | 717M 3108k 1383M 12.4G|   0     0 |   0     0 >
05-07 07:43:11|  0   0  10|  2   1  98   0   0   0|   0     0 |   0     0 | 717M 3108k 1383M 12.4G|2668B 4084B|   0     0 >
05-07 07:43:12|  0   0   0|  2   1  98   0   0   0|   0     0 |   0     0 | 717M 3108k 1383M 12.4G|3470B 3724B|   0     0 >
05-07 07:43:13|  0   0 6.0|  1   1  98   0   0   0|   0    16k|   0  3.00 | 718M 3108k 1383M 12.4G|3548B 4195B|   0     0 >
05-07 07:43:14|1.0   0  81| 21  21  51   6   0   1|   0  1860k|   0  85.0 | 716M 3108k 1383M 12.4G|  16k   18k|   0     0 >
05-07 07:43:15|2.0   0  81| 12  10  78   0   0   0|   0   436k|   0  80.0 | 724M 3108k 1390M 12.3G|  14k 9245B|   0     0 >
05-07 07:43:16|1.0   0  86| 14   8  77   0   0   0|   0    48k|   0  11.0 | 721M 3108k 1383M 12.4G|  11k 6252B|   0     0 >
05-07 07:43:17|  0   0  21|  7   3  90   0   0   0|   0     0 |   0     0 | 715M 3108k 1383M 12.4G|8982B 5462B|   0     0 >
05-07 07:43:18|1.0   0  25|  3   2  95   0   0   0|   0     0 |   0     0 | 715M 3108k 1383M 12.4G|2688B 3152B|   0     0 >^C
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]#
```



# 動作確認 Resource Limits なし

Resource Limits が無いPod 内で負荷を掛けるために、bash を起動します。

```
kubectl exec -it no-limited-pod bash
```



Pod 内から見える CPU の情報は、Resource Limits の有無では何もかわりません

```
root@no-limited-pod:/# cat /proc/cpuinfo 
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 85
model name      : Intel(R) Xeon(R) Platinum 8167M CPU @ 2.00GHz
stepping        : 4
microcode       : 0x1
cpu MHz         : 1995.308
cache size      : 16384 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology cpuid tsc_known_freq pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat umip pku ospke md_clear
bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit
bogomips        : 3990.61
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 85
model name      : Intel(R) Xeon(R) Platinum 8167M CPU @ 2.00GHz
stepping        : 4
microcode       : 0x1
cpu MHz         : 1995.308
cache size      : 16384 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1
apicid          : 1
initial apicid  : 1
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology cpuid tsc_known_freq pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat umip pku ospke md_clear
bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit
bogomips        : 3990.61
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

root@no-limited-pod:/# 
```



Memory も同様に何もかわりません

```
root@no-limited-pod:/# free -m
              total        used        free      shared  buff/cache   available
Mem:          14761         606       12666          25        1489       13851
Swap:             0           0           0
root@no-limited-pod:/# 
```



stress コマンドで CPU に負荷を掛けます

```
stress -c 2
```



Host側で dstat コマンドを実行し、消費したリソースを確認します。`usr` の部分が、99% 前後になっています。Resource Limits が有りでは、50% 前後になっていたところですが、何も制限されていないことが分かります。

```
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]# dstat -tpcdrmngy
Terminal width too small, trimming output.
----system---- ---procs--- ----total-cpu-usage---- -dsk/total- --io/total- ------memory-usage----- -net/total- ---paging-->
     time     |run blk new|usr sys idl wai hiq siq| read  writ| read  writ| used  buff  cach  free| recv  send|  in   out >
05-07 08:46:03|  0   0 8.3|  4   2  94   0   0   0|  69k  129k|1.26  4.83 | 721M 3108k 1385M 12.4G|   0     0 |   0     0 >
05-07 08:46:04|  0   0 4.0|  4   2  95   0   0   0|   0     0 |   0     0 | 721M 3108k 1385M 12.4G|  16k   19k|   0     0 >
05-07 08:46:05|  0   0 2.0|  1   0  99   0   0   0|   0    16k|   0  4.00 | 721M 3108k 1385M 12.4G|6948B 5678B|   0     0 >
05-07 08:46:06|1.0   0  10|  2   1  97   0   0   0|   0   212k|   0  28.0 | 722M 3108k 1385M 12.4G|2658B 3138B|   0     0 >
05-07 08:46:07|2.0   0 3.0| 75   0  25   0   0   0|   0     0 |   0     0 | 722M 3108k 1385M 12.4G|4912B 5792B|   0     0 >
05-07 08:46:08|3.0   0 2.0| 99   1   0   0   0   0|   0     0 |   0     0 | 722M 3108k 1385M 12.4G|3815B 4355B|   0     0 >
05-07 08:46:09|2.0   0 4.0| 99   1   1   0   0   0|   0     0 |   0     0 | 722M 3108k 1385M 12.4G|2566B 3072B|   0     0 >
05-07 08:46:10|2.0   0 6.0| 99   1   0   0   0   1|   0    12k|   0  3.00 | 722M 3108k 1385M 12.4G|7160B 3705B|   0     0 >
05-07 08:46:11|2.0   0 1.0| 99   1   1   0   0   0|   0     0 |   0     0 | 722M 3108k 1385M 12.4G|8320B   13k|   0     0 >^C
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]#
```



では今度は、メモリに負荷を掛けます。

```
root@limited-pod:/# stress --vm 1 --vm-bytes 250M --vm-hang 1
stress: info: [22] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
command terminated with exit code 137
testuser01@cloudshell:manifests (ap-tokyo-1)$ 
```



Host側メモリを見ると、250MB 程のメモリが使用されています。Resource Limits 有りでは OOM Killed されましたが、Resource Limits 無しでは正常に稼働しています。

```
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]# dstat -tpcdrmngy
Terminal width too small, trimming output.
----system---- ---procs--- ----total-cpu-usage---- -dsk/total- --io/total- ------memory-usage----- -net/total- ---paging-->
     time     |run blk new|usr sys idl wai hiq siq| read  writ| read  writ| used  buff  cach  free| recv  send|  in   out >
05-07 08:47:30|  0   0 8.3|  4   2  94   0   0   0|  69k  128k|1.25  4.80 | 721M 3108k 1385M 12.4G|   0     0 |   0     0 >
05-07 08:47:31|  0   0  12|  1   1  98   0   0   0|   0     0 |   0     0 | 721M 3108k 1385M 12.4G| 132B  150B|   0     0 >
05-07 08:47:32|  0   0 1.0|  3   1  96   0   0   0|   0     0 |   0     0 | 721M 3108k 1385M 12.4G|  11k   17k|   0     0 >
05-07 08:47:33|  0   0  13|  4   2  94   0   0   0|   0     0 |   0     0 | 721M 3108k 1385M 12.4G| 946B 1022B|   0     0 >
05-07 08:47:34|  0   0 2.0|  3   5  92   0   0   0|   0    28k|   0  5.00 | 972M 3108k 1385M 12.1G|  23k   24k|   0     0 >
05-07 08:47:35|  0   0 4.0|  3   4  94   0   0   0|   0     0 |   0     0 | 972M 3108k 1385M 12.1G|  66B   66B|   0     0 >
05-07 08:47:36|  0   0  12|  2   4  94   0   0   1|   0   136k|   0  20.0 | 972M 3108k 1385M 12.1G|6038B 7014B|   0     0 >
05-07 08:47:37|  0   0   0|  1   4  95   0   0   0|   0     0 |   0     0 | 972M 3108k 1385M 12.1G|1184B 1772B|   0     0 >
05-07 08:47:38|  0   0   0|  2   4  94   0   0   0|   0     0 |   0     0 | 972M 3108k 1385M 12.1G|6213B 6756B|   0     0 >^C
[root@oke-crdazdbgvsw-n3geolfmm3d-s6bnuvahoda-2 ~]#
```







# まとめ

この結果をみると、CPU については Limits を指定すれば、指定した数を超えずに稼働してくれて良いです。

メモリについては、指定した数を超えると OOM Killed されて強制再起動されます。そのため、該当の Worker Node の全体でメモリをうまく管理していく必要があります。



# 参考URL

CPU
https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-cpu-resource/

Memory
https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-memory-resource/

Metric Server
https://github.com/kubernetes-sigs/metrics-server