K8s 中級 #7 RBAC / NetworkPolicy / ResourceQuota — セキュリティとリソースポリシー
K8s 中級シリーズの最後の記事です。#1 から #6 までワークロードを運用するモデルを 1 層ずつ積み上げてきました。コントローラ 4 種、永続データ、外部入口、リソース要求・上限、ヘルスチェック、オートスケーリングまで — Pod 1 つを安定的に立てて負荷に合わせて増減させる 1 サイクルが手に入りました。この記事ではその上にもう 1 層加えるテーマを扱います — 1 つのクラスタに複数のチーム・環境が一緒に住む状況のセキュリティとリソース統制 です。キーワードは 3 つです。RBAC(誰が何をできるか)、NetworkPolicy(どんなトラフィックが通るか)、ResourceQuota(どれくらい作れるか)。3 つのオブジェクトすべてが namespace 単位ポリシーである共通点があり、基礎 #7 で「Namespace 自体はセキュリティ境界ではない」と触れたその空白をこの 3 つが埋めます。シリーズの最後の記事なので、7 編全体の振り返りと次のトラック(K8s 上級)の予告も一緒に入れます。
このシリーズは K8s 中級 7 編です。
- #1 StatefulSet / DaemonSet / Job / CronJob — Deployment ではない他のコントローラ
- #2 PV / PVC / StorageClass — 永続データモデル
- #3 Ingress と Ingress Controller — 外部入口
- #4 resources.requests / limits — Pod のリソース要求と上限
- #5 Health check — liveness / readiness / startup probe
- #6 オートスケーリング — HPA / VPA / Cluster Autoscaler
- #7 RBAC / NetworkPolicy / ResourceQuota — セキュリティとリソースポリシー ← この記事
3 つのポリシーの共通座標 — namespace 単位 #
基礎 #7 で Namespace を扱うときに 1 行残しておきました — Namespace 自体はセキュリティ境界ではありません。 オブジェクト名を分けてくれる論理的な区画にすぎず、本当の隔離はその上に乗るポリシーオブジェクトたちが作るという話でした。その上に乗るポリシーの本体がこの記事のテーマ 3 つです。
| 次元 | オブジェクト | 説明 |
|---|---|---|
| 権限 | Role / ClusterRole / RoleBinding / ClusterRoleBinding | 誰がどのオブジェクトにどの動詞を使えるか |
| トラフィック | NetworkPolicy | どの Pod がどの Pod と通信できるか |
| リソース | ResourceQuota / LimitRange | 1 つの namespace がどれくらい作れるか |
3 つのオブジェクトすべてが namespace 単位で適用されるか(NetworkPolicy、ResourceQuota、LimitRange、Role/RoleBinding)、namespace に結合して適用されます(ClusterRole + RoleBinding の組み合わせ)。1 つのクラスタの上に dev / staging / prod、あるいはチーム A / チーム B が一緒に住むマルチテナント運用の隔離は、この 3 つの次元が合わさったときにはじめて完成します。
この記事の流れは単純です。権限から — RBAC をもっとも詳しく扱い、次にトラフィック(NetworkPolicy)、次にリソース(ResourceQuota / LimitRange)の順で行きます。
RBAC — 誰が何をできるか #
RBAC(Role-Based Access Control)は K8s API 呼び出し単位で権限を表現するモデルです。kubectl get pods、kubectl create deployment、kubectl delete secret のようなすべての動作は結局 K8s API サーバへの HTTP リクエストです。RBAC はそのリクエスト 1 件 1 件に対して「この主体がこの動詞をこのリソースに使えるか」を検査します。
モデルは 4 つのオブジェクトで表現されます。権限の束(Role / ClusterRole)と、その権限と主体をつなぐオブジェクト(RoleBinding / ClusterRoleBinding)です。
| オブジェクト | 何か | スコープ |
|---|---|---|
Role | 権限の束 (verbs + resources) | namespace |
ClusterRole | 権限の束 (verbs + resources) | クラスタ (すべての namespace で共有) |
RoleBinding | Role または ClusterRole を主体に付与 | namespace |
ClusterRoleBinding | ClusterRole を主体に付与 | クラスタ全域 |
ここで 1 つ微妙な部分があります。RoleBinding は ClusterRole を参照することもできます。 ClusterRole は権限の束で、RoleBinding はその束をどの namespace で誰に与えるかを決定するオブジェクトなので、「標準 ClusterRole(例: view、edit)を 1 つ作っておいて namespace ごとに RoleBinding で別の人に付与」するパターンが運用でもっとも一般的です。
Subject — 権限を受ける側 #
権限を付与される主体(Subject)は 3 つです。
- User — 人間ユーザー。K8s 自体にはユーザーデータベースがなく、外部認証(OIDC、x509 クライアント証明書、クラウド IAM マッピングなど)がユーザー名を伝えてくれます。
- Group — ユーザーの束。User と同様に外部認証がグループ情報を K8s に伝えます。
- ServiceAccount — Pod が K8s API にアクセスするときに使う ID。人ではなくワークロードのアイデンティティです。
この 3 つのうち K8s マニフェストで直接作って管理するのは ServiceAccount がほぼ唯一です。User と Group は外部認証の産物なので K8s 内にオブジェクトとして存在しません。RoleBinding の subjects フィールドに書かれるだけです。
ServiceAccount — Pod の ID #
すべての Pod は何かの ServiceAccount の ID を持ってクラスタに住んでいます。マニフェストに spec.serviceAccountName を書かないと、その namespace の default ServiceAccount が自動で結ばれます。Pod 内で kubectl のようなツールで K8s API にアクセスすると、その呼び出しはその ServiceAccount の権限で実行されます。
kubectl get serviceaccounts -n defaultNAME SECRETS AGE
default 0 3dデフォルトの default ServiceAccount にはどんな権限も付与されていません。RBAC を適用した運用クラスタで Pod 内から kubectl get pods を試みると次のメッセージが出るのが正常です。
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:default" cannot list resource "pods" in API group "" in the namespace "default"このメッセージを見て RoleBinding で権限を結ぶと、同じコマンドが通ります。次の例でその流れを追います。
RBAC マニフェスト 1 セット #
もっとも単純なシナリオをマニフェスト 1 枚にまとめてみます。dev namespace に pod-reader という ServiceAccount を作り、その SA に「Pod を読める権限」を付与します。そしてその SA を使う Pod 内で kubectl get pods が通るかを確認する流れです。
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader
namespace: dev
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: dev
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader
namespace: dev
subjects:
- kind: ServiceAccount
name: pod-reader
namespace: dev
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io3 つのオブジェクトの責務を 1 行ずつ押さえておきます。
- ServiceAccount
pod-reader—devnamespace の新しい ID。まだどんな権限もありません。 - Role
pod-reader—podsリソースに対してget、list、watch動詞を許可する権限の束。apiGroups: [""]はコア API グループ(Pod、Service、ConfigMap など)を指す。 - RoleBinding
pod-reader— Role と ServiceAccount をつなぐオブジェクト。subjectsが受ける側、roleRefが与える側。
この 3 つのオブジェクトを一度に適用してから、その ServiceAccount を使う Pod を立ててみます。
apiVersion: v1
kind: Pod
metadata:
name: reader
namespace: dev
spec:
serviceAccountName: pod-reader
containers:
- name: kubectl
image: bitnami/kubectl:1.30
command: ["sleep", "3600"]kubectl apply -f rbac-pod-reader.yaml
kubectl apply -f reader-pod.yaml
kubectl exec -n dev reader -- kubectl get pods -n devNAME READY STATUS RESTARTS AGE
reader 1/1 Running 0 30s同じ Pod 内で権限のない動作を試みると、止められることも確認できます。
kubectl exec -n dev reader -- kubectl create deployment nginx --image=nginx -n deverror: failed to create deployment: deployments.apps is forbidden: User "system:serviceaccount:dev:pod-reader" cannot create resource "deployments" in API group "apps" in the namespace "dev"pod-reader Role は Pod の get/list/watch だけ与えており、Deployment の create は与えていないので、ちょうどそこで拒否されます。
verbs と resources の核心 #
Role / ClusterRole の rules でよく使われる verbs を表にまとめると次のとおりです。
| verb | 意味 |
|---|---|
get | 単一オブジェクト照会 |
list | オブジェクトリスト照会 |
watch | 変更イベントの購読 |
create | オブジェクト生成 |
update | オブジェクト更新(全体) |
patch | オブジェクト部分更新 |
delete | オブジェクト削除 |
deletecollection | マッチするオブジェクトの一括削除 |
読み取りだけ許可するなら get、list、watch の 3 つを一緒に与えます。kubectl get のようなコマンドが内部的に list を使い、watch は informer キャッシュの更新に使われるからです。書き込みまで許可するなら create、update、patch、delete を追加します。
resources は pods、services、configmaps のように複数形の名前を書きます。apiGroups はそのリソースが属する API グループです。コアグループ(Pod / Service / ConfigMap / Secret など)は [""](空文字列)、Deployment / StatefulSet / DaemonSet は ["apps"]、Job / CronJob は ["batch"]、Ingress は ["networking.k8s.io"] です。リソースがどのグループに属するかは kubectl api-resources で一度に確認できます。
kubectl api-resourceskubectl auth can-i — 権限検証 #
RBAC を触っておくと次の質問がすぐに続きます — 「では、このユーザーは本当にこの動作ができるのか?」 マニフェストを長く見ても結果が一目で入ってこないことが多いです。K8s がこの質問に直接答えてくれるコマンドが kubectl auth can-i です。
kubectl auth can-i create pods -n dev
kubectl auth can-i delete deployments -n prodyes / no のどちらかが出力されます。別のユーザーや ServiceAccount の立場で問いたいなら --as オプションで impersonation を使います。
kubectl auth can-i create pods --as=alice -n dev
kubectl auth can-i list secrets --as=system:serviceaccount:dev:pod-reader -n dev--as オプション自体にも権限が必要です(impersonation 権限)。運用クラスタの admin が新しく作った RBAC ポリシーが意図どおりに動作するかを確認するときにもっともよく使われるパターンです。すべての権限を一度に見たいなら --list を付けます。
kubectl auth can-i --list --as=alice -n devよくある罠 — 広すぎる ClusterRole #
RBAC を初めて適用するときにもっともよく犯すミスが 面倒さに負けて ClusterRole cluster-admin を ServiceAccount に結ぶ パターンです。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: app-admin
subjects:
- kind: ServiceAccount
name: app
namespace: dev
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.iocluster-admin は K8s API 全体に対する無制限の権限です。この ClusterRoleBinding が適用された ServiceAccount のトークンが Pod 1 つでも外部に漏れれば、そのトークンでクラスタのすべての namespace のすべての Secret を読み、すべてのオブジェクトを削除できます。コンテナイメージ内のライブラリに脆弱性が 1 つあるだけでもその 1 点がクラスタ全体の事故に広がります。
運用の標準原則は 最小権限 (least privilege) です。ワークロードが本当に必要な verbs と resources だけを正確に絞って Role に束ね、その Role を RoleBinding で付与します。K8s が事前に作っておく標準 ClusterRole のセットがあり、それを RoleBinding で namespace に限定して使うパターンが一般的な出発点です。
| ClusterRole | 説明 |
|---|---|
view | namespace のオブジェクトを読み取り。Secret は除外 |
edit | view + ほとんどのオブジェクトの書き込み。Role/RoleBinding など RBAC オブジェクトは除外 |
admin | edit + その namespace 内の RBAC オブジェクト管理 |
cluster-admin | クラスタ全体に無制限 |
view / edit / admin を RoleBinding で特定 namespace に限定すれば、人・チーム・サービスに適切な権限の束をすばやく付与でき、cluster-admin は クラスタ運用者ほんの一握り にだけ ClusterRoleBinding で付与する形が標準です。
automountServiceAccountToken #
ServiceAccount のトークンは基本的に Pod 内の /var/run/secrets/kubernetes.io/serviceaccount/token に自動でマウントされます。K8s API を呼び出すことのない Pod にはそのトークンすらマウントしない方が安全です。マニフェストの automountServiceAccountToken: false でオフにできます。
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
automountServiceAccountToken: false
containers:
- name: nginx
image: nginx:1.27この 1 行を入れておくと、その Pod が万が一侵害されても K8s API に直接アクセスするトークンがありません。セキュリティガイドの常連の推奨事項です。
NetworkPolicy — Pod 間トラフィックの制御 #
RBAC が K8s API への権限を扱うなら、NetworkPolicy は Pod 間の IP トラフィック を扱います。同じクラスタの 2 つの Pod が互いの IP を知って通信しようとするとき、そのトラフィックが許可されているかを検査するポリシーです。
デフォルトはすべて通過 #
K8s ネットワークモデルのデフォルトは単純です — NetworkPolicy がなければすべての Pod が互いに通信できます。 同じ namespace でも別の namespace でも、Pod の IP さえわかれば自由にパケットを送れます。マルチテナントクラスタでこのデフォルト値がそのまま放置されると、ある namespace の Pod が別の namespace の DB Pod に自由にアクセスする形になります。
NetworkPolicy の動作ルールは次の 2 行に整理されます。
- NetworkPolicy が 1 つでもマッチする Pod のトラフィックは、そのポリシーの
policyTypesに書かれた方向に対して default-deny が適用されます。 - そしてそのポリシーの
ingress/egressルールに明示的に許可されたトラフィックだけが通ります。
マッチするポリシーが 1 枚もない Pod は上のルールが発動しないのですべてのトラフィックが通ります。マッチするポリシーが 1 枚でもあれば、そのポリシーの方向についてはホワイトリストモデルに切り替わります。
CNI が NetworkPolicy をサポートしなければならない #
NetworkPolicy は K8s マニフェスト次元では標準ですが、実際にトラフィックを止めるのは CNI (Container Network Interface) プラグインがします。 なので CNI が NetworkPolicy をサポートしなければマニフェストを適用しても何も起こりません。トラフィックはそのまま通ります。
| CNI | NetworkPolicy サポート |
|---|---|
| Calico | サポート |
| Cilium | サポート (eBPF ベース) |
| Antrea | サポート |
| flannel | 非サポート |
| EKS の amazon-vpc-cni | 別途オプションの有効化が必要 (Calico を一緒にインストールするか vpc-cni の NetworkPolicy オプションをオン) |
運用クラスタで NetworkPolicy を使う計画なら、クラスタ作成時から CNI を選んでおかなければなりません。デフォルトの EKS クラスタで NetworkPolicy マニフェストを適用して「なぜトラフィックが止まらないんだ?」がよくある罠です — CNI がサポートしなければマニフェストはただ etcd に入っているオブジェクトにすぎません。
default-deny → allow パターン #
運用の標準パターンは namespace に default-deny ポリシーを 1 枚敷いて、必要な通信だけ明示的に許可 することです。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod
spec:
podSelector: {}
policyTypes:
- Ingress
- EgresspodSelector: {} は空セレクタで「この namespace のすべての Pod」を指します。policyTypes に Ingress と Egress が両方書かれていて ingress / egress ルールが 1 行もないので、この namespace のすべての Pod は入ってくるトラフィックも出ていくトラフィックもすべて遮断されます。
この状態でそのまま置くと Pod が DNS 照会すらできなくなるので、最低限 DNS トラフィックは開けてやらなければなりません。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: prod
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53その次にワークロード別に必要な通信を 1 枚ずつ追加します — frontend が backend へ行く 80/TCP、backend が DB へ行く 5432/TCP のような形です。
NetworkPolicy マニフェスト — frontend → backend #
もっとも一般的な 1 コマを 1 枚に書いておくと次のとおりです。backend Pod が frontend Pod から入ってくる 8080/TCP トラフィックだけを受けるようにする ingress ポリシーです。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: prod
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080読み方は次のとおりです。
spec.podSelector— このポリシーが適用される Pod。app=backendラベルが付いた Pod にのみ適用。policyTypes: [Ingress]— 入ってくる方向に対するポリシーであることを明示。egress はこのポリシーでは制御しません。ingress[0].from— どこから来るトラフィックを許可するか。app=frontendラベルが付いた Pod から。ingress[0].ports— どのポートに対して。8080/TCP のみ。
from のセレクタは 3 つから選べます — この 3 つは別々または一緒に使えます。
| セレクタ | 意味 |
|---|---|
podSelector | 同じ namespace の Pod ラベルマッチ |
namespaceSelector | 別の namespace のすべての Pod (またはその namespace + podSelector の組み合わせ) |
ipBlock | CIDR 表現で IP 範囲 (外部 IP またはノード IP) |
namespaceSelector と podSelector を同じ from 項目に一緒に書くと「特定 namespace の特定ラベル Pod」になります。2 つを別々に書くと「その namespace のすべての Pod または同じ namespace のそのラベル Pod」の意味になってしまうので、意図した形が何かを正確に見て書かなければなりません。
ingress:
- from:
- namespaceSelector:
matchLabels:
env: prod
podSelector:
matchLabels:
app: frontendingress:
- from:
- namespaceSelector:
matchLabels:
env: prod
- podSelector:
matchLabels:
app: frontendこの 2 つのマニフェストの意味が違うという点が NetworkPolicy の常連の罠です。上のマニフェストは「env=prod namespace の frontend Pod だけ」で、下のマニフェストは「env=prod namespace のすべての Pod または同じ namespace の frontend Pod」です。
egress ルール — 出ていく方向 #
ingress の鏡が egress です。backend Pod が DB へ 5432 ポートで出ていく通信だけ許可するポリシーは次の形です。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-egress-to-db
namespace: prod
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432このポリシー 1 枚が適用されると backend Pod は postgres Pod の 5432/TCP 以外にはどこにも出ていけません。上で作った default-deny + allow-dns の組み合わせと合わせると、backend Pod の出ていく通信は「DNS + DB」の 2 つに限定されます。外部インターネットへ行く通信をワークロードごとに正確にホワイトリストで絞っておくのが運用セキュリティの標準的な形です。
NetworkPolicy の限界 #
NetworkPolicy は L3/L4 ポリシー です。IP、ポート、プロトコルだけを見ます。HTTP メソッドやパスのような L7 ポリシーは NetworkPolicy の範囲外です — Cilium の L7 ポリシーや Istio / Linkerd のようなサービスメッシュがその次元を扱います。そして NetworkPolicy はクラスタ内のトラフィックポリシーで、外部インバウンドは Ingress / LoadBalancer 次元 で、外部アウトバウンドは NAT ゲートウェイのセキュリティグループ で別途制御されます。1 つのクラスタのセキュリティの形は複数の層のポリシーが一緒に作っていきます。
ResourceQuota — namespace リソース合計上限 #
#4 でコンテナ単位リソースモデル — requests と limits — を扱いました。コンテナ 1 つの保証と上限を定めるオブジェクトでした。その上にもう 1 層加えるのが namespace 単位の合計上限 である ResourceQuota です。
運用シナリオは明確です。1 つのクラスタに dev / staging / prod、または複数のチームが一緒に住むとき、dev namespace がすべてのノードの CPU を使い切って prod ワークロードのリソースが不足する事故を防がなければなりません。ResourceQuota がその空白を埋めます。
ResourceQuota マニフェスト #
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: dev
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "50"
services: "20"
configmaps: "30"
secrets: "30"
persistentvolumeclaims: "10"
requests.storage: 100Giこの ResourceQuota が dev namespace に適用されると、次の合計が上限を超えられません。
- その namespace 内のすべての Pod の
requests.cpu合計 ≤ 4 コア requests.memory合計 ≤ 8Gilimits.cpu合計 ≤ 8 コアlimits.memory合計 ≤ 16Gi- オブジェクト個数 — Pod 50 個、Service 20 個、ConfigMap・Secret 各 30 個、PVC 10 個
- PVC の
requests.storage合計 ≤ 100Gi
上限を超えるオブジェクト生成は K8s API サーバで拒否されます。dev namespace にすでに 4 コアが割り当てられた状態で追加 Pod の requests.cpu: 1 を作ろうとすると、その Pod 生成リクエストは admission 段階で拒否されて次のメッセージが出ます。
Error from server (Forbidden): error when creating "...": pods "..." is forbidden: exceeded quota: dev-quota, requested: requests.cpu=1, used: requests.cpu=4, limited: requests.cpu=4ResourceQuota 動作確認 #
kubectl get resourcequota -n dev
kubectl describe resourcequota dev-quota -n devName: dev-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
configmaps 12 30
limits.cpu 6 8
limits.memory 12Gi 16Gi
pods 18 50
persistentvolumeclaims 3 10
requests.cpu 3 4
requests.memory 6Gi 8Gi
requests.storage 30Gi 100Gi
secrets 15 30
services 8 20Used / Hard の表が 1 ページに出てくる形です。運用クラスタで 1 つの namespace がどこまで埋まっているかを確認するときに describe がもっとも速いツールです。
LimitRange と対 — コンテナ単位のデフォルト・上限 #
ResourceQuota の微妙な罠の 1 つが — ResourceQuota が有効な namespace に Pod を作るときに、コンテナに requests/limits を書かないと拒否されます。 ResourceQuota が合計を計算するためにはすべてのコンテナのリソース値が必要なのに、マニフェストに書かれていないコンテナは合計計算ができないからです。
この部分の運用安全網が LimitRange です。LimitRange はコンテナ単位でデフォルト値と最大・最小を定めるオブジェクトです。
apiVersion: v1
kind: LimitRange
metadata:
name: dev-limits
namespace: dev
spec:
limits:
- type: Container
default:
cpu: 200m
memory: 256Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "2"
memory: 2Gi
min:
cpu: 50m
memory: 64Miこの LimitRange が適用された namespace に Pod マニフェストが入ってくると、次のように動作します。
- コンテナに
requestsがなければdefaultRequest(100m / 128Mi)が自動で埋まる - コンテナに
limitsがなければdefault(200m / 256Mi)が自動で埋まる - コンテナの
requestsやlimitsがmax(2 / 2Gi)を超えれば拒否 min(50m / 64Mi)未満なら拒否
ResourceQuota と LimitRange の責務を分けると 1 行ずつ整理されます。
| オブジェクト | 単位 | 何を定めるか |
|---|---|---|
LimitRange | コンテナ | デフォルト値・最大・最小 |
ResourceQuota | namespace | 合計上限、オブジェクト個数上限 |
運用の形は 2 つを一緒に置くことです。LimitRange が壊れたマニフェストの空白を埋めてくれて、ResourceQuota がその埋まった値の合計が上限を超えないように防ぎます。2 つが一緒にあってこそマルチテナント運用のリソースポリシーが安定的に回ります。
scopes / scopeSelector — 一部の Pod だけに #
ResourceQuota は基本的に namespace のすべての Pod に適用されますが、scope で絞れます。よく使われるパターンは PriorityClass 別の分離です — たとえば high-priority ワークロードのリソース合計だけを別途制限したり、BestEffort QoS の Pod だけ別途制限したりする形です。
apiVersion: v1
kind: ResourceQuota
metadata:
name: high-priority-quota
namespace: dev
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
scopeSelector:
matchExpressions:
- operator: In
scopeName: PriorityClass
values: ["high"]運用の基本形では scope なしで全体に適用する 1 枚で始めて、必要なときに scope を持つポリシーを追加する方式が無難です。
3 つのポリシーの協業 — マルチテナントクラスタの隔離 #
3 つのオブジェクトが作る隔離の形を 1 つの絵にまとめておくと次のとおりです。
[ RBAC ] 誰がオブジェクトを作れるか
│ (verbs × resources × namespace)
│
[ NetworkPolicy ] 作られた Pod が誰と通信するか
│ (podSelector × from/to × ports)
│
[ ResourceQuota ] その namespace がどれくらい作れるか
│ (cpu/memory 合計 + オブジェクト個数)
│
[ LimitRange ] コンテナ単位のデフォルト・上限
(default / max / min)3 つのオブジェクトが別々に回るように見えますが、実際には 1 つの namespace の隔離を一緒に作ります。dev namespace に RBAC を適用して dev チームだけがその中のオブジェクトを触れるようにし、NetworkPolicy で dev の Pod が prod の DB へ行くトラフィックを止め、ResourceQuota で dev が使い切れる CPU・メモリ・オブジェクト個数を限定します。この 3 つがすべて適用されているとき、dev で発生した事故が staging と prod に漏れない隔離が成立します。
この隔離がきれいに回るためには 1 つ前提がさらに必要です — namespace 自体をうまく分けておくこと です。環境別(dev / staging / prod)、チーム別(team-a / team-b)、またはサービス単位で namespace をどう分けるかのポリシーは、クラスタを最初にセットアップするときに決めておくべき決定です。この決定を曖昧に置いて RBAC / NetworkPolicy / ResourceQuota を適用しようとすると、ポリシーの肌理をどこに合わせるかが毎回揺れます。
シリーズ振り返り — K8s 中級 7 編で手に入ったもの #
最後の記事なので 7 編を一度押さえておきます。基礎シリーズがマニフェスト 1 枚を読み書きする段階まで連れていってくれたなら、中級シリーズはその上に運用の肌理を 1 層ずつ加えました。
- #1 — StatefulSet / DaemonSet / Job / CronJob です。Deployment ではないコントローラ 4 種で、アイデンティティとディスクが必要なワークロード、ノードごとに 1 つずつ立つべきワークロード、一回性の作業、周期実行作業の 4 つのパターンを扱います。
- #2 — PV / PVC / StorageClass です。永続データモデルとして、静的・動的プロビジョニング、accessModes、reclaimPolicy、volumeBindingMode、allowVolumeExpansion を整理し、StatefulSet の volumeClaimTemplates が何を作るかを説明します。
- #3 — Ingress と Ingress Controller です。外部入口を 1 か所に集めるオブジェクトと、そのマニフェストを実際のトラフィックルーティングに解いてくれるコントローラを扱い、HTTP/HTTPS、TLS 終端、バーチャルホスト、パスベースルーティングまで整理します。
- #4 — resources.requests / limits です。コンテナ単位のリソース要求と上限を説明し、requests がスケジューリングの基準、limits が OOM・CPU スロットリングの基準であることと、QoS 3 等級が evict 優先度を決める流れを扱います。
- #5 — Health check です。liveness / readiness / startup probe の 3 つで、コンテナの生存・サービス準備・初期化段階を K8s がどう判定するかを見ます。
- #6 — HPA / VPA / Cluster Autoscaler です。Pod 個数、Pod リソース、ノード個数が負荷に合わせて自動的に変わる 3 つの次元を説明し、metrics-server・custom metrics・HPA + VPA の衝突の罠も押さえます。
- #7 — RBAC / NetworkPolicy / ResourceQuota です。セキュリティとリソースポリシーを通じて誰が、どんなトラフィックが、どれくらい許可されるかのマルチテナントクラスタ隔離の 3 つの次元を扱います。
この 7 編すべてを追ってきた時点なら、会社のクラスタのマニフェストディレクトリでどんな種類のオブジェクトに出会ってもその意図と運用上の罠を 1 行で読める段階です。kind: StatefulSet を見ると volumeClaimTemplates と headless Service を自然に思い浮かべ、kind: Ingress を見るとその裏の Ingress Controller が何かを問い、resources の下の空の limits を見ると OOM リスクと LimitRange の不在を同時に疑うようになります。マニフェスト 1 枚がクラスタ内でどう回るかのモデルが頭の中に定着した段階です。
次のトラック — K8s 上級 #
K8s 中級シリーズで意図的に持ち越した深いテーマたちが K8s 上級トラックの筋書きです。6 編で束ねる予定で、表で先にまとめておくと次のとおりです。
| テーマ | 説明 |
|---|---|
| CNI 深さ — Calico / Cilium / eBPF | クラスタネットワークの実際のデータプレーン。iptables ベースと eBPF ベースの違い、NetworkPolicy の実行段の形。 |
| RBAC / ServiceAccount 深さ | Aggregated ClusterRole、impersonation、外部 IAM マッピング(EKS の IRSA、GKE の Workload Identity)、トークン lifecycle。 |
| Admission Controller / OPA Gatekeeper / Kyverno | ポリシーエンジン。マニフェストが etcd に入る前に検査・変形する段階。「limits なしのコンテナを拒否」「特定ラベルを強制」のようなポリシー。 |
| CRD と Operator パターン | Kubernetes API 拡張。CustomResourceDefinition で新しいオブジェクト種を定義、controller-runtime ベースの Operator でそのオブジェクトを運用。 |
| オブザーバビリティ | Prometheus + Grafana + Loki、kube-state-metrics、OpenTelemetry。クラスタ・ワークロードのメトリクス・ログ・トレースの標準スタック。 |
| GitOps — ArgoCD / Flux | マニフェストの source of truth を git に置く運用モデル。drift detection、multi-cluster、sync policy。 |
この 6 つのテーマをすべて扱い終えると、クラスタをセットアップする人の視野で K8s を見られる段階まで一歩さらに入ります。マニフェストを上手く書く段階から、どんなポリシーエンジンを敷くか・どんなオブザーバビリティスタックを選ぶか・GitOps パイプラインをどう組むかを決定する段階に移るトラックです。
その次 — K8s 実戦 #
上級トラックの次は K8s 実戦 6 編を準備しています。上級までが K8s のオブジェクトモデルとポリシーの深さを扱うなら、実戦はその上に本物のサービスを 1 つ載せて運用する 1 サイクルです。
| テーマ | 説明 |
|---|---|
| EKS クラスタセットアップ | AWS EKS クラスタを最初から、IAM、VPC、ノードグループ、アドオン。 |
| アプリデプロイ骨格 | Deployment + Service + Ingress + ConfigMap + Secret の 1 セット、Helm chart で整理。 |
| DB 連動 | RDS / Aurora を Pod から安全に呼ぶ道、Secrets Manager 統合、コネクションプール。 |
| CI/CD パイプライン | GitHub Actions でコンテナビルド → ECR push → ArgoCD sync。 |
| モニタリング・アラーム | CloudWatch + Prometheus、核心アラームルールセット、on-call 流れ。 |
| 運用チェックリスト | アップグレード、バックアップ・リカバリ、コスト点検、セキュリティ点検の定期運用サイクル。 |
この 2 つのトラック(上級 + 実戦)を経ると K8s を導入して運用する人 の視野がほぼすべて入ります。中級シリーズが終わる地点がその大きな絵の真ん中です — オブジェクトモデルは手に入り、その上に乗るポリシー・拡張・運用の深さが次のトラックです。
締めくくり #
K8s 中級シリーズ 7 編を締めくくりました。この記事ではマルチテナントクラスタの隔離を作る 3 つのポリシーオブジェクト — RBAC、NetworkPolicy、ResourceQuota — を 1 サイクルでまとめました。RBAC が K8s API の権限を、NetworkPolicy が Pod 間トラフィックを、ResourceQuota(と相棒の LimitRange)が namespace のリソース合計を制御し、3 つの次元が一緒に適用されているときにはじめて 1 つのクラスタの上に複数の環境・チームが安全に一緒に住めるというモデルを追いました。シリーズ全体で見ると、基礎 7 編がマニフェスト 1 枚を読み書きする段階、中級 7 編がそのマニフェストが運用クラスタでどう回るかの深さを 1 層ずつ加えた段階でした。次のトラックである K8s 上級 では CNI・RBAC の深さからポリシーエンジン、CRD/Operator、オブザーバビリティ、GitOps まで — クラスタをセットアップして運用する視野のテーマたちを順次扱います。その次の K8s 実戦 では EKS の上に本物のサービスを載せる 1 サイクルを最初から最後まで追います。