KubernetesのResource RequestsとResource Limitsについて

# Resource Requests

## Resource Requests とは

podをデプロイする時に必要とするリソース(CPU/メモリ)を指定することができる仕組み。
ただしpodは指定したresource requests以上のリソースを使うことができる。(リソース使用量の制限はlimitを使う)
またその指定したリソースがそのpod用に確保されるわけではない。
あくまでpodをデプロイ時にそのリソース量が空いているかをチェックするためのもの。

**ポイントとしてはpodをデプロイするときに、nodeのリソース使用量は見ないで、resource requestsをみてデプロイが行われる。**
なので、nodeのリソース(CPUやメモリ)の使用率が100%でも、resource requestsに空きがあればデプロイされる。空きがないnodeにはデプロイされない。
たとえばメモリ1Gのnodeがあって、resource requestsが400MBの指定がされているpodが2つデプロイされていた場合、3つ目はデプロイできない。
デプロイしようとしたらそのpodのSTATUSは`Pending`になる。


```yaml
apiVersion: v1
kind: Pod
metadata:
  name: requests-pod
spec:
  containers:
  - image: busybox
    command: ["dd", "if=/dev/zero", "of=/dev/null"]
    name: main
    resources:
      requests:  # ここで指定
        cpu: 200m
        memory: 10Mi
```

cpu 200m は200 millicoresの略で**1000milli coreで1cpu**。200 mill coreで 1/5 CPU使うということ。(ちなみに`cpu: 1`と指定したら`cpu: 1000m`と解釈される)
10Miは 10 mebibytes 。

## 例: Resource Requestsを指定してみる

```yaml
$ kubectl run requests-pod-1 --image=busybox --restart Never --requests='cpu=1200m,memory=500Mi' -- sleep 100000

$ kubectl describe nodes
Name:       minikube
...
Capacity: # nodeの使用可能リソース量
  cpu: 2
  memory: 2048484Ki
pods: 110
Allocatable: # Podが使用可能なリソース量(Capacityより若干少ないのはsystem daemonが少しリソースを使っているから)。なおResouceRequestを指定指定してもここの数値は減らない。
  cpu:           2
  memory:        1946084Ki
  pods:          110
...
Non-terminated Pods:         (5 in total) #ここにpodで指定したResource Request値が表示される
  Namespace                  Name                                     CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------                  ----                                     ------------  ----------  ---------------  -------------
  default                    requests-pod-1                           1200m (60%)   0 (0%)      500Mi (26%)      0 (0%)
  kube-system                kube-addon-manager-minikube              5m (0%)       0 (0%)      50Mi (2%)        0 (0%)
  kube-system                kube-dns-54cccfbdf8-686qf                260m (13%)    0 (0%)      110Mi (5%)       170Mi (8%)
  kube-system                kubernetes-dashboard-77d8b98585-mwptt    0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                storage-provisioner                      0 (0%)        0 (0%)      0 (0%)           0 (0%)
Allocated resources: #ここにpodで指定した総Resource Request値が表示される
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ------------  ----------  ---------------  -------------
  1465m (73%)   0 (0%)      660Mi (34%)      170Mi (8%)
```

## 例2: nodeのCapacityを超えてResource Requestを指定してみる

```yaml
$  kubectl run requests-pod-2 --image=busybox --restart Never --requests='cpu=2000m,memory=2000Mi' -- sleep 100000
pod "requests-pod-2" created

# Resource Requestの空きがないためSTATUSは`Pending`になっている
$ kc get pod
NAME             READY     STATUS    RESTARTS   AGE
requests-pod-1   1/1       Running   0          52m
requests-pod-2   0/1       Pending   0          2s

# EventsのMessageに`0/1 nodes are available: 1 Insufficient cpu, 1 Insufficient memory.`と表示されていることが分かる。
$ kc describe pod requests-pod-2
Name:         requests-pod-2
Namespace:    default
Node:         <none>
Labels:       run=requests-pod-2
Annotations:  <none>
Status:       Pending
IP:
Containers:
  requests-pod-2:
    Image:  busybox
    Port:   <none>
    Args:
      sleep
      100000
    Requests:
      cpu:        2
      memory:     2000Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-lvqmh (ro)
Conditions:
  Type           Status
  PodScheduled   False
Volumes:
  default-token-lvqmh:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-lvqmh
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  <none>
Tolerations:     <none>
Events:
  Type     Reason            Age               From               Message
  ----     ------            ----              ----               -------
  Warning  FailedScheduling  3s (x5 over 10s)  default-scheduler  0/1 nodes are available: 1 Insufficient cpu, 1 Insufficient memory.
```

# Resource Limits

## Resource Limitsとは

resource limitsを指定すると、そのpodが使用するリソース(CPU/メモリ)に制限を設けることができる。
たとえnodeにリソースの空きがあっても指定されたresource limits以上のリソースは使用することができなくなる。
resource limitsを指定しない場合、無制限にリソースを使用できる。
上記のようにresource requestsを指定していなかったら、limit値と同様の値がresource requestsに設定される。
resource requestと違って、resource limitはnodeの使用可能リソース量を超えて指定ができる。
たとえばメモリが1Gのnodeがあって、pod Aのメモリrequestが500MBでlimitが800MB、pod Bのメモリrequestが500MBでlimitが500MBでもOK。
CPUも同様。

ちなみにResource Limitで指定されたCPUリソース以上をコンテナのプロセスが実際に使用しようとした場合、
CPU速度が遅くなるだけで、問題が起きるわけではない。
しかしメモリの場合は違う。Resource Limitで指定した以上のメモリをコンテナのプロセスが使用しようとした場合、
コンテナのプロセスはkillされる(一般に"コンテナがOOM Killされた"という)。
もしpodのrestart policyが`Always`もしくは`OnFailure`が設定されていた場合、podのコンテナは自動的に再起動される。なのでkillされたことに気づかないかもしれない。
もし再度メモリオーバーになったら再度再起動されるが、再起動時間はexponential backoffアルゴリズムにしたがって段々遅くなる。
その場合、podのstatusはCrashLoopBackOffになる。
CrashLoopBackOffはkubeletが諦めたことをささない。少しずつ再起動期間は伸びるが最終的に5分間隔で再起動が繰り返されるようになる。
再起動回数はpodのRESTARTSに表示される。

## 例: Resource Limitsを指定してみる

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: limited-pod
spec:
  containers:
  - image: busybox
    command: ["dd", "if=/dev/zero", "of=/dev/null"]
    name: main
    resources:
      limits: # ここで制限を指定
        cpu: 1
        memory: 20Mi
```

以下のようにCPU Limits、Memory LimitsはnodeのCapacityを超えて指定ができる

```yaml
$ kubectl run requests-pod-1 --image=busybox --restart Never --requests='cpu=1200m,memory=500Mi' -- sleep 100000
$ kubectl run requests-and-limits-pod-2 --image=busybox --restart Never --requests='cpu=200m,memory=400Mi' --limits='cpu=1500m,memory=1500Mi' -- sleep 100000
$ kubectl run requests-and-limits-pod-3 --image=busybox --restart Never --requests='cpu=200m,memory=400Mi' --limits='cpu=1500m,memory=1500Mi' -- sleep 100000

$ kubectl describe nodes
・・・
Non-terminated Pods:         (7 in total)
  Namespace                  Name                                     CPU Requests  CPU Limits   Memory Requests  Memory Limits
  ---------                  ----                                     ------------  ----------   ---------------  -------------
  default                    requests-and-limits-pod-2                200m (10%)    1500m (75%)  400Mi (21%)      1500Mi (78%)
  default                    requests-and-limits-pod-3                200m (10%)    1500m (75%)  400Mi (21%)      1500Mi (78%)
  default                    requests-pod-1                           1200m (60%)   0 (0%)       500Mi (26%)      0 (0%)
  kube-system                kube-addon-manager-minikube              5m (0%)       0 (0%)       50Mi (2%)        0 (0%)
  kube-system                kube-dns-54cccfbdf8-686qf                260m (13%)    0 (0%)       110Mi (5%)       170Mi (8%)
  kube-system                kubernetes-dashboard-77d8b98585-mwptt    0 (0%)        0 (0%)       0 (0%)           0 (0%)
  kube-system                storage-provisioner                      0 (0%)        0 (0%)       0 (0%)           0 (0%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ------------  ----------  ---------------  -------------
  1865m (93%)   3 (150%)    1460Mi (76%)     3170Mi (166%)
・・・
```

# 参考

* [Kubernetes in Action](http://amzn.asia/80rSDOx) (最高に分かりやすく書かれているのでぜひ買いましょう)
* [Managing Compute Resources for Containers](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/)
* [Kubernetesで実際のメモリを超えるコンテナアプリを動かすと、どうなるか？](http://dr-asa.hatenablog.com/entry/2018/04/10/171807)