KubernetesのHierarchical Namespacesによるpropagationを完全に理解した
はじめに
この記事ではKubernetesのHierarchical Namespaces(以下、HNS)について紹介します。
Kubernetesのクラスタを運用していると、マルチテナンシーやリソース効率化などのためにnamespaceを活用することがあります。namespaceはRBACやNetworkPoliciesなどを境界づけるために有用ですが、一方で制約や管理の難しさの問題もあります。そうした
問題を解消する手段のひとつとして、マルチテナンシーのワーキンググループにおいてHNSの開発が進められています。
HNSではその名の通り、階層型namespaceを作れます。この階層構造のメリットとして、一部のresourceをnamespaceで分離しつつ、階層の親子関係での伝搬(propagation)を可能にします。個人的には、AWSの組織単位(Organization Unit)やGCPのフォルダに近いものをイメージしています。
開発状況
記事の公開時点での最新バージョンはv0.9.0
です。
productionで利用可能なv1.0
のETAが2021 late
とされているため、そう遠くはないうちにリリースされると予想しています。
クラウドベンダーではGCPでのみベータ版として対応しています。
前置きはこのぐらいにして、実際にnamespaceを作りながら試してみましょう。
サンプルコードはこちらのリポジトリに置いておきます。
kubectl-hnsのinstallとHierarchical Namespace Controllerのapply
はじめにkindでクラスタを作成し、Krewで kubectl-hns
をインストールします。続いてHierarchical Namespace Controller(以下、HNC)のmanifestをapplyします。
$ kind create cluster --name hns-cluster-sample
$ kubectl krew install hns
$ kubectl hns version
kubectl-hns version v0.9.0
$ kubectl apply -f https://github.com/kubernetes-sigs/hierarchical-namespaces/releases/download/v0.9.0/hnc-manager.yaml
階層型Namespaceを作ってみる
従来通りのkubectl create namespace
でparent namespaceを作り、そのあとにkubectl hns create
でそれぞれchild namespace、grandchild namespaceを作成します。
$ kubectl create ns parent
$ kubectl hns create child -n parent
$ kubectl hns create grandchild -n child
kubectl get namespaces
で確認できますが、一見すると今までと変わらないnamespaceのように見えます。
$ kubectl get namespaces -A | grep -E "parent|child"
child Active 2m2s
grandchild Active 47s
parent Active 2m28s
しかし、kubectl hns tree
を実行するとこのようにnamespaceの階層を確認できます。kubectl hns create
で作られたnamespaceの前には [s]
が表示されています。これらはSubnamespaceと呼ばれ、従来のnamespace(Full Namespace)とは明確に区別されます。
$ kubectl hns tree parent
parent
└── [s] child
└── [s] grandchild
[s] indicates subnamespaces
kubectl hns describe
で階層の親子関係を詳細に確認できます。
$ kubectl hns describe parent
Hierarchy configuration for namespace parent
No parent
Children:
- child (subnamespace)
No conditions
No recent HNC events for objects in this namespace
$ kubectl hns describe child
Hierarchy configuration for namespace child
Parent: parent
Children:
- grandchild (subnamespace)
No conditions
No recent HNC events for objects in this namespace
$ kubectl hns describe grandchild
Hierarchy configuration for namespace grandchild
Parent: child
No children
No conditions
No recent HNC events for objects in this namespace
kubectl describe namespace
で確認すると、HNC用のラベルとアノテーションが付与されています。
key | value | 意味 |
---|---|---|
Label | *.tree.hnc.x-k8s.io/depth |
ある祖先のnamespaceを基準とした階層の深さ |
Label | hnc.x-k8s.io/included-namespace |
(調べたもののわからず) |
Annotations | hnc.x-k8s.io/subnamespace-of |
subnamespaceの親 |
$ kubectl describe namespaces parent
Name: parent
Labels: hnc.x-k8s.io/included-namespace=true
kubernetes.io/metadata.name=parent
parent.tree.hnc.x-k8s.io/depth=0
Annotations: <none>
Status: Active
No resource quota.
No LimitRange resource.
$ kubectl describe namespaces child
Name: child
Labels: child.tree.hnc.x-k8s.io/depth=0
hnc.x-k8s.io/included-namespace=true
kubernetes.io/metadata.name=child
parent.tree.hnc.x-k8s.io/depth=1
Annotations: hnc.x-k8s.io/subnamespace-of: parent
Status: Active
No resource quota.
No LimitRange resource.
$ kubectl describe namespaces grandchild
Name: grandchild
Labels: child.tree.hnc.x-k8s.io/depth=1
grandchild.tree.hnc.x-k8s.io/depth=0
hnc.x-k8s.io/included-namespace=true
kubernetes.io/metadata.name=grandchild
parent.tree.hnc.x-k8s.io/depth=2
Annotations: hnc.x-k8s.io/subnamespace-of: child
Status: Active
No resource quota.
No LimitRange resource.
また、新たに追加されたresourceであるsubnamespaceanchorも確認できます。
$ kubectl api-resources | grep subnamespaceanchors
subnamespaceanchors subns hnc.x-k8s.io/v1alpha2 true SubnamespaceAnchor
$ kubectl get subnamespaceanchors -A
NAMESPACE NAME AGE
child grandchild 105s
parent child 113s
RoleとRoleBindingを伝搬させる
階層型namespaceができたところで、roleとrolebindingを伝搬させてみましょう。
以下のようなディレクトリとファイルを用意します。
$ tree .
.
├── base
│ ├── kustomization.yaml
│ └── subnamespace-admin.yaml
├── child
│ └── kustomization.yaml
├── grandchild
│ └── kustomization.yaml
└── parent
└── kustomization.yaml
defaultのserviceaccount(system:serviceaccount:${namespace}:default
)にnamespacesとsubnamespaceanchorsのroleをbindingします。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: subnamespace-admin
rules:
- apiGroups: ["hnc.x-k8s.io"]
resources: ["namespaces", "subnamespaceanchors"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: subnamespace-admin-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: subnamespace-admin
subjects:
- kind: ServiceAccount
name: default
resources:
- subnamespace-admin.yaml
namespace: parent
namePrefix: parent-
resources:
- ../base
namespace: child
namePrefix: child-
resources:
- ../base
namespace: grandchild
namePrefix: grandchild-
resources:
- ../base
parent namespaceのapply
parentのkustomization.yamlのみをapplyしてみます。
$ kubectl apply -k parent
parent namespaceにroleが作成されているのはもちろんですが、child namespaceとgranchild namespaceにも作成されている結果に注目してください。
$ kubectl get role -A | grep -E "NAME|parent"
NAMESPACE NAME CREATED AT
child parent-subnamespace-admin 2021-12-21T14:27:44Z
grandchild parent-subnamespace-admin 2021-12-21T14:27:44Z
parent parent-subnamespace-admin 2021-12-21T14:27:44Z
parent namespaceのroleは至って普通です。
$ kubectl describe role parent-subnamespace-admin -n parent
Name: parent-subnamespace-admin
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
namespaces.hnc.x-k8s.io [] [] [*]
subnamespaceanchors.hnc.x-k8s.io [] [] [*]
child namespaceとgrandchild namespaceのroleは、同じPolicyRuleでなおかつHNCのLabelが付与されています。
$ kubectl describe role parent-subnamespace-admin -n child
Name: parent-subnamespace-admin
Labels: app.kubernetes.io/managed-by=hnc.x-k8s.io
hnc.x-k8s.io/inherited-from=parent
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
namespaces.hnc.x-k8s.io [] [] [*]
subnamespaceanchors.hnc.x-k8s.io [] [] [*]
$ kubectl describe role parent-subnamespace-admin -n grandchild
Name: parent-subnamespace-admin
Labels: app.kubernetes.io/managed-by=hnc.x-k8s.io
hnc.x-k8s.io/inherited-from=parent
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
namespaces.hnc.x-k8s.io [] [] [*]
subnamespaceanchors.hnc.x-k8s.io [] [] [*]
rolebindingでも同様です。
$ kubectl get rolebinding parent-subnamespace-admin-binding -n parent
NAME ROLE AGE
parent-subnamespace-admin-binding Role/parent-subnamespace-admin 99m
$ kubectl get rolebinding -A \
-l app.kubernetes.io/managed-by=hnc.x-k8s.io \
-l hnc.x-k8s.io/inherited-from=parent
NAMESPACE NAME ROLE AGE
child parent-subnamespace-admin-binding Role/parent-subnamespace-admin 99m
grandchild parent-subnamespace-admin-binding Role/parent-subnamespace-admin 99m
このようにsubnamespaceを持つnamespaceに特定のresourceを作成すると、その子孫のsubnamespaceにおいても複製されます。
これがHNSによる伝搬(propagation)です。
system:serviceaccount:parent:defaultの権限
実際にsystem:serviceaccount:parent:default
のroleを確認してみましょう。
$ kubectl auth can-i --list -n parent --as system:serviceaccount:parent:default | grep -E "Resources|namespace"
Resources Non-Resource URLs Resource Names Verbs
namespaces.hnc.x-k8s.io [] [] [*]
subnamespaceanchors.hnc.x-k8s.io [] [] [*]
例えば、parent namespaceにおいてsubnamespaceanchorのlistは取得可能ですが、podsのlistは取得できません。
$ kubectl get subns -n parent --as system:serviceaccount:parent:default
NAME AGE
child 2d4h
$ kubectl get pods -n parent --as system:serviceaccount:parent:default
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:parent:default" cannot list resource "pods" in API group "" in the namespace "parent"
これはchild namespace、grandchild namespaceにおいても同様です。
$ kubectl get subns -n child --as system:serviceaccount:parent:default
NAME AGE
grandchild 2d4h
$ kubectl get subns -n grandchild --as system:serviceaccount:parent:default
No resources found in grandchild namespace.
system:serviceaccount:parent:default
のroleとrolebindingがparent namespaceの子孫のsubnamespaceでも複製され、有効であると確認できました。
SubnamespaceによるNamespaceの権限移譲
このpropagationが有効活用されうるのはどのようなケースでしょうか。
従来のnamespace(Full Namespace)の課題として、namespaceの権限はcluster wide scopeであり、namespaceの管理のためには強い権限を付与しなければなりませんでした。
先ほどの例でいうと、system:serviceaccount:parent:default
にはあえてnamespaceのroleをbindしましたが、このroleではFull Namespaceを作成できません。
$ kubectl create ns test --as system:serviceaccount:parent:default
Error from server (Forbidden): namespaces is forbidden: User "system:serviceaccount:parent:default" cannot create resource "namespaces" in API group "" at the cluster scope
これはCluster Roleでの付与が必要です。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-admin
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: namespace-admin-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: namespace-admin
subjects:
- kind: ServiceAccount
name: default
$ kubectl create ns test --as system:serviceaccount:parent:default
namespace/test created
$ kubectl delete namespaces test --as system:serviceaccount:parent:default
namespace "test" deleted
一方で、subnamespaceはresource: ["subnamespaceanchors"]
のroleのみで作成できます。
ここでクラスタにおけるnamespaceの権限分離について考えてみましょう。仮にクラスタの管理者と開発者で権限を分ける運用を想定した場合、管理者としてはnamespaceのclusterroleを開発者に委譲してむやみにnamespaceが作成される事態を避けたいと考えるかもしれません。そうしたときの代替案として、開発者が新しいnamespaceを必要になったときに、管理者にその都度依頼を出して作成してもらう運用が考えられます。これによって不用意な権限委譲は避けられますが、一方で都度依頼を出さなければいけない煩わしさが残ります。
そこで、subnamespaceがこの問題を解決します。
namespaceはnon-namescoped resourceですが、subnamespaceanchorはnamespaced resourceです。
$ kubectl api-resources | grep -E "NAME|namespace"
NAME SHORTNAMES APIVERSION NAMESPACED KIND
namespaces ns v1 false Namespace
subnamespaceanchors subns hnc.x-k8s.io/v1alpha2 true SubnamespaceAnchor
今回の例ではclusterroleを付与せず、すでに付与されているroleでsubnamespaceを作成できます。その権限はparent namespaceの階層下にのみ有効です。
$ kubectl hns create child2 -n parent --as system:serviceaccount:parent:default
$ kubectl hns create grandchild2 -n child2 --as system:serviceaccount:parent:default
$ kubectl hns tree parent
parent
├── [s] child
│ └── [s] grandchild
└── [s] child2
└── [s] grandchild2
新たにchild2 namespace、grandchild2 namespaceを作成できました。
一方で、roleはparent namespaceのscopeのため、それ以外のnamespaceに対してsubnamespaceを作成できません。
$ kubectl hns create child3 -n default --as system:serviceaccount:parent:default
Could not create subnamespace anchor.
Reason: subnamespaceanchors.hnc.x-k8s.io "child3" is forbidden: User "system:serviceaccount:parent:default" cannot create resource "subnamespaceanchors" in API group "hnc.x-k8s.io" in the namespace "default"
これまでのsystem:serviceaccount:parent:default
の権限をまとめると、おおよそ以下のとおりです(すべての組み合わせまでは確認できていません)。
操作 | コマンド例 | 権限あり | 権限がない理由 |
---|---|---|---|
新規Full Namespaceの作成 | kubectl create namespace new-namespace |
× | namespaceのClusterRoleがないため |
既存のFull Namespaceへの操作 |
kubectl get namespaces kubectl delete namespace another-namespace
|
× | namespaceのClusterRoleがないため |
自身のFull Namespaceへの操作 |
kubectl describe namespace parent kubectl delete namespace parent
|
× | namespaceのClusterRoleがないため |
新規subnamespaceの作成 | kubectl hns create new-subnamespace -n parent |
○ | |
既存のsubnamespaceへの操作 |
kubectl delete subnamespaceanchor child -n parent kubectl delete subns grandchild -n child
|
○ | |
子孫ではないsubnamespaceへの操作 | kubectl get subnamespaceanchors -n default |
× | subnamespaceanchorのRoleがparent namespace scopeのため |
このようにnamespaceに閉じられたsubnamespaceanchorのroleによって、clusterroleを付与せずnamespaceを管理できます。
child namespace・grandchild namespaceのapply
残りのchild namespace、grandchild namespaceのkustomization.yamlもapplyしてみましょう。
$ kubectl apply -k child
$ kubectl apply -k grandchild
subnamespace-adminとsubnamespace-admin-bindingがchild namespaceに作られ、grandchild namespaceに伝搬します。grandchildのそれらはgrandchild namespaceにのみ作成されます。
$ kubectl get role -A | grep -E "NAME|parent|child"
NAMESPACE NAME CREATED AT
child child-subnamespace-admin 2021-12-21T19:57:22Z
child parent-subnamespace-admin 2021-12-21T19:54:48Z
grandchild child-subnamespace-admin 2021-12-21T19:57:22Z
grandchild grandchild-subnamespace-admin 2021-12-21T19:57:28Z
grandchild parent-subnamespace-admin 2021-12-21T19:55:14Z
parent parent-subnamespace-admin 2021-12-21T14:27:44Z
$ kubectl get rolebinding -A | grep -E "NAME|parent|child"
NAMESPACE NAME ROLE AGE
child child-subnamespace-admin-binding Role/child-subnamespace-admin 7m10s
child parent-subnamespace-admin-binding Role/parent-subnamespace-admin 9m44s
grandchild child-subnamespace-admin-binding Role/child-subnamespace-admin 7m10s
grandchild grandchild-subnamespace-admin-binding Role/grandchild-subnamespace-admin 7m3s
grandchild parent-subnamespace-admin-binding Role/parent-subnamespace-admin 9m18s
parent parent-subnamespace-admin-binding Role/parent-subnamespace-admin 5h36m
system:serviceaccount:child:defaultの権限
system:serviceaccount:child:default
の権限を確認します。
祖先となるparent namespaceへの操作はもちろん失敗します。
$ kubectl get subns -n parent --as system:serviceaccount:child:default
Error from server (Forbidden): subnamespaceanchors.hnc.x-k8s.io is forbidden: User "system:serviceaccount:child:default" cannot list resource "subnamespaceanchors" in API group "hnc.x-k8s.io" in the namespace "parent"
$ kubectl hns create child-2 -n parent --as system:serviceaccount:child:default
Could not create subnamespace anchor.
Reason: subnamespaceanchors.hnc.x-k8s.io "child-2" is forbidden: User "system:serviceaccount:child:default" cannot create resource "subnamespaceanchors" in API group "hnc.x-k8s.io" in the namespace "parent"
child namespace階層下の操作は成功します。
$ kubectl get subns -n child --as system:serviceaccount:child:default
NAME AGE
grandchild 25m
$ kubectl get subns -n grandchild --as system:serviceaccount:child:default
No resources found in grandchild namespace.
$ kubectl hns create grandchild-2-by-child -n child --as system:serviceaccount:child:default
Successfully created "grandchild-2-by-child" subnamespace anchor in "child" namespace
system:serviceaccount:grandchild:defaultの権限
system:serviceaccount:grandchild:default
も同様です。
権限なし
$ kubectl get subns -n parent --as system:serviceaccount:grandchild:default
$ kubectl get subns -n child --as system:serviceaccount:grandchild:default
$ kubectl hns create child-2 -n parent --as system:serviceaccount:grandchild:default
$ kubectl hns create child-2 -n child --as system:serviceaccount:grandchild:default
権限あり
$ kubectl get subns -n grandchild --as system:serviceaccount:grandchild:default
$ kubectl hns create great-grandson-by-grandchild -n grandchild --as system:serviceaccount:grandchild:default
例としてこのような階層になります。
$ kubectl hns tree parent
parent
└── [s] child
├── [s] grandchild
│ └── [s] great-grandson-by-grandchild
└── [s] grandchild-2-by-child
実践的なHNS
より実践的には以下のようなHNSの階層も考えられるでしょう(どこまでシングルクラスタで粘るかの是非はさておき)。
$ kubectl hns tree -A
department-a
├── [s] team-1
│ ├── [s] product-x
│ │ ├── [s] product-x-dev
│ │ ├── [s] product-x-prd
│ │ ├── [s] product-x-qa
│ │ └── [s] product-x-stg
│ └── [s] product-y
│ ├── [s] product-y-dev
│ ├── [s] product-y-prd
│ ├── [s] product-y-qa
│ └── [s] product-y-stg
└── [s] team-2
└── [s] product-z
department-b
└── [s] team-3
propagate可能なresource
その他の仕様の一部をかんたんに紹介します。
デフォルトではRBACのroleとrolebindingのみです。
kubectl hns config
またはkubectl get hncconfiguration
で確認・変更できます。
$ kubectl hns config set-resource [resource] --group [group] --mode [Propagate|Remove|Ignore]
$ kubectl hns config describe
Synchronized resources:
* Propagating: rolebindings (rbac.authorization.k8s.io/v1)
* Propagating: roles (rbac.authorization.k8s.io/v1)
Conditions:
$ kubectl get -o yaml hncconfiguration
apiVersion: v1
items:
- apiVersion: hnc.x-k8s.io/v1alpha2
kind: HNCConfiguration
metadata:
creationTimestamp: "2021-12-19T11:39:43Z"
generation: 57
name: config
resourceVersion: "70215"
uid: 1a2275de-b5b4-4be3-849a-3d0939384732
spec: {}
status:
resources:
- group: rbac.authorization.k8s.io
mode: Propagate
numPropagatedObjects: 26
numSourceObjects: 3
resource: rolebindings
version: v1
- group: rbac.authorization.k8s.io
mode: Propagate
numPropagatedObjects: 15
numSourceObjects: 3
resource: roles
version: v1
kind: List
metadata:
resourceVersion: ""
selfLink: ""
他には設定により以下のresourceや、custom resourceもpropagateできます。
- Network Policies
- Limit Ranges
- Resource Quotas
- Secrets
- Config Maps
propagationのモード
以下の3つのモードによって設定を変更できます。
- Propagate
子孫のnamespaceにresourceを複製する。 - Remove
複製元のresourceは保持して複製されたresourceを削除する。 - Ignore
複製元のresourceの作成・変更・削除を伝搬しなくなるが、複製されたresourceを削除せずに残す。roleとrolebindingを除いた他のresourceのデフォルト。
まとめ
実際に階層型namespaceを作成してresourceのpropagationを試してみました。今回紹介した仕様はほんの一部にすぎないので、より詳しく知りたい方はユーザーガイドに一通り目を通してみてください。
参考リンク
Discussion