シリーズ記事一覧
- 第1回:全体像と環境構築
- 第2回:Pod・ReplicaSet・Deployment
- 第3回:Service・Ingress
- 第4回:ConfigMap・Secret・Volume
- 第5回:Namespace・RBAC
- 第6回:トラブルシュートと運用
- 第7回:設計総合演習
📑 目次
- この記事について
- この記事のゴール
- 前提条件
- Docker Composeではこうだった(Before)
- k8sではこうなる(After)
- ハンズオン:Namespaceで分離し、RBACで制御する
- つまずきポイント(体験談)
- まとめ
- 次回予告
1. この記事について
ここまでで、Pod(第2回)、Service(第3回)、ConfigMap/Secret(第4回)と、k8sの主要リソースを一通り触りました。
ところで、これまでのハンズオンでは全てのリソースを default という名前の空間に作っていました。
実務でこれをやるとどうなるか?
🍽️ チェーンレストランの全店舗が「同じ厨房」を共有している状態です。
- A店のスタッフがB店のレシピ帳(Secret)を勝手に見れる
- 開発中の新メニュー(dev環境)が営業中のメニュー(prod環境)と混在する
- 新人アルバイトが本番の食材を廃棄してしまう(権限がない操作をしてしまう)
今回は「空間を分ける」と「権限を制御する」— k8sのセキュリティの基本に取り組んでみました。
2. この記事のゴール
この記事で書いていること:
- Namespaceで分離する理由
- Namespace間のリソース分離を実際に試した話
- RBAC の3要素(Role / RoleBinding / ServiceAccount)の関係の整理
- 「読み取りのみ」権限を付与してテストしてみた話
3. 前提条件
| 項目 | 要件 |
|---|---|
| 前回の完了 | 第4回でConfigMap/Secret/Volumeを理解済み |
| クラスタ状態 | kindクラスタが起動中 |
# WSL2 Ubuntu で実行
kubectl get nodes
4. Docker Composeではこうだった(Before)
4-1. Docker Composeの「空間」
Docker Composeでは、基本的に全てのコンテナが同じネットワーク・同じ空間で動きます。
# docker-compose.yml
services:
api:
image: myapp:latest
db:
image: postgres:14
redis:
image: redis:7
環境を分けたい場合は、ファイルを分けるくらい。
# 開発環境
docker-compose -f docker-compose.dev.yml up
# 本番環境
docker-compose -f docker-compose.prod.yml up
4-2. でもこの壁がある
Docker Composeでは、チーム開発や本番運用で以下の限界にぶつかります。
| # | 壁 | Docker Composeの現実 |
|---|---|---|
| 壁1 | 環境の分離がファイル単位 | dev/staging/prodが混在しがち |
| 壁2 | アクセス制御がない |
docker-compose コマンドを打てる人は全操作可能 |
| 壁3 | リソース制限がない | 1つのコンテナがメモリを食い尽くすことも |
🔰 Memo: 個人開発ではこれで十分だと思います。
問題は「チームで使う」「本番環境を運用する」ときに出てきます。
5. k8sではこうなる(After)
5-1. Namespaceで論理分離
🍽️ 比喩:商業ビルのフロア分け
Namespaceは、1つのk8sクラスタ(ビル)の中に「フロア」を作る仕組みです。
フロア Namespace 中身 1F production本番のPod、Service、Secret 2F stagingステージングのPod、Service、Secret 3F development開発中のPod、Service、Secret 管理事務所 kube-systemk8sの管理用Pod(API Server等) 各フロアのリソースは独立しています。
1Fのapp-secretと 3Fのapp-secretは別物です。
5-1-1. デフォルトで存在するNamespace
kubectl get namespaces
| Namespace | 用途 |
|---|---|
default |
ユーザーリソースのデフォルト配置先 |
kube-system |
k8sの管理コンポーネント |
kube-public |
全ユーザーに公開される情報 |
kube-node-lease |
ノードの死活監視 |
🔰 Memo: これまでのハンズオンでは全て default に作っていました。
実務では用途ごとにNamespaceを分けるのが基本だそうです。
5-2. RBACで権限制御
Namespaceで環境を分離しただけでは、誰でもどのNamespaceにもアクセスできてしまいます。
そこで必要になるのがRBAC(Role-Based Access Control) ― 役割ベースのアクセス制御です。
🍽️ 比喩:入館証システム
Namespaceでフロアを分けても、誰でも入れたら意味がありません。
RBACは「誰が、どのフロアで、何ができるか」を制御する入館証システムです。
RBACは3つの要素で構成されます。
| 要素 | 比喩 | 役割 |
|---|---|---|
| ServiceAccount | スタッフID | 「誰が」を識別する(Pod用の身分証明書) |
| Role | 権限定義書 | 「何ができるか」を定義する |
| RoleBinding | 入館証の発行 | ServiceAccountにRoleを紐づける |
5-3. Role vs ClusterRole
| 種類 | スコープ | 比喩 | 用途 |
|---|---|---|---|
| Role | 特定のNamespace内 | 「1Fだけで使える入館証」 | 特定環境の権限管理 |
| ClusterRole | クラスタ全体 | 「全フロア使えるマスターキー」 | 管理者やクラスタ全体の権限 |
同様に RoleBinding と ClusterRoleBinding があります。
まず、1つのNamespace内でどう動くかを見てみましょう:
基本パターン(1つのNamespace内):
これが複数のNamespaceに展開されると、以下のような全体像になります:
全体像(複数Namespace + ClusterRole):
🍽️ productionフロアの
viewerは「メニュー(Pod)を見る」だけ。
developmentフロアのdeveloperは「新メニューを作る」もOK。
adminはマスターキーで全フロアに入れる。
5-4. 権限チェックの流れ
6. ハンズオン:Namespaceで分離し、RBACで制御する
6-1. 作業ディレクトリ
# WSL2 Ubuntu で実行
mkdir -p ~/k8s-handson/namespace-rbac
cd ~/k8s-handson/namespace-rbac
6-2. Step1:Namespaceを作ってリソースを分離
6-2-1. 2つのNamespaceを作成
# ~/k8s-handson/namespace-rbac/ で実行
# production 用のNamespace
kubectl create namespace production
# development 用のNamespace
kubectl create namespace development
# 確認
kubectl get namespaces
期待される出力:
NAME STATUS AGE
default Active ...
development Active 5s
kube-node-lease Active ...
kube-public Active ...
kube-system Active ...
production Active 5s
6-2-2. 各Namespaceにリソースを配置
📂 namespace-rbac/
├── prod-deployment.yaml ← Step 1 🆕
└── dev-deployment.yaml ← Step 1 🆕
# prod-deployment.yaml
# production 用のDeployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: production # ← Namespace を指定
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25 # 本番は安定バージョン
ports:
- containerPort: 80
# dev-deployment.yaml
# development 用のDeployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: development # ← Namespace を指定
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:latest # 開発は最新バージョン
ports:
- containerPort: 80
| 項目 | prod-deployment | dev-deployment | 違いの理由 |
|---|---|---|---|
| namespace | production | development | 環境ごとにリソースを分離 |
| replicas | 2 | 1 | 本番は冗長化、開発は1つで十分 |
| image | nginx:1.25(固定) | nginx:latest(最新) | 本番は安定版、開発は最新機能を試す |
| name | web-app | web-app | 同じ名前でOK(Namespaceが異なるため衝突しない) |
Namespace = 「フロアの仕切り」
同じビル(クラスタ)内でも、フロア(Namespace)が違えば同じ部屋番号(リソース名)を使えます。
namespace:フィールドを指定しないとdefaultNamespace に作成されます。
🔰 Note: 両方とも name: web-app にしています。
Docker Composeなら名前が衝突しますが、k8sではNamespaceが違えば同じ名前のリソースを作れるんですね。
# ~/k8s-handson/namespace-rbac/ で実行
# 両方を適用
kubectl apply -f prod-deployment.yaml
kubectl apply -f dev-deployment.yaml
6-3. Step2:Namespace間の分離を確認
# production のPodを確認
kubectl get pods -n production
期待される出力:
NAME READY STATUS RESTARTS AGE
web-app-xxxxxxxxx-xxxxx 1/1 Running 0 10s
web-app-xxxxxxxxx-yyyyy 1/1 Running 0 10s
# development のPodを確認
kubectl get pods -n development
期待される出力:
NAME READY STATUS RESTARTS AGE
web-app-xxxxxxxxx-zzzzz 1/1 Running 0 10s
# default のPodを確認(何もないはず)
kubectl get pods -n default
期待される出力:
No resources found in default namespace.
同じ名前のDeploymentが、別々のNamespaceで独立して動いています。
🍽️ 1F(production)には2人のスタッフ、2F(development)には1人。
お互いのフロアには干渉しません。
6-3-1. 全Namespaceを横断して見る
# 全NamespaceのPodを一覧
kubectl get pods --all-namespaces
管理者は
--all-namespaces(または-A)で全フロアを俯瞰できます。
ビルの警備室のモニターのようなものです。
6-4. Step3:RBAC — 読み取り専用ユーザーを作成
Step1〜2でNamespaceの分離を体験しました。
ここからはRBACで**「見るだけで変更はできない」権限**を作ります。実務で最もよく使うパターンです。
6-4-1. 3つのファイルを作成
📂 namespace-rbac/
├── prod-deployment.yaml ✅ Step 1
├── dev-deployment.yaml ✅ Step 1
├── serviceaccount.yaml ← Step 3 🆕
├── role-readonly.yaml ← Step 3 🆕
└── rolebinding-readonly.yaml ← Step 3 🆕
6-4-2. ① ServiceAccount(スタッフID)
# serviceaccount.yaml
# ServiceAccount = Podの身分証明書
apiVersion: v1
kind: ServiceAccount
metadata:
name: prod-viewer # この名前がスタッフID
namespace: production # production に所属
6-4-3. ② Role(権限定義書)
# role-readonly.yaml
# Role = 「何ができるか」の定義
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader # ロール名
namespace: production # このNamespace内でのみ有効
rules:
# Pod に対する権限
- apiGroups: [""] # コアAPI(Pod, Service等)
resources: ["pods"] # 対象リソース
verbs: ["get", "list", "watch"] # 許可する操作(読み取りのみ)
# Service に対する権限
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list"]
6-4-4. Role の読み方
| YAMLの項目 | 比喩 | 意味 |
|---|---|---|
apiGroups: [""] |
「基本メニュー(Pod等)について」 | コアAPIグループ |
resources: ["pods"] |
「Podという料理について」 | 対象リソースの種類 |
verbs: ["get", "list", "watch"] |
「見る・一覧する・監視する、はOK」 | 許可する操作 |
🍽️ この権限定義書は「1Fのメニュー(Pod)とレジ表(Service)は見ていいけど、新メニュー作成(create)や廃棄(delete)はダメ」という内容です。
6-4-5. 主なverbs一覧
| verb | 意味 | 比喩 |
|---|---|---|
get |
1つ取得 | 特定の料理を確認する |
list |
一覧取得 | メニュー一覧を見る |
watch |
変更を監視 | オーダー状況をリアルタイムで見る |
create |
作成 | 新メニューを追加する |
update |
更新 | メニューの内容を変更する |
delete |
削除 | メニューから外す |
6-4-6. ③ RoleBinding(入館証の発行)
# rolebinding-readonly.yaml
# RoleBinding = ServiceAccountにRoleを紐づける
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: prod-viewer-binding # バインディング名
namespace: production
subjects:
# 誰に(ServiceAccount)
- kind: ServiceAccount
name: prod-viewer # ← serviceaccount.yaml の name と一致
namespace: production
roleRef:
# 何の権限を(Role)
kind: Role
name: pod-reader # ← role-readonly.yaml の name と一致
apiGroup: rbac.authorization.k8s.io
🍽️ 「prod-viewer さん(ServiceAccount)に、pod-reader という入館証(Role)を発行します」という申請書(RoleBinding)です。
6-4-7. 適用
# ~/k8s-handson/namespace-rbac/ で実行
kubectl apply -f serviceaccount.yaml
kubectl apply -f role-readonly.yaml
kubectl apply -f rolebinding-readonly.yaml
# 確認
kubectl get serviceaccount -n production
kubectl get role -n production
kubectl get rolebinding -n production
期待される出力(serviceaccount):
NAME SECRETS AGE
default 0 ...
prod-viewer 0 5s
6-5. Step4:権限テスト — できること・できないこと
6-5-1. kubectl auth can-i で権限を確認
# prod-viewer は production で pods を get できる?
kubectl auth can-i get pods \
--namespace production \
--as system:serviceaccount:production:prod-viewer
期待される出力:
yes
# prod-viewer は production で pods を delete できる?
kubectl auth can-i delete pods \
--namespace production \
--as system:serviceaccount:production:prod-viewer
期待される出力:
no
# prod-viewer は development で pods を get できる?
kubectl auth can-i get pods \
--namespace development \
--as system:serviceaccount:production:prod-viewer
期待される出力:
no
RBAC が効いています。
6-5-2. 結果まとめ
| 操作 | Namespace | 結果 | 理由 |
|---|---|---|---|
get pods |
production | ✅ yes | Role で get を許可 |
list pods |
production | ✅ yes | Role で list を許可 |
delete pods |
production | ❌ no | Role に delete がない |
get secrets |
production | ❌ no | Role に secrets がない |
get pods |
development | ❌ no | Role は production 限定 |
🍽️ 1F(production)の入館証で、1Fのメニュー(Pod)は見られる。
でも、レシピ帳(Secret)は見られないし、2F(development)には入れない。
削除(廃棄)もできない。
6-5-3. 実際にPodとして使ってみる
# prod-viewer の ServiceAccount でPodを起動し、内部からAPIを叩く
kubectl run rbac-test \
--image=bitnami/kubectl \
--namespace production \
--serviceaccount=prod-viewer \
--rm -it --restart=Never \
-- kubectl get pods -n production
期待される出力:
NAME READY STATUS RESTARTS AGE
web-app-xxxxxxxxx-xxxxx 1/1 Running 0 5m
web-app-xxxxxxxxx-yyyyy 1/1 Running 0 5m
# 同じSAで delete を試みる
kubectl run rbac-test2 \
--image=bitnami/kubectl \
--namespace production \
--serviceaccount=prod-viewer \
--rm -it --restart=Never \
-- kubectl delete pod web-app-xxxxxxxxx-xxxxx -n production
期待される出力:
Error from server (Forbidden): pods "web-app-xxxxxxxxx-xxxxx" is forbidden: User "system:serviceaccount:production:prod-viewer" cannot delete resource "pods" in API group "" in the namespace "production"
Forbidden(禁止)が返されました。
RBACによるアクセス制御がちゃんと機能しているのを確認できました。
6-6. 後片付け
# リソースの削除
kubectl delete namespace production
kubectl delete namespace development
# Namespaceを削除すると、中のリソースも全て削除される
kubectl get namespaces
🔰 Note: Namespaceを削除すると、そのNamespace内のDeployment、Pod、Service、ConfigMap、Secret、Role、RoleBinding... 全てが消えます。
本番では絶対に慎重にやらないといけないですね。
7. つまずきポイント(体験談)
7-1. -n フラグの付け忘れ
症状: kubectl get pods で何も出ない → default Namespace を見ていた。
対策: 常に -n <namespace> を付ける習慣をつける。
# よく使うNamespaceをデフォルトに変更
kubectl config set-context \
--current \
--namespace=production
# 確認
kubectl config view --minify | grep namespace
これで -n production を省略できます。
7-2. RoleBinding の subjects.name の不一致
症状:
Error from server (Forbidden): ...
権限を設定したはずなのに拒否される。
対策: 3つのリソースの名前の紐づきを確認。
# ServiceAccount名を確認
kubectl get sa -n production
# Roleの内容を確認
kubectl describe role pod-reader -n production
# RoleBindingの紐づきを確認
kubectl describe rolebinding prod-viewer-binding -n production
🍽️ 入館証(RoleBinding)に書かれた名前と、実際のスタッフID(ServiceAccount名)が違ったら、ゲートを通れません。
7-3. Role と ClusterRole の混同
| 間違い | 結果 |
|---|---|
| RoleBinding で ClusterRole を参照 | 動く(そのNamespace内でClusterRoleの権限が適用される) |
| ClusterRoleBinding で Role を参照 | エラー(ClusterRoleBindingはClusterRoleしか参照できない) |
🔰 Memo: 迷ったら「まずNamespace内のRoleから始める」のが安全だと感じました。
ClusterRoleは管理者向けの上級機能です。
8. まとめ
8-1. この記事でやったこと
| # | 内容 | 状態 |
|---|---|---|
| 1 | Namespaceで論理的にリソースを分離した | ✅ |
| 2 | 同名リソースが別Namespaceで共存できることを確認した | ✅ |
| 3 | RBAC(ServiceAccount + Role + RoleBinding)を構築した | ✅ |
| 4 | 権限テスト(できること/できないこと)を確認した | ✅ |
8-2. RBAC の3要素(まとめ表)
| 要素 | 比喩 | 定義する内容 | スコープ |
|---|---|---|---|
| ServiceAccount | スタッフID | 「誰が」 | Namespace |
| Role | 権限定義書 | 「何ができるか」 | Namespace |
| RoleBinding | 入館証の発行 | 「誰に何の権限を」 | Namespace |
| ClusterRole | 全フロア共通権限 | 「何ができるか」 | クラスタ全体 |
| ClusterRoleBinding | マスターキー発行 | 「誰に何の権限を」 | クラスタ全体 |
8-3. Docker Compose → k8s 対応表
| Docker Compose | Kubernetes | 備考 |
|---|---|---|
| ファイルで環境を分離 | Namespace | k8sは1クラスタ内で論理分離 |
| (該当なし) | RBAC | Docker Composeには権限管理がない |
| (該当なし) | ServiceAccount | Pod単位の身分証明 |
| 全員がdocker-compose操作可能 | Role + RoleBinding | 操作ごとに許可/拒否を制御 |
8-4. 第4回との繋がり:Secretの安全性
前回「base64は暗号化ではない」と書きました。
Secretの安全性はRBACで担保するのが正解です。
# Secretを読めないRole
rules:
- apiGroups: [""]
resources: ["pods"] # Podだけ許可
verbs: ["get", "list"]
# ← secrets は含めない = Secretにアクセスできない
🍽️ レシピ帳(Secret)は金庫に入れるだけでは不十分。
「金庫の鍵を持てるのは誰か」(RBAC)まで設定して初めて安全。
9. 次回予告
第6回:運用 — ログ・監視・トラブルシュート
環境分離も権限制御もできました。
でも、実務で最も時間を使うのは運用です。
🍽️ レストランを開店するより、毎日の営業(トラブル対応・品質管理)の方がずっと大変。
次回は:
-
kubectl logs/kubectl describe/kubectl execの使い分け - 「Podが起動しない!」のトラブルシュート手順
- リソース使用量の確認方法
- 実務でよく使う
kubectlコマンド集