Kubernetesのリミットとリクエストについて理解する

By 清水 孝郎 - NOVEMBER 20, 2022

SHARE:

本文の内容は、2022年11月18日にJAVIER MARTÍNEZが投稿したブログ(https://sysdig.com/blog/kubernetes-limits-requests/)を元に日本語に翻訳・再構成した内容となっております。

Kubernetesでコンテナを扱う場合、どのようなリソースがどのように必要なのかを知っておくことが重要です。あるプロセスは、他のプロセスよりも多くのCPUやメモリを必要とします。中にはクリティカルなものもあり、決して飢えさせないようにしなければなりません。

Kubernetesでは、コンテナが使用するリソースの最大量Limitsと定義しています。

一方、Requestsは、コンテナ用に確保されるリソースの最低保証量です。

それを知った上で、コンテナとPodを適切に設定することで、両者のメリットを享受できるようになります。

今回は、以下をご紹介します。:

  • Kubernetesにおけるリソース
  • Kubernetesのリクエスト
  • Kubernetesの制限
  • ネームスペース ResourceQuota
  • ネームスペース LimitRange

Kubernetesのリソース

KubernetesにおけるCPU設定

CPUとは、コンピューティングの処理時間の単位で、コアで測定されます。

  • コアより小さい量を表すためにミリコア(m)を使用することができます(例:500mならコアの半分)。
  • 最小値は1mです。
  • 1つのノードが複数のコアを利用できる場合があるため、CPU > 1を要求することができます。
  • CPU は圧縮可能なリソースであり、すべての要求を満たすために引き伸ばすことができます。CPUを要求するプロセスが多すぎる場合、そのうちのいくつかはスロットルされます。

Kubernetesにおけるメモリ設定

Kubernetesでは、メモリはバイトで測定されます。

  • E、P、T、G、M、kでエクサバイト、ペタバイト、テラバイト、ギガバイト、メガバイト、キロバイトを表しますが、一般的に使われるのは最後の4つだけです。(例:500M、4G)
  • 警告:メモリに小文字のmを使わないでください(これはミリバイトを表しますが、とんでもなく低い数字です)。
  • MiでMebibyteを定義し、残りはEi、Pi、Tiで定義します(例:500Mi)。

1メビバイト(およびその類似のキビバイト、ギビバイト、…)は、2の20乗バイトです。メートル法のキロ、メガの定義との混同を避けるために作られたものです。キロやメガが1000の倍数であるのに対し、この表記はバイトの標準的な定義であるため、この表記を使うべきでしょう。

メモリ非圧縮性リソースです。つまり、CPUと同じように引き伸ばすことができません。プロセスが動作するのに十分なメモリを得られない場合、そのプロセスは強制終了されます。

Kubernetesリクエスト

Kubernetesでは、コンテナが使用するリソースの最低保証量としてリクエストを定義しています。

基本的には、コンテナが消費するリソースの最小量を設定することになります。

Podがスケジュールされると、kube-schedulerはKubernetes requestsをチェックして、Pod内のすべてのコンテナに対して最低でもその量を満たすことができる特定のNodeに割り当てるようにします。要求された量が利用可能なリソースよりも多い場合、Podはスケジュールされず、Pending状態のままとなります。

この例では、100mコアのCPUと4Miのメモリを要求するように設定しています:

resources:
requests:
cpu: 0.1
memory: 4Mi

リクエストを使用する:

  • NodeにPodを割り当てる際、そのPod内のコンテナによる指示されたリクエストが満たされるようにする。
  • 実行時には、そのPod内のコンテナに対して、指示された量のリクエストが最低限保証される。

メモリリクエスト

あるコンテナが要求量以上のメモリを消費し、そのNodeの容量に問題がある場合、kubeletはそのコンテナのPodをevictさせることがあります。

CPUリクエスト

CPUリクエストは、特定のコンテナに対して付与されるCPUのクォータです。

内部的には、LinuxのCFS(Completely Fair Scheduler)を利用して実装されています。

CPU requests diagram
Pod AとBは、CPU時間を保証するリクエストを持っています。Pod Cはリクエストがないためスロットルされるが、最終的には処理されます。

KubernetesはCPUの消費を理由にPodをevictさせることはありませんが、Nodeのキャパシティに問題がある場合、想定よりも少ないCPU時間を割り当てる必要があるため、パフォーマンスに影響が出ます。これはスロットリングと呼ばれ、基本的にはプロセスがCPUを再び使用できるようになるまで待つ必要があることを意味します。

Kubernetesリミット

Kuberneteでは、コンテナによって使用されるリソースの最大量としてリミットを定義しています。

これは、コンテナが表示されたメモリ量またはCPU量以上を消費することはできないということを意味します。

resources:
limits:
cpu: 0.5
memory: 100Mi

リミットを使用する:

  • NodeにPodを割り当てる際。リクエストが設定されていない場合、デフォルトでKubernetesはリクエスト=リミットを割り当てます。
  • 実行時に、KubernetesはPod内のコンテナがリミットで示された以上のリソースを消費していないことを確認します。

メモリリミット

メモリリミットは、コンテナに対して許可される最大メモリ量を設定します。リミットを超えた場合、KubernetesはOOM(Out of Memory)によりプロセスを強制終了します。

Memory Requests and Limits in Kubernetes diagram
Top Podは1Gのリクエストセットを持ち、Bottom Podは1Gのリミットを持つ。Top Podが例外的にそれ以上のメモリを要求した場合、ノードは提供することができます。Bottom Podは1G以上消費することは絶対にできないので、killされる

CPUリミット

CPUリミットは、コンテナに対して許容されるCPUの最大量を設定します。これを超えた場合、Kubernetesはプロセスをスロットルし、その実行を遅らせます。

Kubernetesでリソースの使用量を制御するためにリミットを使用すべきケースは非常に少ないです。なぜなら、飢餓を回避したい(すべての重要なプロセスがそのシェアを得ることを保証したい)のであれば、そもそもリクエストを使用すべきなのです。

リミットを設定することで、例外的なケースでプロセスが追加のリソースを取得するのを防ぐだけで、メモリの場合はOOM kill、CPUの場合はThrottlingが発生します。



実用的な例

例えば、4コア、16GB RAMのノードでクラスターを運用しているとします。多くの情報を抽出することができます。

Kubernetes Limits and Requests practical example

  1. Podの実効リクエストは、メモリが400MiB、CPUが600ミリコアです。Podをスケジュールするために、十分な空き割り当て可能領域を持つノードが必要です。
  2. redisコンテナのCPUシェアは512、busyboxコンテナのCPUシェアは102となります。Kubernetesは常に各コアに1024のシェアを割り当てるので、redis: 1024 * 0.5 cores ≅ 512 and busybox: 1024 * 0.1cores ≅ 102となります。
  3. Redisコンテナは600MB以上のRAMを確保しようとするとOOM killされ、Podが失敗する可能性が高いです。
  4. Redisは、100msごとに100ms以上のCPUを使おうとするとCPUスロットルにかかり、パフォーマンスが低下します(4コアあるので、利用可能時間は100msごとに400msとなります)。
  5. Busybox コンテナは、200MB を超える RAM を割り当てようとすると OOM kill され、その結果 Pod がフェイルします。
  6. 100msごとに30ms以上のCPUを使用しようとすると、BusyboxはCPUスロットルにかかり、パフォーマンスが低下します。

ネームスペースResourceQuota

ネームスペースのおかげで、Kubernetesのリソースをテナントとも呼ばれる異なるグループに分離することができます。

ResourceQuotasを使用すると、ネームスペース全体にメモリやCPUの制限を設定し、その中のエンティティがその量より多く消費できないようにすることができます。
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-demo
spec:
hard:
requests.cpu: 2
requests.memory: 1Gi
limits.cpu: 3
limits.memory: 2Gi

  • requests.cpu: このネームスペースのすべてのリクエストの合計に対するCPUの最大量
  • requests.memory: このネームスペースのすべてのリクエストの合計に対するMemoryの最大量
  • limits.cpu: このネームスペースにあるすべてのリミットの合計に対する CPU の最大値。
  • limits.memory: このネームスペース内のすべてのリミットの合計の最大メモリ量
そして、ネームスペースに適用します:

kubectl apply -f resourcequota.yaml --namespace=mynamespace

ネームスペースの現在のResourceQuotaを一覧が確認できます:

kubectl get resourcequota -n mynamespace

ネームスペース内の特定のリソースに対してResourceQuotaを設定した場合、そのネームスペース内のすべてのPodに対して、それに応じてリミットまたはリクエストを指定する必要があることに注意してください。そうしないと、Kubernetesは “failed quota “というエラーを返します:

Error from server (Forbidden): error when creating "mypod.yaml": pods "mypod" is forbidden: failed quota: mem-cpu-demo: must specify limits.cpu,limits.memory,requests.cpu,requests.memory

万が一、現在のResourceQuotaを超えるコンテナリミットやリクエストで新しいPodを追加しようとした場合、Kubernetesは“exceeded quota”エラーを返します。

Error from server (Forbidden): error when creating "mypod.yaml": pods "mypod" is forbidden: exceeded quota: mem-cpu-demo, requested: limits.memory=2Gi,requests.memory=2Gi, used: limits.memory=1Gi,requests.memory=1Gi, limited: limits.memory=2Gi,requests.memory=1Gi

ネームスペース LimitRange

ResourceQuotasは、ネームスペースに割り当て可能なリソースの総量を制限したい場合に便利です。しかし、中の要素にデフォルト値を与えたい場合はどうなるのでしょうか?

LimitRangesは、ネームスペース内の各エンティティのリソース設定を制限するKubernetesのポリシーです。

apiVersion: v1
kind: LimitRange
metadata:
name: cpu-resource-constraint
spec:
limits:
- default:
cpu: 500m
defaultRequest:
cpu: 500m
min:
cpu: 100m
max:
cpu: "1"
type: Container

  • default::何も指定されていない場合、作成されるコンテナはこの値を持ちます。
  • min:作成されるコンテナは、これより小さいリミットやリクエストを持つことはできません。
  • max:作成されるコンテナには、これより大きなリミットやリクエストは設定できません。

後で、リクエストやリミットが設定されていない新しいPodを作成すると、LimitRangeは自動的にそのすべてのコンテナにこれらの値を設定します。

Limits:
cpu: 500m
Requests:
cpu: 100m

ここで、1200Mを上限とするPodを新規に追加することを想像してください。以下のようなエラーが表示されます。

Error from server (Forbidden): error when creating "pods/mypod.yaml": pods "mypod" is forbidden: maximum cpu usage per Container is 1, but limit is 1200m

デフォルトでは、LimitRangesを設定しない場合でも、Pod内のすべてのコンテナは、実質的に100m CPUの要求を持つことになることに注意してください。

まとめ

Kubernetesクラスターに最適なリミットを選択することは、エネルギー消費とコストの両方を最適化するための鍵です。

オーバーサイズにしたり、Podに多くのリソースを割り当てたりすると、コストが高騰する可能性があります。

CPUやMemoryが少ないと、アプリケーションが正常に動作しなかったり、Podがevictする可能性があります。

前述の通り、Kubernetesのリミットは、非常に特殊な状況を除いては、良いことよりも悪いことを引き起こす可能性があるため、使用すべきではありません。Out of Memoryの場合はコンテナが強制終了され、Out of CPUの場合はスロットルされる可能性があります。

リクエストについては、プロセスがリソースの保証されたシェアを得ることを保証する必要がある場合に使用します。


Sysdig MonitorでKubernetesのリソースを適切なサイズにする

Sysdig Monitorの新機能であるコストアドバイザーを使用すると、Kubernetesのコストを最適化することができます。

  • メモリリクエスト
  • CPUリクエスト

Sysdig Advisorは、ライブログ、パフォーマンスデータ、推奨される改善手順により、平均解決時間(MTTR)を短縮します。Kubernetesのトラブルシューティングのための簡単なボタンです!



30日間無料でお試しください!

https://youtu.be/2mbq9DxrsH4