KindでVirtualClusterを試そう

## はじめに

[NTTコミュニケーションズ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nttcom)の5日目を担当します@khrdです!

本記事は自チームで運用中のk8sクラスタについて，今後どうやってテナントを切り出していけば社内ユーザにとって使いやすいだろう，自チームで管理しやすいだろうといった文脈からMulti-Tenancy sigで取り組まれているプロジェクトの1つである`Virtual Clusters` (以下`VC`と省略)について調査をまとめたものになります．


## 目次

この記事で触れる内容は下記の通りで，上から順番に説明を進めてさせていただきます．

- VCの概要
- VCの作成手順
- スーパークラスタ(親クラスタ)とテナントクラスタ(VC)で見える範囲の違い
- VCでAdmin操作をしてみる
- まとめ

## VCの概要

VCは一言で言ってしまえば，スーパークラスタ(親クラスタ)の中に仮想的なクラスタ(VC)を作成できる仕組みです．

スーパークラスタの中にVC専用のapiserverやetcdを作成するため，テナントユーザはVCを強い権限で操作することができ，他のテナントやスーパークラスタを直接見たり操作することができないため強固なアイソレーションを実現できます．

[こちら](https://github.com/kubernetes-sigs/multi-tenancy/tree/master/incubator/virtualcluster#virtualcluster---enabling-kubernetes-hard-multi-tenancy)によると主要なコンポーネントは3つあり，`vc-manager`, `syncer`, `vn-agent`があり役割はそれぞれ下記のとおりです．

#### 1. vc-manager
VCクラスタの管理を担当．VCのCRDの変更をウォッチして望ましい状態にする．
 
#### 2. syncer
VCとスーパークラスタ間を同期を担当．VCユーザがVCのネームスペース配下にPodを作成するとスーパークラスタに反映させる．その逆もしかり．

#### 3. vn-agent
Superクラスタのkubeletにプロキシする． kubeletとやりとりを行うのでdaemonSetリソースで全Workerノードに配置される．

### Pros/Cons
#### Pros

- VCユーザ(テナントクラスタのユーザ)はRBAC操作やCRDのインストールなどcluster admin権限でVCを操作することができる
- VCユーザは他のVCやスーパークラスタのPodやNamespaceを見れない(アイソレーションができる)

#### Cons

- スーパークラスタとVCのクラスタVersionは同じであることが推奨
    - 現時点でv1.16でSyncerはビルドされているため後方互換性はサポートされない...

- VCはPVをサポートしておらず，スーパークラスタのPVを利用する形となる
    - こちらは大きなデメリットとはならなさそうですが，テナントユーザが各自のStorageを利用したい場合には問題になるかもしれません

- VCのCNIはテナントユーザがカスタマイズできない

- VCユーザがVCの証明書をローテートする方法が用意されていない
    - スーパクラスター管理者でないと実施できなさそう

## VCの作成手順

### 今回用意するテスト環境

![Screen Shot 2020-12-03 at 11.15.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80918/5775a2ef-2f68-c213-3477-54621ae7bec9.png)

テスト環境として上記のようなクラスタをKindで作成します

- Kind Test cluster
    - master x1台
    - worker x3台
    - Cluster versionは1.16.15
- ホスト端末はMac book pro

こちらのクラスタ内にVCのCRDなどををインストールしていき作成のテナントクラスタの作成を行っていきます．

### vcサブコマンドをMacにインストール

まずVC作成にあたってkubectlプラグインである`vc`をのちのち使用しますが，
バイナリが提供されていないため先にこちらをビルドして準備します．

使用したGoのバージョンは下記の通りです

```
$ go version
go version go1.13.4 darwin/amd64
```

リポジトリからソースファイルなどを取得してビルドします

```
$ go get github.com/kubernetes-sigs/multi-tenancy
$ make build WHAT=cmd/kubectl-vc GOOS=darwin
$ sudo cp _output/bin/kubectl-vc /usr/local/bin
```

プラグインインストールが完了すると下記のような`vc`サブコマンドが使用可能となります

```
$ kubectl vc --version
kubectl-vc version v1.0.0-3dc7a9c
```

### Kindでテストクラスタを構築する

それではKindでテスト環境を作っていきます

`master` x1, `worker` x3のマルチクラスターを構成するために`multinode.yaml`を作成します．

```
$ cat << EOF > multinode.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
EOF
```

作成した`mutinode.yaml` を用いてスーパークラスタを作成します

```
$ kind create cluster --config multinode.yaml --image=kindest/node:v1.16.15
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.16.15) 🖼
 ✓ Preparing nodes 📦 📦 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Have a nice day! 👋
```

### VCのCRDなどの適用

続いてVCのインストールを行います

```
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/multi-tenancy/master/incubator/virtualcluster/config/crds/tenancy.x-k8s.io_clusterversions.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/multi-tenancy/master/incubator/virtualcluster/config/crds/tenancy.x-k8s.io_virtualclusters.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/multi-tenancy/master/incubator/virtualcluster/config/setup/all_in_one.yaml
```

インストールした時点で`vc-manager` namespaceに概要で述べたcomponentsがインストールされます．

![Screen Shot 2020-12-01 at 17.33.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80918/68a73f8e-5a0c-3bef-c1d4-9a92c43917b5.png)


### Cluster Versionの適用

![Screen Shot 2020-12-03 at 09.28.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80918/7ff87eb4-6dc8-90a6-106a-ec67acf21ebf.png)


次にVCの雛形となるCluster Versionと呼ばれるCRDリソースを適用します．

```
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/multi-tenancy/master/incubator/virtualcluster/config/sampleswithspec/clusterversion_v1_nodeport.yaml
```

ちなみにClusterVersionの中身はこのようになっています．
VCのクラスターのマスター機能であるetcdやapiserverのimage, 実行時の引数などが記述されています．
なおデメリットでも述べたとおり，vn-agentなどが1.16でビルドされているため，使われているimageを最新versionにするなど柔軟には変えられそうにないです...

```
apiVersion: tenancy.x-k8s.io/v1alpha1
kind: ClusterVersion
metadata:
  labels:
    controller-tools.k8s.io: "1.0"
  name: cv-sample-np
spec:
  etcd:
      ~~省略~~
          spec:
            subdomain: etcd
            containers:
            - name: etcd
              image: virtualcluster/etcd-v3.4.0
      ~~省略~~
  apiserver:
      ~~省略~~
          spec:
            hostname: apiserver
            subdomain: apiserver-svc
            containers:
            - name: apiserver
              image: virtualcluster/apiserver-v1.16.2
              imagePullPolicy: Always
      ~~省略~~
  controllerManager:
      ~~省略~~
          spec:
            containers:
            - name: controller-manager
              image: virtualcluster/controller-manager-v1.16.2
              imagePullPolicy: Always
      ~~省略~~
```

### VCの作成

最後に`vc`サブコマンドを利用してVirtualClusterリソースを作成することてVCが作成されます．

```
$ kubectl vc create -f https://raw.githubusercontent.com/kubernetes-sigs/multi-tenancy/master/incubator/virtualcluster/config/sampleswithspec/virtualcluster_1_nodeport.yaml -o vc-1.kubeconfig
2020/10/22 13:34:24 etcd is ready
2020/10/22 13:34:45 apiserver is ready
2020/10/22 13:35:19 controller-manager is ready
2020/10/22 13:35:19 VirtualCluster default/vc-sample-1 setup successfully
```

コマンド実行終了するとVCユーザ用のkubeconfig`vc-1.kubeconfig`が生成されます．

##スーパークラスタとテナントクラスタ(VC)で見える範囲の違い

さてvcが作成できたので早速アイソレーションができているか確認してみましょう．

### スーパークラスタからの見え方

#### Namespace

まずNamespaceを確認します

```
$ k get ns
NAME                                         STATUS   AGE
default                                      Active   91m
default-b41df0-vc-sample-1                   Active   34m
default-b41df0-vc-sample-1-default           Active   33m
default-b41df0-vc-sample-1-kube-node-lease   Active   33m
default-b41df0-vc-sample-1-kube-public       Active   33m
default-b41df0-vc-sample-1-kube-system       Active   33m
kube-node-lease                              Active   91m
kube-public                                  Active   91m
kube-system                                  Active   91m
local-path-storage                           Active   91m
vc-manager                                   Active   74m
```

こちらのとおり，`default-b41df0-vc-sample-1*`というNamespaceリソースが生成されされており
すべて1つのVC用のNamespaceリソースとなっています．

ちなみにVCのマスター機能は`default-b41df0-vc-sample-1`Namespaceで動作しています．

```
$ k get statefulsets.apps -n default-b41df0-vc-sample-1
NAME                 READY   AGE
apiserver            1/1     35m
controller-manager   1/1     35m
etcd                 1/1     35m
```

### テナントクラスタからの見え方

#### Namespace

VCからNamespaceを確認します

```
$ k get ns
NAME              STATUS   AGE
default           Active   7m4s
kube-node-lease   Active   7m6s
kube-public       Active   7m6s
kube-system       Active   7m6s
```

ご覧の通り，スーパクラスターからは全てのNamespaceが閲覧できましたが，VCからはVC用のNamespaceしか見れなくなりました．

またスーパクラスターではVC用のNamespaceは`default-b41df0-vc-sample-1-*`という名前になっていましたが，
VCから見ると下図のように`default-b41df0-vc-sample-1-`以下の名前として表示されます．

![Screen Shot 2020-12-03 at 10.16.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80918/ae139356-3e77-06b8-771b-9293d00398d8.png)

### Node

VCからノード確認ができるか確認してみました．

```
$ k get no
No resources found
```

どうやらVCからノード情報は見れないようです．

## VCでAdmin操作をしてみる

最後にVCはテナントユーザが強い権限を使うことが可能なのでAdmin操作可能かどうかを見てみます

### RBAC

```
$ k apply -f my-role.yaml
role.rbac.authorization.k8s.io/my-role created

$ kubectl apply -f my-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/my-rolebinding created
```

RBAC操作は問題なく行えました

### CRD

```
$ k apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.yaml

customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
~~省略~~
```

CRDのインストールも問題なく行えました

クラスタ管理者に代理でインストールや強い権限を付与してもらわなくてもテナントユーザが自由にクラスタをいじれそうなので便利そうです

## まとめ

まとめです．
VirtualClusterの基本的なコンポーネントを確認した後，実際にインストールして強力なアイソレーションが出来ていることを確認できました．
ただConsでも述べた通り，テナントユーザが操作できない領域もあり，その辺りの改善が今後されるかどうか注視したいと思います．
また，今回紹介できなかったmulti-tenancy sigの別プロジェクトである「Tenant Operator」「Hierararchical namespaces (aka HNC)」についても今後の動きに期待ですね!

明日は，@staybuzzさんの投稿です．お楽しみに!!

## 参考資料

https://github.com/kubernetes-sigs/multi-tenancy/tree/master/incubator/virtualcluster
https://www.youtube.com/watch?v=5RgF_dYyvEY