Kubernetes Namespace

Kubernetes クラスター内の API リソースを論理的に分離し、権限とリソース枠の適用単位を切る仕組み。ノードとカーネルは共有されるためセキュリティ境界にはならない

Kubernetesコンテナ

Kubernetes Namespace とは

Namespace は、Kubernetes クラスター内の API リソースを論理的に分離する仕組みである。分離されるのは名前の有効範囲と、Namespace スコープのポリシーが届く範囲の 2 つだ。リソース名は同じ Namespace 内で一意であればよく、別の Namespace と重複してもかまわない。Namespace を入れ子にはできず、1 つのリソースが属する Namespace はちょうど 1 つである。Node や PersistentVolume のような低レベルのリソースはどの Namespace にも属さない。

環境 (dev/stg/prod)、チーム、アプリケーションごとに分けるのが一般的だが、公式ドキュメントは利用者が数人から数十人程度のクラスターなら Namespace を意識する必要はないという立場を取る。同じソフトウェアのバージョン違いを並べたいだけならラベルで足りる。

基本的な使い方

kubectl-n を省略すると、現在のコンテキストに設定された Namespace を対象にする。作業対象が一定ならコマンドごとに指定するのではなくコンテキスト側に焼き込んだほうが、production のつもりで staging を触る事故が減る。

# Namespace の作成
kubectl create namespace production
kubectl create namespace staging

# Namespace を指定してリソースを操作
kubectl get pods -n production
kubectl apply -f deployment.yaml -n production

# 現在のコンテキストの既定 Namespace を切り替える
kubectl config set-context --current --namespace=production

# Namespace に属さないリソースを一覧する
kubectl api-resources --namespaced=false
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    env: production
    team: backend

最初から存在する 4 つの Namespace

Kubernetes はクラスター作成時に次の 4 つを用意する。kube- で始まる名前はシステム用に予約されているため、自前の Namespace には使わない。

Namespace用途
defaultNamespace を指定しない場合の既定
kube-systemKubernetes システムコンポーネント (CoreDNS, kube-proxy)
kube-public未認証のクライアントを含む全クライアントが読み取れる。公開情報を置く場という位置づけは慣習であって強制ではない
kube-node-lease各ノードに対応する Lease オブジェクトを保持し、kubelet のハートビートを受ける

default にアプリケーションを置くのは避ける。公式ドキュメントも本番クラスターでは default を使わないことを勧めている。誰が何を置いたか追えなくなるうえ、Namespace 単位の権限もリソース枠も掛けようがなくなる。

Namespace の分割パターン

環境ごと (小規模チーム)

namespace: development
namespace: staging
namespace: production

チームごと (中〜大規模)

namespace: team-backend
namespace: team-frontend
namespace: team-data

アプリケーションごと

namespace: order-service
namespace: payment-service
namespace: notification-service

粒度に迷ったら、権限 (RBAC) とリソース枠 (ResourceQuota) を分けたい単位に合わせると判断がぶれない。Namespace はこの 2 つの適用単位そのものであり、逆に言えば権限も枠も同じでよいものを割っても、管理対象の Namespace が増えるだけで得るものがない。

Namespace をまたぐ参照は FQDN で書く

Service を作ると <service 名>.<namespace 名>.svc.cluster.local という DNS 名が割り当たる。コンテナ<service 名> だけを指定した場合は自分と同じ Namespace の Service に解決されるため、development / staging / production で同じマニフェストを使い回せる。逆に Namespace をまたいで参照するときは FQDN を書く必要がある。この規則があるので、Namespace 名は RFC 1123 の DNS ラベルとして妥当な文字列でなければならない。

公式ドキュメントは、公開トップレベルドメインと同じ名前の Namespace を作ると、末尾のドットを付けない名前解決がその Namespace の Service に吸われ、公開 DNS より優先されると警告している。Namespace の作成権限を絞る理由の 1 つだ。

RBAC による Namespace レベルのアクセス制御

Role と RoleBinding は Namespace スコープのリソースである。組み込みの ClusterRole を RoleBinding から参照すると、その権限は RoleBinding を置いた Namespace の中だけで効く。

# backend チームに production Namespace の読み取り権限を付与
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: backend-readonly
  namespace: production
subjects:
  - kind: Group
    name: backend-team
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: rbac.authorization.k8s.io

組み込みの view は Namespace 内のほとんどのオブジェクトを読めるが、Secret と Role / RoleBinding は読めない。Secret の中身が読めれば ServiceAccount の資格情報を経由して権限昇格できてしまうためで、「読み取り専用ロールだから全部見えるはず」と考えると期待とずれる。また Node や PersistentVolume のようなクラスタースコープのリソースは、RoleBinding をいくら足しても Namespace 側からは扱えない。

ResourceQuota でリソース制限

Namespace ごとに CPU、メモリ、Pod 数の上限を設定し、1 つの Namespace がクラスター全体のリソースを占有するのを防ぐ。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "10"
    requests.memory: 20Gi
    limits.cpu: "20"
    limits.memory: 40Gi
    pods: "50"

運用で踏みやすい点が 3 つある。1 つ目は、requests.cpurequests.memory に枠を置いた Namespace では Pod 側がその値を明示しないと作成が拒否されること。全員に書かせたくなければ LimitRange で既定値を与える。2 つ目は、枠を超える要求が HTTP 403 で弾かれるのは Pod の作成時点であり、Deployment の作成自体は成功してしまうこと。理由は Deployment ではなく ReplicaSet のイベントに出るため、「Pod が予定数まで増えない」という症状から辿ることになる。3 つ目は、枠の変更も枠の逼迫も既存のリソースには遡らないこと。上限を下げても、すでに走っている Pod が退去させられるわけではない。

NetworkPolicy による通信制御

既定では Pod は入力も出力も隔離されておらず、Namespace をまたぐ通信も含めてすべて通る。NetworkPolicy を作ると、そのポリシーが選択した Pod が選択した方向について隔離され、許可リストに挙がった通信だけが残る。入力側と出力側の隔離は独立に宣言する点に注意する。下は production の Pod への入力を同じ Namespace 内からだけに絞る例である。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-from-other-namespaces
  namespace: production
spec:
  podSelector: {}
  ingress:
    - from:
        - podSelector: {}  # 同じ Namespace 内からのみ許可

NetworkPolicy を実際に施行するのはネットワークプラグインであり、対応していない構成ではオブジェクトを作っても何も起きない。しかもエラーにはならないため、絞ったつもりで開いたままという状態に気づけない。導入時はまず拒否ポリシーを 1 つ置いて、本当に遮断されるかを通信で確かめる。

他の Namespace からの通信を許可する場合は namespaceSelector を使う。Kubernetes 1.22 以降はコントロールプレーンが全 Namespace に変更不可のラベル kubernetes.io/metadata.name (値は Namespace 名) を付けるので、ラベルを自分で付け忘れた Namespace も名前で選択できる。

Namespace はセキュリティ境界ではない

Namespace が分けるのは API リソースの名前空間とポリシーの適用範囲であって、実行基盤そのものではない。RBAC を正しく設定すれば API 経由のアクセスは Namespace 単位で止まるが、止まらないものが残る。

  • 別の Namespace の Pod でも同じノードに載り、ホストのカーネルを共有する。コンテナからホストへ抜ける脆弱性を踏まれれば、そのノード上の他テナントのプロセスやファイルに手が届く。
  • ネットワークは既定で全通で、NetworkPolicy を置くまで Namespace 間の通信は素通りする。しかも通信は既定で暗号化されない。
  • Node、PersistentVolume、StorageClass、CustomResourceDefinition はどの Namespace にも属さない。クラスタースコープの権限を渡した時点で、Namespace の区切りは意味を持たない。

テナント間に強い分離が要るなら、テナントごとにノードを分ける、サンドボックス化されたランタイムを使う、クラスターそのものを分けるといった実行基盤側の手段を併用する。

削除は配下ごと消える

kubectl delete namespaces <名前> は、その Namespace の配下にあるものをすべて削除する。削除は非同期で、完了までしばらく Terminating 状態が見える。取り返しがつかない操作なので、Namespace の削除権限は作成権限と同じ扱いにせず切り分けておく。

現場での応用を知るには関連書籍も役立つ。

この記事は役に立ちましたか?

関連用語

関連する記事