K8s 基礎 #7 Namespace とラベル — クラスタの整理法

読了 15分

K8s 基礎シリーズの最後の記事です。この記事では今まで作ったオブジェクトがなぜ default namespace に集まっていたか、Namespace が何を分離してくれるか、そしてラベルと selector がどうオブジェクトをまとめて見つけるかを整理します。最後にこの 7 編全体を振り返り、次のトラックである K8s 中級で何を扱うかを予告します。

このシリーズは K8s 基礎 7 編です。

default namespace の限界 #

-A オプションを付けてクラスタの全オブジェクトを覗き見ると、私たちが作っていないものがたくさん立っているのが見えます。

全 namespace のオブジェクトを見る
kubectl get all -A
出力例 — 抜粋
NAMESPACE     NAME                                   READY   STATUS    RESTARTS   AGE
kube-system   pod/coredns-5d78c9869d-2xvlw           1/1     Running   0          3d
kube-system   pod/etcd-minikube                      1/1     Running   0          3d
kube-system   pod/kube-apiserver-minikube            1/1     Running   0          3d
kube-system   pod/kube-controller-manager-minikube   1/1     Running   0          3d
kube-system   pod/kube-proxy-7gbdn                   1/1     Running   0          3d
kube-system   pod/kube-scheduler-minikube            1/1     Running   0          3d
default       pod/web-7f8b6c8f7d-abcde               1/1     Running   0          1h

kube-system は K8s が自分自身を回すためのコントロールプレーンコンポーネントが集まっている namespace です。coredns (クラスタ DNS)、etcd、apiserver、controller-manager、scheduler、kube-proxy — #1 で図で見たコンポーネントがすべてここに生きています。私たちが #2 から見ていたけれど名前は触れなかった隔離空間です。

namespace 一覧
kubectl get ns
出力例
NAME              STATUS   AGE
default           Active   3d
kube-node-lease   Active   3d
kube-public       Active   3d
kube-system       Active   3d

既定で作られるシステム namespace が 4 つ — defaultkube-systemkube-publickube-node-lease — 常に立っています。1 行ずつ押さえると。

  • default-n オプション無しで作ったオブジェクトが入るところ。シリーズ #1〜#6 の全オブジェクトがここに集まりました。
  • kube-system — K8s コントロールプレーンコンポーネントが住むところ。一般ユーザが直接触りません。
  • kube-public — 認証無しでも読めるオブジェクトのためのところ。クラスタ情報の公開用でほとんど使いません。
  • kube-node-lease — ノードのハートビートを効率的に管理するために 1.13 から分離されたところ。運用者が直接触ることは無い。

ここまではシステムが自動で回すから大きな問題はないのですが、1 つのクラスタで dev / staging / prod を一緒に回したり、チーム A・B が同じクラスタを分け合ったり、あるアプリは 1 つのまとまり、別のアプリは別のまとまりに分けたいときから default 1 か所に全部入れる形が崩れます。同じ名前の Service が環境ごとに別々にあるべきで、権限も環境別に分けるべきで、片方の環境の事故が他方に漏れないようにすべきだからです。

Namespace が解いてくれること #

Namespace を 1 行で要約すると 1 クラスタ内の仮想クラスタ です。Linux のユーザアカウントや git のブランチに似た、同じ物理リソースの上に論理的な区画を作る仕組みです。解いてくれることは次の 4 つが核心です。

  • 名前空間の分離 — 同じ名前のオブジェクトを別の namespace に別々に置けます。web という Service が dev と prod の両方に独立して存在でき、衝突しません。
  • RBAC の単位 — 権限を namespace 単位で分けられます。「チーム A は team-a namespace の中だけ読み書き可能、それ以外は見られない」のようなポリシーの基本単位です。
  • リソースクォータの単位ResourceQuotaLimitRange オブジェクトで namespace ごとに CPU・メモリ・オブジェクト数の上限を設定できます。dev が prod のリソースを食ってしまわないようにする道具です。
  • NetworkPolicy の単位 — namespace 間のトラフィックを遮断したり許可したりするポリシーを書けます。既定では全 namespace が互いに通信できる状態で、これを狭めるには NetworkPolicy が必要です。

ここで 1 つはっきり押さえる価値があります — Namespace 自体はセキュリティ境界ではありません。 単純にオブジェクト名を分ける論理区画にすぎません。本当の隔離は上の 4 項目のうち後ろの 3 つ (RBAC、リソースクォータ、NetworkPolicy) が別途行います。Namespace だけ作っておいて RBAC も NetworkPolicy も書いていないなら、権限のあるユーザはどの namespace のオブジェクトも見て触れますし、Pod 同士も互いに通信します。このシリーズはマニフェストの形までを扱い、RBAC / NetworkPolicy / ResourceQuota の深さは K8s 中級でまとめて扱います。

クラスタスコープ vs namespace スコープ #

オブジェクトには 2 種類あります。namespace スコープ (namespaced) のオブジェクトはどこかの namespace に属さねばならず、クラスタスコープ (cluster-scoped) のオブジェクトは namespace 無しでクラスタ全体に 1 つだけ存在します。私たちが見たオブジェクトで分けると。

スコープ
namespace スコープPod、Deployment、ReplicaSet、Service、ConfigMap、Secret、Job、Ingress
クラスタスコープNode、PersistentVolume、Namespace 自体、ClusterRole、StorageClass

Node が namespace に属さないのは直感的です — ノードは物理・仮想マシンでありクラスタのリソースで、どこかの環境に属するものではないからです。オブジェクトがどちら側に属するかコマンドで確認することもできます。

namespace スコープのオブジェクト一覧
kubectl api-resources --namespaced=true
クラスタスコープのオブジェクト一覧
kubectl api-resources --namespaced=false

運用していて「このオブジェクトに -n を付けるべきか?」が混同するときに 1 度回せば答えがすぐ出ます。

Namespace を作る #

命令的に 1 行で作れます。

命令的に作る
kubectl create namespace dev
出力例
namespace/dev created

git に意図を残したいならマニフェストで書きます。

dev-ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: dev
  labels:
    env: dev
apply
kubectl apply -f dev-ns.yaml

apiVersion は ConfigMap・Secret と同様にコアグループの v1 です。Namespace 自体はクラスタスコープオブジェクトなので、その metadata に namespace: フィールドを書きません (書けません)。

オブジェクトを特定の namespace に入れる道は 2 通りです。

  • マニフェストに書くmetadata.namespace: dev の 1 行をオブジェクトの metadata に直接書きます。
  • コマンドのオプションでkubectl apply -f web.yaml -n dev のように -n で指定します。
metadata.namespace で書いた場合
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: dev
  labels:
    app: web
spec:
  # ... 以下 [#4](/ja/posts/k8s-basics-4) と同じ

両方が同時にあるとマニフェストの値が優先されます (コマンドオプションはマニフェストに namespace が無いときだけ適用)。混乱を減らすには どちらか一方だけ 使うのがいいです。git に意図を残す観点では、マニフェストに書いておく方が次の人に親切です — コマンドオプションはシェル履歴にしか残らず、次に同じマニフェストを見たとき「これはどこに行くのか?」がまた疑問になるからです。

NS 別に眺める #

-n オプションで 1 つの namespace のオブジェクトだけ見ます。

dev namespace の Pod
kubectl get pods -n dev
kube-system の全オブジェクト
kubectl get all -n kube-system

-A オプションは全 namespace を一度に見ます。

全 namespace を一度に
kubectl get pods -A

-n を毎回書くのは煩雑です。現在のコンテキストの既定 namespace を変えると その後はオプション無しでもその namespace に行きます。

既定 namespace の切替
kubectl config set-context --current --namespace=dev
現在のコンテキストを確認
kubectl config view --minify | grep namespace:

このコマンドは ~/.kube/config の現在のコンテキストに既定 namespace を書いておきます。その後 kubectl get pods だけで dev の Pod が出ます。

kubens — 1 行で namespace を切替 #

上のコマンドが長いので、運用者がほぼ皆使う道具が kubens (kubectx パッケージの相方) です。1 行で namespace を切り替えられます。

kubens の使用
kubens                  # 現在の namespace 出力 + 候補一覧
kubens dev              # dev に切替
kubens -                # 直前の namespace に戻る

kubectx がクラスタ (コンテキスト) 切替用なら kubens は namespace 切替用です。両方とも 1 つのパッケージから来ます — Homebrew、apt、scoop どこでも kubectx を入れれば一緒に入ってきます。毎日 K8s を触る人にはほぼ必須に近い便利道具です。

NS 内のオブジェクトがどう互いを呼ぶか #

#5 の Service 節で見た流れをもう一度押さえる番です。同じ namespace の中に住む Pod 同士は Service 名だけで互いを呼べました。

同じ namespace の中
http://web/api          # web という Service を短い名前で

別の namespace の Service を呼ぶには名前の後ろに namespace を付けます。

別の namespace の Service
http://web.prod/api                     # 短く
http://web.prod.svc.cluster.local/api   # FQDN

K8s クラスタ内の全 Service は <service>.<namespace>.svc.cluster.local という FQDN で解決されます。短く縮めて web.prod だけ書いてもクラスタ DNS が自動で埋めてくれます。1 行で整理すると DNS が namespace 間の橋 です — namespace がオブジェクト名を分ける区画なら、DNS はその区画を超えてオブジェクトを呼べるようにする通路の役目を担います。

ラベル vs アノテーション #

ここで視点を変えて、クラスタ整理のもう 1 つの軸である ラベル に移ります。ラベルは #4 の selector から実はずっと見ていました — Deployment の spec.selector.matchLabels、Pod テンプレートの metadata.labels、Service の spec.selector がすべてラベルでオブジェクトをまとめ選び出すメカニズムです。

ラベルとよく混同されるのが アノテーション (annotation) です。両方とも metadata の下にキー-値で書く形は同じですが、用途が明らかに分かれます。

ラベル (label)アノテーション (annotation)
K8s がマッチングに使用はい (selector)いいえ
長さ・内容短く意味のあるキー-値 (数十字)任意 — 長くても OK、JSON・base64 など
用途オブジェクトの分類・選択道具・運用者が付けるメモ
キー例app=webenv=prodtier=backendprometheus.io/scrape: "true"kubectl.kubernetes.io/last-applied-configuration

1 行の差は — ラベルは検索キー、アノテーションは付箋 です。K8s のコントローラ (Deployment、Service、NetworkPolicy など) がどのオブジェクトを扱うか選ぶときに見るのがラベルで、外部道具 (Prometheus、Helm、ArgoCD、Ingress コントローラなど) や運用者がオブジェクトにメタデータを付け足すときに使うのがアノテーションです。

キーと値の制約も少し違います。ラベルは selector に使われるだけ形式が狭いです — キーは ASCII 英数字に -_. 程度、値も同様に短い文字列だけが入ります (両方とも数十字以内)。アノテーションはその制約が緩んでいて任意のテキスト・JSON も入ります。kubectl.kubernetes.io/last-applied-configuration アノテーションが丸ごとマニフェスト JSON を持っているのがその例です。

ラベルとアノテーションが一緒に書かれた metadata
metadata:
  name: web
  labels:
    app: web
    env: prod
    tier: backend
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8080"
    kubernetes.io/change-cause: "bump nginx 1.27 -> 1.28"

標準ラベル規約 — app.kubernetes.io/* #

ラベルに app=web のような独自キーを自由に付けてもいいですが、K8s コミュニティが推奨する標準ラベルセットがあります。キーの接頭辞が app.kubernetes.io/ で始まる 6 つです。

キー意味例の値
app.kubernetes.io/nameアプリ名nginxwebkafka
app.kubernetes.io/instanceこのデプロイインスタンスの識別子web-prodkafka-shop
app.kubernetes.io/versionバージョン1.272.4.1
app.kubernetes.io/component役割frontendbackenddatabase
app.kubernetes.io/part-of上位システムshop-platformanalytics
app.kubernetes.io/managed-by管理道具Helmargocdkubectl

これらのキーを使う理由は 運用道具・ダッシュボードが標準として認識するから です。Lens、k9s、Datadog、Helm のような道具がこのキーを見てオブジェクトをまとめて見せてくれます。独自キーだけ使うより互換性が明らかに良いです。独自キー (例: envteam) を一緒に使っても問題なく、標準ラベルセットと独自ラベルを一緒に書くのが一般的な形です。

標準ラベルが付いた Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: prod
  labels:
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: web-prod
    app.kubernetes.io/version: "1.27"
    app.kubernetes.io/component: frontend
    app.kubernetes.io/part-of: shop-platform
    app.kubernetes.io/managed-by: kubectl
    env: prod
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: nginx
      app.kubernetes.io/instance: web-prod
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nginx
        app.kubernetes.io/instance: web-prod
        app.kubernetes.io/version: "1.27"
        app.kubernetes.io/component: frontend
        env: prod
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80

selector の matchLabels と Pod テンプレートの labels には標準ラベルの中でも 変わらないものだけ を入れる方が安全です。version のようなキーを selector に入れるとバージョンを上げるときに selector も一緒に変えねばならず、#4 で見た selector immutability の罠にかかりやすくなります。

ラベルで選び出す — kubectl -l #

ラベルが付いていれば kubectl-l オプションでオブジェクトを選び出せます。selector の文法は単純な等号から集合表現までいくつかあります。

等号マッチ
kubectl get pods -l app=web
kubectl get pods -l env=prod,tier=backend         # AND

カンマで繋げば AND です。OR は次の集合表現で書きます。

集合表現
kubectl get pods -l 'env in (dev,staging)'
kubectl get pods -l 'env notin (prod)'
kubectl get pods -l 'tier'                         # tier ラベルがあるもの
kubectl get pods -l '!debug'                       # debug ラベルが無いもの

複数のオブジェクト種類に一緒に使うこともできます。

ラベルで複数種類を一度に
kubectl get deploy,svc,cm -l app.kubernetes.io/instance=web-prod
ラベルで一括削除 — 危険
kubectl delete pods -l env=dev

最後のコマンドは強力です — マッチする全 Pod を一度に消します。運用クラスタでラベルを誤って入れて意図より多くのオブジェクトを消してしまう事故がしばしば起きます。一括削除の前には同じ selector で get を先に回して対象が意図したものか確認するのが安全です。

-l セレクタの文法が重要な理由は — 同じ文法が Service の spec.selector、Deployment の spec.selector.matchLabels、NetworkPolicy の podSelector、ResourceQuota の scopeSelector など K8s 内のほぼ全オブジェクトのマッチングにそのまま使われるからです。ラベルを 1 度身につければその上に乗るオブジェクトの selector が自然に読めます。

仮想の運用 1 コマ — これを全部合わせると #

ここまでの道具を 1 つのマニフェスト束にまとめると、運用クラスタの基本の形が現れます。dev / staging / prod 3 つの namespace、その中に同じ名前の Deployment・Service・ConfigMap・Secret があり、ラベルで環境とバージョンが付いています。

prod namespace の Deployment + Service
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: prod
  labels:
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: web-prod
    app.kubernetes.io/version: "1.27"
    app.kubernetes.io/component: frontend
    app.kubernetes.io/part-of: shop-platform
    env: prod
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: nginx
      app.kubernetes.io/instance: web-prod
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nginx
        app.kubernetes.io/instance: web-prod
        app.kubernetes.io/component: frontend
        env: prod
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80
          envFrom:
            - configMapRef:
                name: web-config
            - secretRef:
                name: db-secret
---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: prod
  labels:
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: web-prod
    env: prod
spec:
  selector:
    app.kubernetes.io/name: nginx
    app.kubernetes.io/instance: web-prod
  ports:
    - port: 80
      targetPort: 80

dev と staging もほぼ同じマニフェストです — metadata.namespaceinstanceversionenv ラベルくらいだけが環境ごとに違って残りはそのまま。この形が 1 つのクラスタ内で環境別にオブジェクトがきれいに分かれる運用クラスタの基本です。

運用で同じマニフェストを環境ごとに少しずつ変えて適用しなければならないとき、毎回手で書き換えていれば間違いが多発します。なので登場する道具が Helm (テンプレート + 値の分離) と Kustomize (ベース + オーバーレイ) です。両方ともこのシリーズの範囲外で、K8s 上級 / 実践トラックで扱います。

片付け #

この記事で作った dev namespace を片付けます。

namespace の削除
kubectl delete ns dev
出力例
namespace "dev" deleted

この 1 行が怖いのは その namespace の中の全オブジェクトが一緒に消える点 です。Deployment、Service、Pod、ConfigMap、Secret、PVC すべて非同期で削除されます。運用クラスタでの namespace 削除はほぼ最後の手段だと見るべきです — 1 度押せばその中のデータまで飛びかねず、PV (永続ボリューム) の reclaimPolicy 設定によってはディスク自体も一緒に消えることがあります。

現在の namespace 既定値を戻す
kubectl config set-context --current --namespace=default

kubens を入れている環境なら kubens default で 1 行です。

シリーズ振り返り — 7 編で手に入ったもの #

最後の記事なので 7 編を一度整理します。

  • #1なぜ K8s か。コンテナ 1 ホストの限界、オーケストレーターが解いてくれること、コントロールプレーンとワーカの絵。
  • #2ローカルクラスタ。minikube / kind / Docker Desktop の使い分けと違い、kubectl コンテキスト。
  • #3最初の Pod。マニフェストの背骨 (apiVersion / kind / metadata / spec)、kubectl apply / get / describe / logs / exec
  • #4Deployment と ReplicaSet。宣言的デプロイ、ローリングアップデート、kubectl rollout、selector immutability。
  • #5Service。ClusterIP / NodePort / LoadBalancer の 3 段、クラスタ内部 DNS。
  • #6ConfigMap と Secret。12-factor の「設定は環境に置く」、env / envFrom / volume の 3 つの注入方式。
  • #7 Namespace とラベル — クラスタの整理法 ← この記事。

ここまでくれば K8s マニフェスト 1 枚を初めて見ても何を意味するか読み書きできる水準です。会社のクラスタのマニフェストディレクトリを開いてもオブジェクト種類とその中のフィールド名が見覚えのあるものに見えるはずです。その上に乗るより深いトピックが次のトラックです。

次 — K8s 中級 #

このシリーズで意図的に後回しにしたトピックが K8s 中級 7 編の筋書きです。

トピック説明
StatefulSet / DaemonSet / Job / CronJobDeployment 以外のコントローラ。データベース、ノードエージェント、一回限りバッチ、スケジュールバッチ。
PV / PVC / StorageClass永続データ。Pod が死んでも生き残るディスクのモデル。
Ingress + Ingress Controller外部入口を 1 か所に集める。nginx / Traefik / GKE Ingress のようなコントローラ。
resources.requests / limitsPod の CPU・メモリ要求・上限。スケジューリングと OOM の基準。
Health checkliveness / readiness / startup probe。K8s がコンテナの生存をどう判定するか。
HPA / VPA / Cluster Autoscaler負荷に合わせて Pod 数、Pod のリソース、ノード数まで自動調整。
RBAC / NetworkPolicy / ResourceQuotaこの記事で短く触れたセキュリティ・リソースポリシーの深さ。

この 7 つを扱った後は、会社のクラスタのマニフェストディレクトリに自分のマニフェストを書けるレベルに 1 歩近づきます。その次の K8s 上級 / 実践 では Helm、Kustomize、GitOps (ArgoCD / Flux)、オブザーバビリティ (Prometheus / Grafana / Loki)、クラスタ運用 (アップグレード、バックアップ、マルチクラスタ) のようなトピックを本格的に扱います。

締め #

基礎シリーズ 7 編で K8s の基礎を整理しました。単一ホストから出発してクラスタを立て、最初のマニフェストを書き、複数の Pod を安定して維持し、外部に公開し、設定と秘密値を分離する過程までを追ってきました。マニフェスト 1 枚が実際にクラスタの中でどんなオブジェクトに構成されるか、そしてそれらのオブジェクトが互いにどう繋がっているかを理解できるなら、このシリーズの目的は十分達成されたと言えます。次のトラックである K8s 中級では、この基礎の上にさらに深いトピックを順番に扱います。

X