kubernetesのマルチテナント運用に役立ちそうな色々
背景
Kubernetesクラスタ運用において、マルチクラスタ戦略 vs マルチテナント戦略 の議論が度々巻き起こります。
マルチクラスタ運用はテナント同士の権限・ネットワーク隔離が容易な反面、運用工数がクラスタ数に比例して増加します。
マルチテナント運用はマルチクラスタの裏返しで、運用工数はそこまで増えない反面、テナント同士の権限・ネットワーク隔離を考慮する必要があります。
マルチテナント戦略を取るときの注意点としては、
Kubernetes policies such as RBAC, quotas, and network policies are essential to safely and fairly share clusters.
ref. https://kubernetes.io/docs/concepts/security/multi-tenancy
とある通り、
- RBAC(Role Based Access Control: ロールベースアクセス制御)
- クラスターにログインできるアカウントのロール管理
- quota
- あるテナントが大量にリソースを使用して他テナントのリソースを圧迫することがないようにする
- network policy
- 通信できないはずの他テナントに通信できないようにネットワーク管理
- その他共有リソース(Node Volumeなど)の隔離
など、マルチクラスタのときはクラスタ単位で隔離できていたものを同じクラスタに乗せることになるので、同様に隔離できるような施策を取る必要があります。
Kubernetesはマルチテナント運用に必要な機能全てを公式に提供しているわけではなく、kubernetes-sigs/multi-tenancy(sig: Special Interest Group, 分科会) で議論はされていますがあまり活発ではないのが実情です。
とはいえ需要はあるので、k8sビルトインの機能を組み合わせたり有志の方々が作っている機能を取り入れたりしてマルチテナント環境を実現するのが一般的なアプローチとなっているようです。
というわけでマルチテナント運用に役立ちそうなものを色々調べて触ってみたいと思います。
kubernetes-sigs/hierarchical-namespaces
背景でkubernetes-sigs/multi-tenancyがあまり活発ではないと言いましたが唯一活発なのがこれです。
ざっくり
- namespaceに親子関係を設定できる
- namespaceの親→子に指定した任意のk8s resourceを伝搬させられる
試してみる
以下のリンクを見ながらやっています。
まずはインストール作業。
# hnsのversionを指定
$ HNC_VERSION=v1.0.0
# hnsをinstall
$ k apply -f https://github.com/kubernetes-sigs/hierarchical-namespaces/releases/download/${HNC_VERSION}/default.yaml
# hns用のpluginをinstall(hnsを扱いやすくなるらしい
# krew(kのplugin manager)が入ってない方はkrewを入れるか手動で入れてください
$ k krew update && k krew install hns
階層化するnamespaceを作ってみます。
この時点ではまだただのnamespaceです。
$ k create ns hoge-org
namespace/hoge-org created
$ k create ns service-1
namespace/service-1 created
階層化してみます。
labelを見ると親子nsとの関係が書いてあります。
$ k hns set service-1 --parent hoge-org
Setting the parent of service-1 to hoge-org
Succesfully updated 1 property of the hierarchical configuration of service-1
$ k hns tree hoge-org
hoge-org
└── service-1
$ k get ns hoge-org -o yaml
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: "2022-08-21T04:32:52Z"
labels:
hnc.x-k8s.io/included-namespace: "true"
hoge-org.tree.hnc.x-k8s.io/depth: "0"
kubernetes.io/metadata.name: hoge-org
name: hoge-org
resourceVersion: "1960"
uid: 1669a35b-92c6-4faa-82b2-d6b80e49da44
spec:
finalizers:
- kubernetes
status:
phase: Active
$ k get ns service-1 -o yaml
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: "2022-08-21T04:33:52Z"
labels:
hnc.x-k8s.io/included-namespace: "true"
hoge-org.tree.hnc.x-k8s.io/depth: "1"
kubernetes.io/metadata.name: service-1
service-1.tree.hnc.x-k8s.io/depth: "0"
name: service-1
resourceVersion: "2440"
uid: e24c4b84-533f-4a18-9fef-72583a1612fa
spec:
finalizers:
- kubernetes
status:
phase: Active
hnsはデフォルトで、Role, RoleBindingsを自動的に子nsに継承します。
EKSでもaws-auth configmapを書くときに親となるnsにアクセスできるようにしておけば、勝手にあとはやってくれそうですね。
$ k -n hoge-org create role hoge-org-sre --verb=update --resource=deployments
role.rbac.authorization.k8s.io/hoge-org-sre created
$ k get role -n hoge-org
NAME CREATED AT
hoge-org-sre 2022-08-21T05:23:51Z
$ k get role -n service-1
NAME CREATED AT
hoge-org-sre 2022-08-21T05:23:51Z
Role, RoleBindings以外のresourceを伝搬できるようにするときはconfigを書き換えます。
下のオブジェクトのspec.resourcesに、
- resource: secrets
mode: Propagate
という感じで追加することで設定を追加できます。
Annotationにpropagate.hnc.x-k8s.io/<select|treeSelect|none>
を書くとresourceごとに上書きすることもできます。
詳しくは
https://github.com/kubernetes-sigs/hierarchical-namespaces/blob/master/docs/user-guide/how-to.md#limit-the-propagation-of-an-object-to-descendant-namespaces を見てください。
modeは三種類あります。
-
Propagate
: オブジェクトを親から子に伝播し、親が削除されたら子も削除します。Role, RoleBindingsのデフォルト動作です。子オブジェクトを直接変更しようとすると怒られてできないようになっています。 -
Delete
:Propagate
で伝搬したコピーをすべて削除します。継承元のオブジェクトには影響ありません。 -
Ignore
: このリソースの変更を停止します。新規または変更されたオブジェクトは伝搬されず、親で削除されたオブジェクトも削除されません。hnc.x-k8s.io/inherited-from
ラベルは削除されません。 Role, RoleBindings以外のresourceについて、設定がない場合のデフォルト動作です。
$ k get hncconfiguration config -o yaml
apiVersion: hnc.x-k8s.io/v1alpha2
kind: HNCConfiguration
metadata:
creationTimestamp: "2022-08-21T04:20:55Z"
generation: 2
name: config
resourceVersion: "7186"
uid: fd12940f-f305-4ccf-8438-f6ab589dceb8
spec: {}
status:
resources:
- group: rbac.authorization.k8s.io
mode: Propagate
numPropagatedObjects: 0
numSourceObjects: 0
resource: rolebindings
version: v1
- group: rbac.authorization.k8s.io
mode: Propagate
numPropagatedObjects: 1
numSourceObjects: 1
resource: roles
version: v1
所感
名前の通りnamespaceの管理と親子間のresource伝搬に特化しているのでそれ以外は特にできない印象です。
namespaceのresourceQuotaをnsごとではなくtreeごとにできるように議論した痕跡はあるものの結局まだ実装されていないようなので、あまり使えなさそうです。
CRDはあるものの、ドキュメントに入っていないかつreleaseにも含まれていなくてWIPっぽいですね。
clastix/capsule
CNCF memberの企業が作っているマルチテナント支援のコンポーネントです。
ざっくり
複数の Namespace を Tenant と呼ばれるグループでまとめ、そのテナント内でNamespace, Network Policies, Security Policies, Resource quota, Limit Range, RBACなどをテナントごとに扱えるようにしてくれるようです。
試してみる
インストール方法は2種類。
- 単一YAMLファイル
- Helm Chart
Helm Chart のほうが好みなので今回は Helm で入れることにします。今回は特にパラメーターをいじることはなさそうなので Helm じゃなくてもよさそうではありますが。
$ helm repo add clastix https://clastix.github.io/charts
$ helm install capsule clastix/capsule -n capsule-system --create-namespace
$ helm status capsule -n capsule-system
Tenant を作ってみます。
$ k create -f - << EOF
apiVersion: capsule.clastix.io/v1beta1
kind: Tenant
metadata:
name: hoge
spec:
owners:
- name: alice
kind: User
EOF
$ k get tenant
NAME STATE NAMESPACE QUOTA NAMESPACE COUNT NODE SELECTOR AGE
hoge Active 0 89s
tenant owner の使い心地を見るために、上で作ったhoge tenant の owner alice になってみます。
今回は kind cluster で検証をしているので、clastix/capsule/hack/create-user.sh を使います。
$ ../../clastix/capsule/hack/create-user.sh alice hoge
creating certs in TMPDIR /tmp/tmp.5cK1OEAWJ7
merging groups /O=capsule.clastix.io
Generating RSA private key, 2048 bit long modulus (2 primes)
.....................+++++
......................+++++
e is 65537 (0x010001)
certificatesigningrequest.certificates.k8s.io/alice-hoge created
certificatesigningrequest.certificates.k8s.io/alice-hoge approved
kubeconfig file is: alice-hoge.kubeconfig
to use it as alice export KUBECONFIG=alice-hoge.kubeconfig
$ export KUBECONFIG=alice-hoge.kubeconfig
$ k config current-context
alice-hoge
まずはnamespaceを作ってみます。
$ k create ns hoge-foo
namespace/hoge-foo created
$ k create ns hoge-foo2
namespace/hoge-foo2 created
$ k get ns hoge-foo -o yaml
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: "2022-09-10T02:06:56Z"
labels:
capsule.clastix.io/tenant: hoge
kubernetes.io/metadata.name: hoge-foo
name: hoge-foo
name: hoge-foo
ownerReferences:
- apiVersion: capsule.clastix.io/v1beta1
blockOwnerDeletion: true
controller: true
kind: Tenant
name: hoge
uid: 6241d243-707b-4563-89d6-6f2f7ba3f91c
resourceVersion: "17358"
uid: 7e7ab627-7157-49d6-a40c-5bc7c1e864e3
spec:
finalizers:
- kubernetes
status:
phase: Active
少し悪巧みをしてみます。
# default User で tenant hoge2 を作成
# alice は使えず、bob だけ使えるようにする
$ k create -f - << EOF
apiVersion: capsule.clastix.io/v1beta1
kind: Tenant
metadata:
name: hoge2
spec:
owners:
- name: bob
kind: User
EOF
# alice に戻って先程作った namespace の tenant を付け替えてみる
$ k label ns hoge-foo --overwrite capsule.clastix.io/tenant=hoge2
Error from server (Cannot assign the desired namespace to a non-owned Tenant): admission webhook "owner.namespace.capsule.clastix.io" denied the request: Cannot assign the desired namespace to a non-owned Tenant
悪さはできないように塞がれているようです。
それでは tenant の中に作った namespace hoge-foo の中にpodを立ててみます。
$ k -n hoge-foo run nginx --image=docker.io/nginx
pod/nginx created
$ k get -n hoge-foo po nginx
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 35s
# default namespace の pod は見えない
$ k get po -n default
Error from server (Forbidden): pods is forbidden: User "alice" cannot list resource "pods" in API group "" in the namespace "default"
$ k delete po nginx -n hoge-foo
pod "nginx" deleted
Tenant に Resource Quota を設定してみます。
$ k apply -f - << EOF
apiVersion: capsule.clastix.io/v1beta1
kind: Tenant
metadata:
name: hoge
spec:
owners:
- name: alice
kind: User
namespaceOptions:
quota: 3
resourceQuotas:
scope: Tenant
items:
- hard:
limits.cpu: "2"
limits.memory: 2Gi
requests.cpu: "1"
requests.memory: 1Gi
pods: "2"
limitRanges:
items:
- limits:
- default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 10Mi
type: Container
EOF
$ k -n hoge-foo run nginx --image=docker.io/nginx
pod/nginx created
$ k get po nginx -n hoge-foo -o jsonpath='{.spec.containers[0].resources}' | yq eval -P
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 10Mi
$ k -n hoge-foo run nginx-2 --image=docker.io/nginx
pod/nginx-2 created
# tenant hoge の namespace hoge-foo で 3 pod 作ろうとするとエラー
$ k -n hoge-foo run nginx-3 --image=docker.io/nginx
Error from server (Forbidden): pods "nginx-3" is forbidden: exceeded quota: capsule-hoge-0, requested: pods=1, used: pods=2, limited: pods=2
$ k delete po nginx-2 -n hoge-foo
pod "nginx-2" deleted
# namespace hoge-foo2 側で pod を作ってみる
$ k -n hoge-foo2 run nginx-2 --image=docker.io/nginx
pod/nginx-2 created
# namespace hoge-foo2 側で3台目の pod を作ろうとするとエラー
# tenant をまたいで resourceQuota を適用できている
$ k -n hoge-foo2 run nginx-3 --image=docker.io/nginx
Error from server (Forbidden): pods "nginx-3" is forbidden: exceeded quota: capsule-hoge-0, requested: pods=1, used: pods=1, limited: pods=1
所感
hierarchical-namespaces に比べると高機能な印象で、ドキュメントも整備されていて読みやすいです。
特に hierarchical-namespaces が実現できていない複数 namespace をまたいだ ResourceQuotas などが嬉しい機能ですね。
実際に使う際にはもう少し自分の組織のユースケースにマッチするかを検証する必要がありそうですが、筋は良さそうです。
その他
-
https://github.com/loft-sh/kiosk
- 開発が止まってそうなのでサラッと見ただけです
Discussion