[k8s] Pod への CPU/メモリ制限について確認した時のメモ(requests/limit/LimitRanges)

以下を確認したときのメモ。

[コンテナおよびPodへのCPUリソースの割り当て](https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-cpu-resource/)
[コンテナおよびPodへのメモリーリソースの割り当て](https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-memory-resource/)

動きとしては基本的に同じように見えたので、今回は [コンテナおよびPodへのCPUリソースの割り当て](https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-cpu-resource/) を確認する。(メモリの場合、OOM Kill の可能性があるなどは違うと思うが）

# まとめ

+ 各 Node で現状どれだけリソースが割当可能であるかは `kubectl describe node` コマンドで **Allocated resources** の情報を見て確認可能
+ `resources:requests` によって Pod が要求する下限の値を指定可能であり、スケジューリングで考慮される
+ `resources:limits` によって Pod が利用可能な上限の値を指定可能である
+ `LimitRanges` によってデフォルト値などの設定が可能

# 環境

AWS の EKS を利用。

```bash
$kubectl version
Client Version: version.Info{Major:"1", Minor:"16+", GitVersion:"v1.16.6-beta.0", GitCommit:"e7f962ba86f4ce7033828210ca3556393c377bcc", GitTreeState:"clean", BuildDate:"2020-01-15T08:26:26Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"14+", GitVersion:"v1.14.9-eks-f459c0", GitCommit:"f459c0672169dd35e77af56c24556530a05e9ab1", GitTreeState:"clean", BuildDate:"2020-03-18T04:24:17Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}

# alias の設定
$alias |grep kubectl
k=kubectl
```

# CPUの要求と制限を指定する

[CPUの要求と制限を指定する](https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-cpu-resource/#cpu%E3%81%AE%E8%A6%81%E6%B1%82%E3%81%A8%E5%88%B6%E9%99%90%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B)という項目をやってみる
事前に `kubectl create namespace cpu-example` は実行済みとする。

まずは検証用の Pod を起動させてない状態で2つのNode のリソース状況を確認。

```bash
$k describe node ip-192-168-48-33.ap-northeast-1.compute.internal

・・・

Capacity:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           20959212Ki
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3977916Ki
 pods:                        17
Allocatable:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           19316009748
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3875516Ki
 pods:                        17

・・・

Non-terminated Pods:         (4 in total)
  Namespace                  Name                        CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                        ------------  ----------  ---------------  -------------  ---
  kube-system                aws-node-6gp92              10m (0%)      0 (0%)      0 (0%)           0 (0%)         15d
  kube-system                coredns-58986cd576-8s9gt    100m (5%)     0 (0%)      70Mi (1%)        170Mi (4%)     13d
  kube-system                coredns-58986cd576-cf457    100m (5%)     0 (0%)      70Mi (1%)        170Mi (4%)     13d
  kube-system                kube-proxy-cckxl            100m (5%)     0 (0%)      0 (0%)           0 (0%)         15d
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                    Requests    Limits
  --------                    --------    ------
  cpu                         310m (15%)  0 (0%)
  memory                      140Mi (3%)  340Mi (8%)
  ephemeral-storage           0 (0%)      0 (0%)
  attachable-volumes-aws-ebs  0           0
Events:                       <none>

$ k describe node ip-192-168-74-169.ap-northeast-1.compute.internal

・・・

Capacity:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           20959212Ki
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3977916Ki
 pods:                        17
Allocatable:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           19316009748
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3875516Ki
 pods:                        17


・・・

Non-terminated Pods:         (2 in total)
  Namespace                  Name                CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                ------------  ----------  ---------------  -------------  ---
  kube-system                aws-node-vs6bm      10m (0%)      0 (0%)      0 (0%)           0 (0%)         15d
  kube-system                kube-proxy-sgv4g    100m (5%)     0 (0%)      0 (0%)           0 (0%)         15d
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                    Requests   Limits
  --------                    --------   ------
  cpu                         110m (5%)  0 (0%)
  memory                      0 (0%)     0 (0%)
  ephemeral-storage           0 (0%)     0 (0%)
  attachable-volumes-aws-ebs  0          0
Events:                       <none>

```

同じインスタンスタイプを使っているので **Capacity(総リソース量)** と **Allocatable(割当可能なリソース量)** については同じになっている。
こちらはあくまで Node の性能によるもので Pod の起動によって変わるものではないと思われる（違っていたらご指摘下さい）

実際に起動している Pod などを差し引いて対象 Node で割り当て済みリソースを確認出来るのは **Allocated resources** の部分となる。
「ip-192-168-48-33」の方が CPU リソースが利用されているが、これは core-dns の Pod 2つがこちらの Node で動いているためかと思われる。

```bash
# core-dns が片方の Node によっている
$k get pods -A -o wide
NAMESPACE     NAME                       READY   STATUS    RESTARTS   AGE   IP               NODE                                                NOMINATED NODE   READINESS GATES
kube-system   aws-node-6gp92             1/1     Running   0          15d   192.168.48.33    ip-192-168-48-33.ap-northeast-1.compute.internal    <none>           <none>
kube-system   aws-node-vs6bm             1/1     Running   0          15d   192.168.74.169   ip-192-168-74-169.ap-northeast-1.compute.internal   <none>           <none>
kube-system   coredns-58986cd576-8s9gt   1/1     Running   0          13d   192.168.53.252   ip-192-168-48-33.ap-northeast-1.compute.internal    <none>           <none>
kube-system   coredns-58986cd576-cf457   1/1     Running   0          13d   192.168.34.56    ip-192-168-48-33.ap-northeast-1.compute.internal    <none>           <none>
kube-system   kube-proxy-cckxl           1/1     Running   0          15d   192.168.48.33    ip-192-168-48-33.ap-northeast-1.compute.internal    <none>           <none>
kube-system   kube-proxy-sgv4g           1/1     Running   0          15d   192.168.74.169   ip-192-168-74-169.ap-northeast-1.compute.internal   <none>           <none>

# core-dns では request で cpu を要求していた
$k describe pods -A |less
・・・
    Requests:
      cpu:        100m
      memory:     70Mi
```

各 Node の CPU割当の差分は 200m(310m-110m) だったので core-dns の起動が影響している事が確認出来た。

次に以下の検証用の YAML を作る。

```cpu-request-limit.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr
    image: vish/stress
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"
```

```bash
# Pod を作成
$k apply -f cpu-request-limit.yaml
pod/cpu-demo created

# 起動した Node を特定
$k get pods -A -o wide |grep cpu-demo
cpu-example   cpu-demo                   1/1     Running   0          26s   192.168.84.154   ip-192-168-74-169.ap-northeast-1.compute.internal   <none>           <none>

# Node の状態を確認
$k describe node ip-192-168-74-169.ap-northeast-1.compute.internal

・・・

Capacity:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           20959212Ki
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3977916Ki
 pods:                        17
Allocatable:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           19316009748
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3875516Ki
 pods:                        17


・・・

Non-terminated Pods:         (3 in total)
  Namespace                  Name                CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                ------------  ----------  ---------------  -------------  ---
  cpu-example                cpu-demo            500m (25%)    1 (50%)     0 (0%)           0 (0%)         16s
  kube-system                aws-node-vs6bm      10m (0%)      0 (0%)      0 (0%)           0 (0%)         15d
  kube-system                kube-proxy-sgv4g    100m (5%)     0 (0%)      0 (0%)           0 (0%)         15d
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                    Requests    Limits
  --------                    --------    ------
  cpu                         610m (30%)  1 (50%)
  memory                      0 (0%)      0 (0%)
  ephemeral-storage           0 (0%)      0 (0%)
  attachable-volumes-aws-ebs  0           0
Events:                       <none>

```

**Allocated resources** の項目が Pod 起動前の 110m から 610m になっているのが分かる。
これは Pod の requests で 「0.5」を指定した為であり、想定どおり。
実際に **Allocated resources** 　より少し上の部分で各 Pod でどれだけリソース割当を行っているかも分かるがこちらもで 500m が cpu-demo Pod に割り当てられている事が分かる。

次に実際のリソース利用状況を見てみる。

```bash
$kubectl top pod cpu-demo --namespace=cpu-example
Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)
```

失敗。
EKS ではメトリクスサーバーがデフォルではインストールされなかったので以下に従い、インストールする。

[Kubernetes メトリクスサーバーのインストール](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/metrics-server.html)

少し待てば `kubectl top` コマンドが使えるようになる。

```bash
$kubectl top pod cpu-demo --namespace=cpu-example
NAME       CPU(cores)   MEMORY(bytes)
cpu-demo   998m         1Mi
```

コンテナでは `--cpus "2"` を指定し、実際には Node では vCPU=2 ではあるが、`limits` で `cpu: "1"` を指定する事で 1vCPU が上限となっている事が分かった。

リソースは削除しておく

```bash
$kubectl delete pod cpu-demo --namespace=cpu-example
```

# ノードよりも大きいCPU要求を指定する

[ノードよりも大きいCPU要求を指定する](https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-cpu-resource/#%E3%83%8E%E3%83%BC%E3%83%89%E3%82%88%E3%82%8A%E3%82%82%E5%A4%A7%E3%81%8D%E3%81%84cpu%E8%A6%81%E6%B1%82%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B)

以下のような 100vCPU を下限とする Pod を作成して確認する。


```cpu-request-limit-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo-2
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr-2
    image: vish/stress
    resources:
      limits:
        cpu: "100"
      requests:
        cpu: "100"
    args:
    - -cpus
    - "2"
```

```bash
$k apply -f cpu-request-limit-2.yaml

# pending になっている
$ kubectl get pod cpu-demo-2 --namespace=cpu-example
NAME         READY   STATUS    RESTARTS   AGE
cpu-demo-2   0/1     Pending   0          15s

# CPU が足りず、スケジューリングされていない
$k describe pod cpu-demo-2 --namespace=cpu-example

・・・

Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  61s   default-scheduler  0/2 nodes are available: 2
 Insufficient cpu.

```

vCPU 100 の Node は存在しない為、スケジューリングされず、Pod は Pending の状態となる。

検証が終わったので削除しておく。

```bash
$kubectl delete pod cpu-demo-2 --namespace=cpu-example
```

# requests を指定しない場合の挙動について

ドキュメントを見ると以下のように書いてある。

[CPU制限を指定しない場合](https://kubernetes.io/ja/docs/tasks/configure-pod-container/assign-cpu-resource/#cpu%E5%88%B6%E9%99%90%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88)

+ コンテナのCPUリソースの使用量に上限がない状態となります。コンテナは実行中のノードで利用可能なすべてのCPUを使用できます。
+ CPU制限を与えられたnamespaceでコンテナを実行されると、コンテナにはデフォルトの制限値が自動的に指定されます。クラスターの管理者はLimitRangeによってCPU制限のデフォルト値を指定できます

EKS の環境では LimitRange はどのようになっているのだろうか。

```bash
$k get limitranges
No resources found in default namespace.
```

`default namesapace` には設定されてなさそうである。
この場合、`requests` を設定しない場合、どのようになるのか。

```bash
# deployment を作る
$kubectl create deployment test --image=nginx

# Pod 数を50 にする
$kubectl scale deployment test --replicas=50

# 確認
$kubectl get deployment test --watch
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
test   27/50   50           27          93s

$k get pods |grep Pending
test-7b66484b7d-2jknd   0/1     Pending   0          2m
test-7b66484b7d-4wsqn   0/1     Pending   0          119s
test-7b66484b7d-795gl   0/1     Pending   0          119s
test-7b66484b7d-92w2s   0/1     Pending   0          119s
test-7b66484b7d-94ggn   0/1     Pending   0          119s
test-7b66484b7d-9bvkm   0/1     Pending   0          119s
test-7b66484b7d-9vpqx   0/1     Pending   0          2m
test-7b66484b7d-b2zfk   0/1     Pending   0          2m
test-7b66484b7d-dthfr   0/1     Pending   0          2m
test-7b66484b7d-ghsq4   0/1     Pending   0          2m
test-7b66484b7d-h2w8b   0/1     Pending   0          119s
test-7b66484b7d-j8bj9   0/1     Pending   0          119s
test-7b66484b7d-k9666   0/1     Pending   0          119s
test-7b66484b7d-msf2g   0/1     Pending   0          119s
test-7b66484b7d-n5llc   0/1     Pending   0          2m
test-7b66484b7d-n6hmf   0/1     Pending   0          119s
test-7b66484b7d-nrkvz   0/1     Pending   0          119s
test-7b66484b7d-q9q88   0/1     Pending   0          119s
test-7b66484b7d-rdntb   0/1     Pending   0          119s
test-7b66484b7d-ttfm4   0/1     Pending   0          119s
test-7b66484b7d-wjkbt   0/1     Pending   0          119s
test-7b66484b7d-z5gxk   0/1     Pending   0          2m
test-7b66484b7d-zrptl   0/1     Pending   0          119s
```

こんな感じでいくつかの Pod は Pending になっており、50 の Pod は起動できなかった。
なぜか。

```bash
# Insufficient pods となっている
$ k describe pods test-7b66484b7d-zrptl

・・・

Events:
  Type     Reason            Age                  From               Message
  ----     ------            ----                 ----               -------
  Warning  FailedScheduling  79s (x2 over 2m31s)  default-scheduler  0/2 nodes a
re available: 2 Insufficient pods.

# Node を見てみる
$k describe node ip-192-168-74-169.ap-northeast-1.compute.internal

・・・

Capacity:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           20959212Ki
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3977916Ki
 pods:                        17
Allocatable:
 attachable-volumes-aws-ebs:  25
 cpu:                         2
 ephemeral-storage:           19316009748
 hugepages-1Gi:               0
 hugepages-2Mi:               0
 memory:                      3875516Ki
 pods:                        17


・・・

Non-terminated Pods:         (17 in total)

・・・

Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                    Requests   Limits
  --------                    --------   ------
  cpu                         110m (5%)  0 (0%)

```

今回の場合、 **Allocatable pods=17** であり、17 の Pod が起動しているので追加の Pod が起動できないという状況であると思われる。
リソースという意味では **Allocated resources** を見ると余裕があり、こちらの制限によってスケジューリングされた状況ではなく、実際に Event のメッセージが違うことからも判断が出来る。

以下で削除しておく

```bash
$k delete deployments test
deployment.extensions "test" deleted
```

# requests/limit のデフォルト値を設定する

[Configure Default CPU Requests and Limits for a Namespace](https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/cpu-default-namespace/)

LimitRange を設定して、`requests` がない場合でもデフォルト値が設定されるようにしてみる。

```cpu-defaults.yaml 
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container
```

```bash
$k apply -f cpu-defaults.yaml

$k get limitranges
NAME              CREATED AT
cpu-limit-range   2020-06-01T00:24:48Z
```

OK.

ではこの状態で `request` 及び `limits` 設定しない Pod を作成してみる。

```bash
# request/limits を指定しない Pod を作成する
$kubectl run nginx-pod --image=nginx:alpine --generator=run-pod/v1
pod/nginx-pod created

# LimitRange で指定したデフォルト値が設定されている
$k get pods -o json |less
・・・

            "spec": {
                "containers": [
                    {
                        "image": "nginx:alpine",
                        "imagePullPolicy": "IfNotPresent",
                        "name": "nginx-pod",
                        "resources": {
                            "limits": {
                                "cpu": "1"
                            },
                            "requests": {
                                "cpu": "500m"
                            }
                        },

```

以下で削除

```bash
$ k delete pod nginx-pod

$ k delete limitranges cpu-limit-range
```