KubernetesのResourceQuotaの動作確認

KubernetesのResourceQuotaを設定した場合の挙動確認のメモ。IBM Cloud Private-CE 3.1で確認。

## 準備

メモリに負荷をかけるためのイメージを用意する。コンテナを起動するとstressコマンドで256MBメモリを消費する。

```Dockerfile:Dockerfile
FROM ubuntu:16.04

RUN apt-get update && \
    apt-get install -y stress && \
    rm -rf /var/lib/apt/lists/*

CMD ["stress", "-m","1", "--vm-bytes" ,"256M","--vm-hang","0","-q"]
```

イメージをビルドする。

```
docker build -t sotoiwa540/stress:1.0 .
```

レジストリにアップロードする。ここではDockerHubにアップロードする。

```
docker login
docker push sotoiwa540/stress:1.0
```

## デプロイ確認

今回はsugiというNamespaceにデプロイするのでnamespaceを作成する。

```
kubectl create ns sugi
```

マニフェストを作成してデプロイする。

```yaml:stress-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stress
  namespace: sugi
spec:
  selector:
    matchLabels:
      app: stress
  replicas: 10
  template:
    metadata:
      labels:
        app: stress
    spec:
      containers:
      - name: stress
        image: sotoiwa540/stress:1.0
        imagePullPolicy: Always
```

```
kubectl apply -f stress-deploy.yaml
```

Podを確認する。

```shell-session
$ kubectl get deploy -n sugi
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
stress    10        10        10           10          2m
$ kubectl get po -n sugi
NAME                     READY     STATUS    RESTARTS   AGE
stress-9bd965f55-5z27g   1/1       Running   0          2m
stress-9bd965f55-98rqf   1/1       Running   0          2m
stress-9bd965f55-9bfqd   1/1       Running   0          2m
stress-9bd965f55-brt6p   1/1       Running   0          2m
stress-9bd965f55-gqnwp   1/1       Running   0          2m
stress-9bd965f55-hm7kx   1/1       Running   0          2m
stress-9bd965f55-lwtns   1/1       Running   0          2m
stress-9bd965f55-t2fj9   1/1       Running   0          2m
stress-9bd965f55-t7hrn   1/1       Running   0          2m
stress-9bd965f55-wttz6   1/1       Running   0          2m
$
```

リソースの使用状況を確認する。

```shell-session
$ kubectl top pod -n sugi
NAME                     CPU(cores)   MEMORY(bytes)
stress-9bd965f55-5z27g   0m           257Mi
stress-9bd965f55-98rqf   0m           258Mi
stress-9bd965f55-9bfqd   0m           258Mi
stress-9bd965f55-brt6p   0m           257Mi
stress-9bd965f55-gqnwp   0m           258Mi
stress-9bd965f55-hm7kx   0m           257Mi
stress-9bd965f55-lwtns   0m           257Mi
stress-9bd965f55-t2fj9   0m           257Mi
stress-9bd965f55-t7hrn   0m           258Mi
stress-9bd965f55-wttz6   0m           257Mi
$
```

一旦デプロイメントを削除する。

```
kubectl delete -f stress-deploy.yaml
```

## requests.memory

はじめにrequests.memoryを試す。

```yaml:requests-memory-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: requests-memory
  namespace: sugi
spec:
  hard:
    requests.memory: 2G
```

```
kubectl apply -f requests-memory-quota.yaml
```

確認する。

```shell-session
$ kubectl describe quota requests-memory -n sugi
Name:            requests-memory
Namespace:       sugi
Resource         Used  Hard
--------         ----  ----
requests.memory  0     2G
$
```

この状態でsterssをデプロイする。Podが作成されない。Pending状態になるわけではなく、そもそもPodがスケジュールされないことに注意。

```shell-session
$ kubectl apply -f stress-deploy.yaml
deployment.apps/stress created
$ kubectl get deploy -n sugi
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
stress    10        0         0            0           27s
$ kubectl get po -n sugi
No resources found.
$
```

各コンテナに`.resources.requests.memory`が設定されていないと、Podをスケジュールできないため。

Deploymentを削除する。

```
kubectl delete -f stress-deploy.yaml
```

`.resources.requests.memory`を追加したバージョンのDeployment定義を作成する。


```yaml:stress-requests-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stress-requests
  namespace: sugi
spec:
  selector:
    matchLabels:
      app: stress
  replicas: 10
  template:
    metadata:
      labels:
        app: stress
    spec:
      containers:
      - name: stress
        image: sotoiwa540/stress:1.0
        imagePullPolicy: Always
        resources:
          requests:
            memory: "300Mi"
```

デプロイする。今度はPodが作成され、Namespaceへのrequests制限が2GBなので、300MB*6つのPodしか作成されていないことが確認できる。

```shell-session
$ kubectl apply -f stress-requests-deploy.yaml
deployment.apps/stress-requests created
$ kubectl get deploy -n sugi
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
stress-requests   10        6         6            6           33s
$ kubectl get po -n sugi
NAME                              READY     STATUS    RESTARTS   AGE
stress-requests-c75fbc58c-284md   1/1       Running   0          39s
stress-requests-c75fbc58c-7p6v7   1/1       Running   0          39s
stress-requests-c75fbc58c-7wmc8   1/1       Running   0          39s
stress-requests-c75fbc58c-gnn67   1/1       Running   0          39s
stress-requests-c75fbc58c-m4xnr   1/1       Running   0          39s
stress-requests-c75fbc58c-n7h8d   1/1       Running   0          40s
$
```

DeploymentとResourceQuotaを削除する。

```
kubectl delete -f stress-requests-deploy.yaml
kubectl delete -f requests-memory-quota.yaml
```

## limits.memory

続いてlimits.memoryを試す。

```yaml:limits-memory-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: limits-memory
  namespace: sugi
spec:
  hard:
    limits.memory: 2G
```

```
kubectl apply -f limits-memory-quota.yaml
```

確認する。

```shell-session
$ kubectl describe quota limits-memory -n sugi
Name:          limits-memory
Namespace:     sugi
Resource       Used  Hard
--------       ----  ----
limits.memory  0     2G
$
```

先ほど使用したDeployment定義をデプロイしてみる。Podが作成されない。

```
$ kubectl apply -f stress-deploy.yaml
deployment.apps/stress created
$ kubectl apply -f stress-requests-deploy.yaml
deployment.apps/stress-requests created
$ kubectl get deploy -n sugi
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
stress            10        0         0            0           30s
stress-requests   10        0         0            0           7s
$ kubectl get po -n sugi
No resources found.
$
```

コンテナに`.resources.limits.memory`が設定されていないと、Podをスケジュールできないため。

Deploymentを削除する。

```
kubectl delete -f stress-deploy.yaml
kubectl delete -f stress-requests-deploy.yaml
```

`.resources.limits.memory`を追加したバージョンのDeployment定義を作成する。


```yaml:stress-limits-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stress-limits
  namespace: sugi
spec:
  selector:
    matchLabels:
      app: stress
  replicas: 10
  template:
    metadata:
      labels:
        app: stress
    spec:
      containers:
      - name: stress
        image: sotoiwa540/stress:1.0
        imagePullPolicy: Always
        resources:
          limits:
            memory: "300Mi"
```

この定義でデプロイする。Podが作成される。

```shell-session
$ kubectl apply -f stress-limits-deploy.yaml
deployment.apps/stress-limits created
$ kubectl get deploy -n sugi
NAME            DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
stress-limits   10        6         6            1           9s
$ kubectl get po -n sugi
NAME                             READY     STATUS    RESTARTS   AGE
stress-limits-8548966749-jkwfr   1/1       Running   0          18s
stress-limits-8548966749-l5wtz   1/1       Running   0          18s
stress-limits-8548966749-mshg5   1/1       Running   0          18s
stress-limits-8548966749-v9l92   1/1       Running   0          18s
stress-limits-8548966749-vwtbb   1/1       Running   0          18s
stress-limits-8548966749-zh4g8   1/1       Running   0          19s
$
```

6つしか作成されない。実際のメモリ使用量としては、あと1つPodを作成できるはずである。

```
$ kubectl top pod -n sugi
NAME                             CPU(cores)   MEMORY(bytes)
stress-limits-8548966749-jkwfr   1m           257Mi
stress-limits-8548966749-l5wtz   1m           258Mi
stress-limits-8548966749-mshg5   1m           257Mi
stress-limits-8548966749-v9l92   1m           257Mi
stress-limits-8548966749-vwtbb   0m           257Mi
stress-limits-8548966749-zh4g8   1m           257Mi
$
```

つまりResourceQuotaに対してlimitsを設定するというのは、実際のメモリ使用量に対して制限を掛けているわけではなく、requestsの場合と同様に、単に各コンテナで設定されているlimitsの合計をチェックしているだけである。

## 補足

各コンテナでRequestsとLimitsを定義する代わりに、LimitRangeでデフォルト値を定義することもできる。

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

## 参考リンク

[Configure Memory and CPU Quotas for a Namespace](
https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/quota-memory-cpu-namespace/)

[Resource Quotas](https://kubernetes.io/docs/concepts/policy/resource-quotas/)