こんにちは!SREチームの先輩エンジニアです。
Kubernetes(K8s)を使ったシステム運用、楽しくもスリリングな毎日を送っていることと思います。
「よし、新しいマイクロサービスをデプロイするぞ!」と意気込んでPodを立ち上げたはいいものの、特定のチームがメモリを爆食いするバグを踏んでしまい、同じクラスタで動いている他の重要なサービスまで巻き込んで全滅……。
……夜中にそんなアラートで叩き起こされた経験、ありませんか?私はあります。冷や汗が背中を伝うあの感覚は二度と味わいたくありません。
複数チームやサービスが同居する「マルチテナント環境」のKubernetesクラスタにおいて、リソースの奪い合いを防ぐことは、インフラエンジニアにとっての最重要防衛ラインです。
今回は、このカオスを防ぎ、クラスタの平和を守るための`ResourceQuota`と`LimitRange`という2つの強力な機能について、基礎から実戦的な設定まで優しく丁寧に解説していきます。これをマスターすれば、リソース枯渇の悪夢から解放されますよ!
—
1. なぜ「上限管理」が必要なのか?(ツールの役割)
Kubernetesのデフォルト状態は、いわば「無法地帯の食べ放題ビュッフェ」です。
ユーザーがPodを作成する際、CPUやメモリの要求量(リクエスト)や上限(リミット)を明示しなくても、クラスタが許す限りいくらでもリソースを消費できてしまいます。
もし、ある開発チームのアプリでメモリリークが発生したらどうなるでしょう?
そのアプリがノードのメモリをすべて食いつぶし、同じノードに相乗りしている無関係のチームのPodまでOSのOOM Killer(Out of Memory Killer)によって強制終了させられてしまいます。
これを防ぐために使うのが、以下の2つのリソースです。
1. LimitRange: ネームスペース内における、「個々のPodやコンテナ」単位の最小・最大リソース制限(およびデフォルト値)を定めます。
2. ResourceQuota: ネームスペース全体で消費できる「リソースの総量(合計値)」や、作成できるオブジェクトの数(Pod数やPersistentVolumeClaim数など)を制限します。
この2つを組み合わせることで、「各コンテナの暴走を防ぎ(LimitRange)」かつ「ネームスペース全体での使いすぎを防ぐ(ResourceQuota)」という、鉄壁のマルチテナント環境が完成します。
—
2. 基礎セットアップと「HelloWorld」的動作確認
百聞は一見にしかず。実際に手を動かして、その動きを確認してみましょう。
今回は、検証用のネームスペース `team-a` を作成し、そこに制限をかけていきます。
ステップ1: 検証用ネームスペースの作成
まずは、テナント(チーム)ごとの部屋を用意します。
kubectl create namespace team-a
ステップ2: LimitRangeの設定(デフォルト値と上限の強制)
開発者がうっかりリソース指定(requests / limits)を書き忘れたとき、あるいはデカすぎる値を設定しようとしたときに介入するルールを作ります。
以下のマニフェストを `limit-range.yaml` として保存してください。
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limit-range
namespace: team-a # 対象のネームスペースを指定
spec:
limits:
- type: Container
# 開発者が指定しなかった場合に自動付与されるデフォルト値
default:
cpu: “500m” # 0.5コア
memory: “512Mi” # 512メガバイト
defaultRequest:
cpu: “100m” # 0.1コア
memory: “128Mi” # 128メガバイト
# このネームスペース内で許可される1コンテナあたりの最大値
max:
cpu: “2”
memory: “2Gi”
# このネームスペース内で許可される1コンテナあたりの最小値
min:
cpu: “50m”
memory: “64Mi”
適用します。
kubectl apply -f limit-range.yaml
ステップ3: ResourceQuotaの設定(ネームスペース全体の総量制限)
次に、`team-a` 全体で使えるリソースの総パイを制限します。
以下のマニフェストを `resource-quota.yaml` として保存してください。
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-resource-quota
namespace: team-a
spec:
hard:
# ネームスペース全体で同時に持てるPodの最大数
pods: “10”
# ネームスペース全体のCPU要求量の合計上限
requests.cpu: “4”
# ネームスペース全体のメモリ要求量の合計上限
requests.memory: “8Gi”
# ネームスペース全体のCPU上限値の合計
limits.cpu: “8”
# ネームスペース全体のメモリ上限値の合計
limits.memory: “16Gi”
適用します。
kubectl apply -f resource-quota.yaml
—
3. 動作確認:ルールが正しく働くかテストする
設定が完了したら、実際に挙動をテストしてみましょう。
テストA: LimitRangeのデフォルト値付与テスト
リソース指定を一切含まない、シンプルなNginxのPodを `team-a` にデプロイしてみます。
kubectl run test-nginx –image=nginx –namespace=team-a
作成されたPodの詳細を確認してみましょう。
kubectl get pod test-nginx -n team-a -o yaml
YAMLの中の `resources` セクションを見てみてください。
開発者が何も指定しなかったにもかかわらず、`LimitRange` のおかげで自動的に以下のように `requests` と `limits` が付与されているはずです!
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
「うっかりリソース無制限Podを作ってしまった」というヒューマンエラーを、これで完全に根絶できます。
テストB: ResourceQuotaの上限超過テスト
今度は、ResourceQuotaの制限を超える巨大なリソース要求を持つPodを作ってみようとします(あるいは、大量のPodを作ってみます)。
今回はテスト用に、上限を超えるメモリを要求するPodを定義してみます。
apiVersion: v1
kind: Pod
metadata:
name: memory-hogger
namespace: team-a
spec:
containers:
- name: hogger
image: busybox
command: [“sleep”, “3600”]
resources:
requests:
memory: “10Gi” # ResourceQuotaで設定した8Giを超えている!
limits:
memory: “10Gi”
これを `kubectl apply` してみると……?
Error from server (Forbidden): error when creating “huge-pod.yaml”: pods “memory-hogger” is forbidden: exceeded quota: team-a-resource-quota, requested: requests.memory=10Gi, used: 128Mi, limited: 8Gi
「exceeded quota(クォータを超過しました)」という美しいエラーメッセージと共に、Kubernetesがデプロイをピシャリと拒絶してくれます。
クラスタを守る頼もしいゲートキーパーが機能している瞬間です。
—
4. 実務でよくあるトラブルシューティング
現場でこれらの設定を運用していると、いくつか「おや?」と躓くポイントに出会います。よくあるトラブルと処方箋をシェアしておきます。
トラブル1: 「リソースを書いていないPod」でデプロイが弾かれる?
> 現象: LimitRangeを設定していない(または `defaultRequest` が無い)環境で、マニフェストに `resources` を書き忘れた開発者のPodが `Forbidden: restricted by LimitRange` で弾かれる。
- 原因と対策:
Kubernetesの原則として、ResourceQuotaが存在するネームスペースでは、原則としてすべてのコンテナに `requests` と `limits` の明記が義務付けられます(さもないと消費量を計算できないため)。
必ず `LimitRange` の `default` と `defaultRequest` をセットで定義し、開発者が書き忘れても自動補完される仕組みをセットで導入しましょう。
トラブル2: PVC(PersistentVolumeClaim)の容量制限に引っかかる
> 現象: Podは起動するのに、データベースなどのステートフルなアプリをデプロイするとエラーになる。
- 原因と対策:
ResourceQuotaは、CPUやメモリだけでなく、ストレージの容量やPersistentVolumeClaimの数も制限できます。
もし開発チームが巨大なDBストレージ(例: 100Gi)を勝手に切り出そうとして弾かれている場合は、ResourceQuotaに `requests.storage` が設定されていないか確認し、チームごとの割当量を見直してください。
spec:
hard:
requests.storage: “50Gi” # ストレージの総量も制限可能
—
おわりに
今回は、Kubernetesの `ResourceQuota` と `LimitRange` を使って、マルチテナント環境のリソース枯渇を防ぐ上限管理術を解説しました。
- LimitRange で「個々の暴走」をせき止め、
- ResourceQuota で「チーム全体の使いすぎ」をコントロールする。
この2つをネームスペースごとに適切に設計・配置するだけで、クラスタの安定性は劇的に向上します。トラブルシューティングに怯える夜とはおさらばして、ぜひ明日のインフラ改善に取り入れてみてください。
あなたのKubernetesライフが、より堅牢で快適なものになりますように。それではまた!