【第四弾】Kubernetesベストプラクティス：Requests & Limits

# ベストプラクティス第四弾です

こんにちは、jackです。

今回でKubernetesベストプラクティスもいよいよ中盤です。
今回も前回同様、概念的な部分が多いので長くなりますがお付き合いください。

公式ブログはこちら
[Kubernetes best practices: Resource requests and limits](https://cloudplatform.googleblog.com/2018/05/Kubernetes-best-practices-Resource-requests-and-limits.html)

[Kubernetesベストプラクティスまとめ一覧](https://qiita.com/jackchuka/items/4ffc89ce31de5b6371ae) というページも作りました。

# TL;DR
* プロジェクトやチームが大きくなってきたら、**リソース管理をするべし**
* リソース管理には **Requests** と **Limits** がある
    * Requestsは**最低限割り振られるリソース（CPUやメモリ）**
    * Limitsは**割り振られるリソースの上限**
* CPUユニットは **`m` (millicores)**
* Memoryユニットは **`Mi` (mebibyte)**

# もっと詳しく

## リソース管理をするべし
Kubernetesを運用する上でリソースを十分に確保するのは重要なことです。例えば巨大なアプリケーションをリソースの限られたNode上で動かそうとすれば、メモリやCPUが足りなくなり正常に動作しなくなります。

また、大規模な開発をする上でも設定の人為的なミスであったり、プログラムの誤動作で不用意にリソースを奪い合い、パフォーマンスが低下するといったことが考えられます。

今回はこのような問題を未然に防ぐため、Kubernetesのrequestsとlimitsを見ていきましょう。

## RequestsとLimits
簡単にいうと、
Requestsは**コンテナに最低限割り振られるリソース（CPUやメモリ）**を指します。Limitsは言葉通り、**割り振られるリソースの上限**を指します。

もう少し詳しく言うと、Kubernetesは与えられたrequestを確保できるNodeにしかコンテナを生成しませんし、コンテナのリソースは与えられたlimitを超えてはいけません。また、limitがrequestを下回る設定はできません（エラーになります）。

ここで1つ注意する必要があるのが、requestsとlimitsはそれぞれコンテナ基準である点です。仮に、**1つのPodに複数のコンテナがある場合、requestsとlimitsは複数コンテナの合算**になります。


## コンテナでのリソース設定

リソースタイプは2つあります。CPUとMemoryです。
Kubernetesのスケジューラーはこれらを元にどこにPodを生成するか判断します。

GKEを使っている場合のデフォルトNamespaceでは以下の初期設定がされています。

|  |CPU  |Memory  |
|---|---|---|
|Requests  |0.1 Cores  |なし  |
|Limits  |なし  |なし  |

"Hello World"のようなアプリケーションならデフォルトでも構いませんが、通常Podを作成する場合はデフォルトではなく以下にのように指定してあげます。

```yml:pod.yml
containers:
  - name: container1
    image: busybox
    resources:
      requests:
        memory: "32Mi"
        cpu: "200m"
      limits:
        memory: "64Mi"
        cpu: "250m"
  - name: container2
    image: busybox
    resources:
      requests:
        memory: "96Mi"
        cpu: "300m"
      limits:
        memory: "192Mi"
        cpu: "750m"
```

上の設定では合計で`500m` CPUと`128` MiBのMemoryとなります。

### CPU
KubernetesのCPUの概念として、`millicores`というものがあります。
CPU1コアが1000 unitとして換算されるので、**1CPU = 1000m**です。
例えば、1/4コアのリソースが欲しい場合は `250m` となります。

*CPUのRequestがNodeよりも多い場合、そのPodは永遠に立ち上がらないので注意しましょう。

また、CPU Limitsで指定したリソースに達するとスロットリングします。パフォーマンスが低下したりしますが、terminateはしません。ここで前回お話したLivenessプローブ等でhealth checkするといったことも考えられます。

### Memory
メモリは bytes で表せます。bytesからpetabytesまで対応してますが、一般的には **mebibyte(Mi)** を使用することが多いでしょう。
CPUと同じようにメモリもNodeのリソースを超える場合はPodが生成されないので注意しましょう。

CPUとは違いMemoryはLimitを超えてしまうとPodが消失します。DeploymentやStatefulSet、DeamonSetを使用している場合、コントローラーが再度Podを生成してくれます。

## 名前空間でのリソース設定
一つのアプリケーションがクラスターの大部分のリソースを使ってしまうことが考えられます。それを防ぐために、Namespaceでのリソースを指定することも可能です。

### ResourceQuotas
Namespace上にリソースを定義するには以下のようにします。

```yml:quota.yml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: demo
spec:
  hard:
    requests.cpu: 500m
    requests.memory: 100Mib
    limits.cpu: 700m
    limits.memory: 500Mib
```

`requests.cpu`と`requests.memory`はそれぞれCPUとMemory Requestsの合計最大値です。要するに10m CPUのコンテナが50個、もしくは100m CPUが5つ、といった具合です。

`limits.cpu`と`limits.memory`はLimitsの合計最大値です。

### LimitRange
Quotaの他にLimitRangeというものがあります。
極端に大きなコンテナや、また逆に小さすぎるコンテナを作れないように、RequestsとLimitsを制約できます。

LimitRangeについては至ってシンプルなのでここではスキップします。
[原文](https://cloudplatform.googleblog.com/2018/05/Kubernetes-best-practices-Resource-requests-and-limits.html)もしくは[公式ドキュメント](https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/memory-default-namespace/#create-a-limitrange-and-a-pod)をどうぞ。

# まとめ

いかがでしたか？
すこし端折りましたが、基本的なことはまとめられたかと思います。

なにも考えずにKubernetesを使ってもうまく行ってしまうことが多いからこそ、内部を知って未然に思わぬ事故を防ぐことも大事ですね。

それでは、また次回！