@amsy810's Blog

~Kubernetes / Cloud Native / Docker / etc~

Hierarchical Namespace で Namespace を階層構造に管理してオブジェクトを伝搬させる

(12/4 11:00 requiredChild について追記)

こんにちは。CyberAgent の青山(@amsy810)です。

この記事は Advent Calendar 1日目の記事です。 毎回なぜ一日目にしてしまったんだろうかと後悔します。

今回は、KubeCon + CloudNativeCon NA 2019 で併設実施された Kubernetes Contributors Summit で聞いて気になっていた、Kubernetes Multi-tenancy WG で開発が進められている、Hierarchical Namespace のお話をします。

一言で言うならば、これは 「Namespace に階層構造の概念を取り入れて、ポリシーやSecretなどのリソースを伝搬させる仕組み」 です。 マイクロサービスチームや組織構造の都合上、同じオブジェクト(ConfigMap, Namespace, ResourceQuota, LimitRange, NetworkPolicy)を登録しておきたいケースは多々あると思います。 また、RBAC の設定を伝搬させることで、下位のチームに権限移譲の設定を上手く扱うことも可能です。

事前準備

事前準備として kubectl と kustomize をインストールしておいてください。 また、今回は下記のバージョンで実施しました。

  • Kubernetes: v1.16.0
  • Kustomize: v3.4.0
  • Hierarchical Namespace Controller: 7db19627f5d21a7a368beaa9d7b9be177d5bb26a (Git commit hash)

Hierarchical Namespace Controller (HNC)のインストール

Hierarchical Namespace Controller は Multi-tenancy WG のリポジトリの中にディレクトリが切られ、開発が進められています。 なお、Hierarchical Namespace Controller は現状まだ "very early software" と言われている通り、PoC 相当の Controller です。

https://twitter.com/aludwin/status/1196520861554991104?s=20

ソースコードを落としてきて、make deploy することでクラスタにインストールする事が可能です。今回は、 masayaaoyama/hnc-controller:7db1962 にビルドしたイメージを公開してあります。

cd
git clone https://github.com/kubernetes-sigs/multi-tenancy.git
# curl -o https://raw.githubusercontent.com/kubernetes-sigs/multi-tenancy/master/incubator/hnc/hack/hnc.yaml
cd multi-tenancy/incubator/hnc

IMG=masayaaoyama/hnc-controller:$(git rev-parse --short HEAD) GOPATH=~/go make deploy

階層化された Namespace の作成

Namespace を作成します。今回は特定のプロジェクトの Namespace (sample-project)を起点として、3階層で構成されている構造を作り出します。

root の Namespace を作成したら、それぞれに親子関係を作るため、 kubectl hnc コマンドを実行します。なお、このコマンドは kubectl plugins が make deploy した際にインストールされています。 実質的には後で説明する HierarchyConfiguration リソースの操作をしているだけです。

kubectl hnc set sample-project --requiredChild team-a
kubectl hnc set sample-project --requiredChild team-b

kubectl hnc set team-a --requiredChild team-a-service1
kubectl hnc set team-a --requiredChild team-a-service2

kubectl hnc set team-b --requiredChild team-b-service1
kubectl hnc set team-b --requiredChild team-b-service2
kubectl hnc set team-b --requiredChild team-b-service3

作成したあとに、Namespace の階層構造を確認してみると、下記のとおりです。 正しく3層の構造が出来ていることが確認できます。

$ kubectl hnc tree --all-namespaces
cert-manager
default
docker
hnc-system
kube-node-lease
kube-public
kube-system
sample-project
├── team-a
│   ├── team-a-service1
│   └── team-a-service2
└── team-b
    ├── team-b-service1
    ├── team-b-service2
    └── team-b-service3

--requiredChild を指定して作成した Namespace が消された場合、reconcile されて再度 Namespace が作成されます。また、「kubectl hnc set team-a --parent sample-project」というコマンドで、この Namespace 作成の reconcile を無効化した状態で階層構造を作ることも可能です。

$ kubectl delete ns team-a
namespace "team-a" deleted

$ kubectl hnc tree sample-project
sample-project
├── team-a
│   ├── team-a-service1
│   └── team-a-service2
└── team-b
    ├── team-b-service1
    ├── team-b-service2
    └── team-b-service3

ポリシーの伝搬の確認

今回は、ConfigMap リソースを作成し、正しくオブジェクトが伝搬していることを確認します。 sample-project 直下に作成した web-config オブジェクトは 8 つの Namespace(伝搬したのは 7 つ)に作られているのに対し、team-a 直下に作成した web-config2 オブジェクトは 3 つの Namespace(伝搬したのは 2 つ)に作られています。

$ kubectl -n sample-project create configmap web-config --from-literal=connection.max=100
$ kubectl -n team-a create configmap web-config2 --from-literal=connection.max=200

# 並び順は見やすいように変えています
$ kubectl get configmap -A | grep "web-config "
sample-project    web-config                           1      57s
team-a            web-config                           1      57s
team-a-service1   web-config                           1      57s
team-a-service2   web-config                           1      57s
team-b            web-config                           1      57s
team-b-service1   web-config                           1      57s
team-b-service2   web-config                           1      57s
team-b-service3   web-config                           1      56s
team-a            web-config2                          1      42s
team-a-service1   web-config2                          1      42s
team-a-service2   web-config2                          1      42s

ポリシー系のオブジェクトのみ親から子へ Sync される様になっています。

伝搬対象のオブジェクトは、下記の 7 種類です。 https://github.com/kubernetes-sigs/multi-tenancy/blob/master/incubator/hnc/pkg/config/gvk.go#L14

var GVKs = []schema.GroupVersionKind{
    {Group: "", Version: "v1", Kind: "Secret"},
    {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"},
    {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"},
    {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"},
    {Group: "", Version: "v1", Kind: "ResourceQuota"},
    {Group: "", Version: "v1", Kind: "LimitRange"},
    {Group: "", Version: "v1", Kind: "ConfigMap"},
}

なお、"type: kubernetes.io/service-account-token" の Secret は除外されています。 https://github.com/kubernetes-sigs/multi-tenancy/blob/7db19627f5d21a7a368beaa9d7b9be177d5bb26a/incubator/hnc/pkg/controllers/object_controller.go#L303-L326

個人的には ServiceAccount も sync してほしい気がしますが、何かしらの理由があるのでしょう。

階層構造の仕組み

階層構造は HierarchyConfiguration リソースを利用して実現しています。 このリソースには、 spec.parentspec.requiredChildren のフィールドが用意されており、kubectl hnc set team-a --requiredChild team-a-service1 および kubectl hnc set team-a --requiredChild team-a-service2 コマンドを実行すると、 team-a Namespace に hierarchy という名前の HierarchyConfiguration リソースが作成されます。 また、status にはその Namespace の子 Namespace が追加されるようになっています。

$ kubectl get hierarchyconfigurations -n team-a hierarchy -o yaml
apiVersion: hnc.x-k8s.io/v1alpha1
kind: HierarchyConfiguration
metadata:
  creationTimestamp: "2019-11-30T16:27:49Z"
  generation: 5
  name: hierarchy
  namespace: team-a
  resourceVersion: "20753"
  selfLink: /apis/hnc.x-k8s.io/v1alpha1/namespaces/team-a/hierarchyconfigurations/hierarchy
  uid: 29a4e049-85fa-4e95-adb3-3826a6fe1255
spec:
  parent: sample-project
  requiredChildren:
  - team-a-service1
  - team-a-service2
status:
  children:
  - team-a-service1
  - team-a-service2

なお、子 Namespace にコピーされたオブジェクトには、コピー元の親 Namespace 名がラベルとして付与されています( hnc.x-k8s.io/inheritedFrom )。

$ kubectl -n team-a-service1 get configmap web-config2 -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
  labels:
    hnc.x-k8s.io/inheritedFrom: team-a
data:
  connection.max: "100"

なお、sample-project から伝搬されている web-config team-a のデータを消した場合は、team-a 配下の Namespace の web-config オブジェクトがすべて削除されます。

$ kubectl delete configmap web-config -n team-a
configmap "web-config" deleted

$ kubectl get configmap -A | grep "web-config "
sample-project    web-config                           1      48m
team-b-service1   web-config                           1      48m
team-b-service2   web-config                           1      48m
team-b-service3   web-config                           1      48m
team-b            web-config                           1      48m

プロポーザルには「HNCによって伝播されるオブジェクトは、変更または削除できない」と書いてありましたが、現状は削除可能になっています。 現状では、子の Namespace に登録されているリソースを削除できてしまうので削除すると、復元されないので注意してください。将来的には ValidationWebhook などが入るのだと思います。

なお、親 Namespace のオブジェクトを削除した場合は、子 Namespace のオブジェクトも再帰的に削除していきます。

もう少し DeepDive

Hierarchical Namespace Controller (HNC)は大きく分けて下記の 2 つの Controller( Hierarchy Controller , Object Controller )から構成されています。 kube-controller-manager と同じような構成ですね。

  • Hierarchy Controller
    • HierarchyConfiguration から Namespace の階層構造を status に書き込む
    • 同様に Labels の付与なども行っている
  • Object Controller
    • 親 Namespace のオブジェクトを子 Namespace にコピーするコントローラ

Hierarchy Controller

reconcile 関数はこの辺りから。 https://github.com/kubernetes-sigs/multi-tenancy/blob/master/incubator/hnc/pkg/controllers/hierarchy_controller.go#L104

実際に Label を付与したり、status を更新する処理は下記のあたりから呼び出されています。(書き込みは別途) https://github.com/kubernetes-sigs/multi-tenancy/blob/master/incubator/hnc/pkg/controllers/hierarchy_controller.go#L149

Object Controller

Recncile 関数はこの辺りから。 https://github.com/kubernetes-sigs/multi-tenancy/blob/7db19627f5d21a7a368beaa9d7b9be177d5bb26a/incubator/hnc/pkg/controllers/object_controller.go#L51

オブジェクトをコピーしてるのはこの辺り。(inst オブジェクトを dests[] namespaces にコピー) https://github.com/kubernetes-sigs/multi-tenancy/blob/7db19627f5d21a7a368beaa9d7b9be177d5bb26a/incubator/hnc/pkg/controllers/object_controller.go#L217-L252

GCP の Anthos でも階層構造の伝搬の仕組みがあるらしい?

また、Anthos Config Management にも同様に階層構造に伝搬するように、Gitリポジトリ内で階層構造を作って同様のことをしているようです。(Anthos 触ってみたい)

Google’s Anthos Config Management (ACM) supports a similar model to this proposal by modelling a namespace hierarchy in the filesystem of a monolithic Git repo, and copies objects from parent namespaces to child namespaces in a K8s apiserver. By contrast, the Hierarchical Namespace Controller (HNC) operates entirely within a K8s apiserver and makes use of CRDs in the namespaces to express hierarchy.ACM may allow the use of HNC in the future if it becomes sufficiently mature.

より詳しく知りたい方は

これ以上の詳細は下記の Design Docs に載っています。興味のある方はぜひ見てみてください。 (12/1 が過ぎてしまうので、この辺りで…) https://docs.google.com/document/d/10MZfFfbQMm33CBboMq2bfrEtXkJQQT4-UH4DDXZRrKY/edit#