vclusterはKubernetes上にKubernetesを起動して仮想的なKubernetesを楽しむことができるツールです。特定の名前空間上にk3sをデプロイしその中でリソースを作成、Syncerがリソースをホスト側のKubernetesへ同期をすることで実現しています。
結構楽しそうなツールだったので遊んでみたログを残しておきます。
# 環境 # Kubernetes Providor : Amazon EKS ❯ kubectl version --short Client Version: v1.19.7 Server Version: v1.19.8-eks-96780e
Hello Worldしてみる
インストール (QuickStart)
専用のCLI(=vclusterCLI)、Helm、kubectlなどいくつかデプロイする方法はありますが、とりあえず動かしてみる分にはvclusterCLIをインストールして動かしてみると良いと思います。
vclusterCLIのインストールが完了したら、あとはvclusterCLIを叩くだけでホスト側のKubernetesクラスター(=ホストクラスター)上に仮想的なKubernetes(=仮想クラスター)が動き始めます。
❯ vcluster create foo-cluster -n foo [info] Creating namespace foo [info] execute command: helm upgrade foo-cluster vcluster --repo https://charts.loft.sh --kubeconfig /var/folders/qs/_40g0sgj1bz5lsc4pbbq7sn00000gn/T/689594003 --namespace foo --install --repository-config='' --values /var/folders/qs/_40g0sgj1bz5lsc4pbbq7sn00000gn/T/116523222 [done] √ Successfully created virtual cluster foo-cluster in namespace foo. Use 'vcluster connect foo-cluster --namespace foo' to access the virtual cluster
仮想クラスターから触ってみる
仮想クラスターへアクセスするためのkubeconfigを生成して実際にアクセスしてみます。CLIの記載通りにvcluster connect foo-cluster --namespace foo
を叩くと仮想クラスターへの接続に必要なkubeconfigが生成されるので、kubeconfigを利用して接続してみます。
❯ vcluster connect foo-cluster --namespace foo [done] √ Virtual cluster kube config written to: ./kubeconfig.yaml. You can access the cluster via `kubectl --kubeconfig ./kubeconfig.yaml get namespaces` [info] Starting port forwarding: kubectl port-forward --namespace foo foo-cluster-0 8443:8443 Forwarding from 127.0.0.1:8443 -> 8443 Forwarding from [::1]:8443 -> 8443 ... # 別のセッションを起動 ❯ kubectl get all --kubeconfig ./kubeconfig.yaml NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.100.229.214 <none> 443/TCP 3m49s
初期起動完了したので、ためしにいくつかリソースを仮想クラスター上に作成して動作確認してみます。
# Deploymentを作成 ❯ kubectl create deployment --image nginx nginx --replicas 3 --kubeconfig ./kubeconfig.yaml deployment.apps/nginx created # 名前空間を作成 ❯ kubectl create namespace mginx --kubeconfig ./kubeconfig.yaml namespace/mginx created # 作成した名前空間上にDeploymentを作成 ❯ kubectl create deployment --image nginx nginx --replicas 3 --kubeconfig ./kubeconfig.yaml -n mginx deployment.apps/nginx created # 作成したDeployment向けのServiceを作成する ❯ kubectl expose deployment -n mginx nginx --port 80 --kubeconfig ./kubeconfig.yaml service/nginx exposed # 作成したリソース一覧を確認する ❯ kubectl get all -A --kubeconfig ./kubeconfig.yaml NAMESPACE NAME READY STATUS RESTARTS AGE kube-system pod/coredns-66c464876b-xrbxj 1/1 Running 0 18m default pod/nginx-6799fc88d8-tttl2 1/1 Running 0 5m51s default pod/nginx-6799fc88d8-hfs6z 1/1 Running 0 5m51s default pod/nginx-6799fc88d8-xt67s 1/1 Running 0 5m51s mginx pod/nginx-6799fc88d8-8skv7 1/1 Running 0 4m50s mginx pod/nginx-6799fc88d8-96q2w 1/1 Running 0 4m50s mginx pod/nginx-6799fc88d8-lzxlb 1/1 Running 0 4m50s NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default service/kubernetes ClusterIP 10.100.229.214 <none> 443/TCP 18m kube-system service/kube-dns ClusterIP 10.100.253.26 <none> 53/UDP,53/TCP,9153/TCP 18m mginx service/nginx ClusterIP 10.100.148.16 <none> 80/TCP 7s NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE kube-system deployment.apps/coredns 1/1 1 1 18m default deployment.apps/nginx 3/3 3 3 5m51s mginx deployment.apps/nginx 3/3 3 3 4m50s NAMESPACE NAME DESIRED CURRENT READY AGE kube-system replicaset.apps/coredns-66c464876b 1 1 1 18m default replicaset.apps/nginx-6799fc88d8 3 3 3 5m52s mginx replicaset.apps/nginx-6799fc88d8 3 3 3 4m51s # 'alice'というServiceAccountを作成する ❯ kubectl create serviceaccount alice --kubeconfig ./kubeconfig.yaml serviceaccount/alice created ❯ kubectl get serviceaccounts --kubeconfig ./kubeconfig.yaml NAME SECRETS AGE default 1 19m alice 1 5m51s
ざっと見た限り、普通のKubernetesのように動作しているように見えます。すごくいい感じ
ホストクラスターから眺めてみる
仮想クラスターで作成したリソースがホストクラスターからどのように見えるのかを確認してみます。
# すべてのリソースを取得する(関連する部分のみ抜粋) ❯ kubectl get all -A NAMESPACE NAME READY STATUS RESTARTS AGE foo pod/coredns-66c464876b-xrbxj-x-kube-system-x-foo-cluster 1/1 Running 0 22m foo pod/foo-cluster-0 2/2 Running 0 23m foo pod/nginx-6799fc88d8-8skv7-x-mginx-x-foo-cluster 1/1 Running 0 9m17s foo pod/nginx-6799fc88d8-96q2w-x-mginx-x-foo-cluster 1/1 Running 0 9m17s foo pod/nginx-6799fc88d8-hfs6z-x-default-x-foo-cluster 1/1 Running 0 10m foo pod/nginx-6799fc88d8-lzxlb-x-mginx-x-foo-cluster 1/1 Running 0 9m17s foo pod/nginx-6799fc88d8-tttl2-x-default-x-foo-cluster 1/1 Running 0 10m foo pod/nginx-6799fc88d8-xt67s-x-default-x-foo-cluster 1/1 Running 0 10m kube-system pod/coredns-59847d77c8-8zpd4 1/1 Running 0 5h26m kube-system pod/coredns-59847d77c8-vxqq5 1/1 Running 0 5h26m kube-system pod/kube-proxy-9wh2q 1/1 Running 0 5h4m kube-system pod/kube-proxy-xqtb7 1/1 Running 0 5h4m NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 5h27m foo service/foo-cluster ClusterIP 10.100.229.214 <none> 443/TCP 23m foo service/foo-cluster-headless ClusterIP None <none> 443/TCP 23m foo service/kube-dns-x-kube-system-x-foo-cluster ClusterIP 10.100.253.26 <none> 53/UDP,53/TCP,9153/TCP 22m foo service/nginx-x-mginx-x-foo-cluster ClusterIP 10.100.148.16 <none> 80/TCP 4m34s kube-system service/kube-dns ClusterIP 10.100.0.10 <none> 53/UDP,53/TCP 5h27m NAMESPACE NAME READY AGE foo statefulset.apps/foo-cluster 1/1 23m # ServiceAccountを確認する ❯ kubectl get serviceaccounts | grep alice // NO RESULT
仮想マシン上で作成したリソースの一部がホストクラスターから見えず(Deploymentとか)、一方仮想マシンで見えていたいくつのそれっぽいリソースがホストクラスターから見えています(Podとか)。 理由は単純で、仮想クラスター側で作成したリソースは仮想クラスター側のk3sで管理されています。そして一部のリソースを仮想クラスターからホストクラスターに同期し、そのあとのリソースの管理をホストクラスター側に委譲しています。
仮想クラスターが実際にどのリソースをホストクラスターへ委譲しているのかは把握してませんが、ざっと見た限り仮想クラスターからホストクラスターへPodやService、IngressやPVCなどを委譲しているようです。
少しだけ深堀り
別の仮想クラスターをHelmでインストールしてみます。vclusterはHelmコマンドを代替しているだけなので等価なHelmコマンドを叩くことでもvclusterをインストールすることが可能です。
❯ cat <<EOF > values.yaml vcluster: image: rancher/k3s:v1.19.5-k3s2 extraArgs: # ホストクラスターのService CIDRをズラしてみる - --service-cidr=10.96.0.0/12 storage: size: 5Gi EOF ❯ helm repo add loft https://charts.loft.sh ❯ helm repo update # インストール ❯ helm upgrade --install bar loft/vcluster \ --values values.yaml \ --namespace bar \ --create-namespace # ホストクラスター側から見えるリソース ❯ kubectl get all -n bar NAME READY STATUS RESTARTS AGE pod/bar-0 2/2 Running 0 19s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/bar ClusterIP 10.100.50.206 <none> 443/TCP 19s service/bar-headless ClusterIP None <none> 443/TCP 19s NAME READY AGE statefulset.apps/bar 1/1 19s
仮想クラスターコントロールプレーンのパラメータ調査
Helmでデプロイする際には細かいパラメータを指定することができます。仮想クラスターのコントロールプレーンであるk3sにデフォルトで指定されているパラメータを確認してみます。
❯ kubectl get pod -n bar bar-0 -o yaml apiVersion: v1 kind: Pod metadata: name: bar-0 namespace: bar spec: containers: - args: - server - --write-kubeconfig=/k3s-config/kube-config.yaml - --data-dir=/data - --disable=traefik,servicelb,metrics-server,local-storage - --disable-network-policy - --disable-agent - --disable-scheduler - --disable-cloud-controller - --flannel-backend=none - --kube-controller-manager-arg=controllers=*,-nodeipam,-nodelifecycle,-persistentvolume-binder,-attachdetach,-persistentvolume-expander,-cloud-node-lifecycle - --service-cidr=10.96.0.0/12 command: - /bin/k3s image: rancher/k3s:v1.19.5-k3s2 name: vcluster volumeMounts: - mountPath: /data name: data ...(snip)
k3sのパラメータを覗いて上記のパラメータと比較してみると
- --disable=traefik,servicelb,metrics-server,local-storage
- traefikやmetrics-serverを無効
- --disable-network-policy
- NetworkPolicy無効
- --disable-agent
- ローカルのエージェントを起動しない
- --disable-scheduler
- スケジューラーを無効(Podの配置がされない)
- --disable-cloud-controller
- Cloud Controllerを無効
- --flannel-backend=none
- Flannelを無効
- --kube-controller-manager-arg=controllers=*,-nodeipam,-nodelifecycle,-persistentvolume-binder,-attachdetach,-persistentvolume-expander,-cloud-node-lifecycle
- いろいろ無効にしてる
k3sのもつController周りを無効にし、物理的な操作が必要になるリソースに関して何もしないようにしているように見えます。これで仮想クラスター内でDeploymentやStafulsetを扱い、PodやServiceをホストクラスターに委譲することができるわけですね。
仮想クラスターへの接続
実装を覗いてみるとvclusterCLIの各種コマンドはkubectlを利用して実現されています。それらを使ってvclusterCLI-lessで仮想クラスターに接続して見ます。
# kubeConfigを持ってきます # https://github.com/loft-sh/vcluster/blob/v0.2.0/cmd/vclusterctl/cmd/connect.go#L93 ❯ kubectl exec -n bar bar-0 -c syncer -- cat /root/.kube/config > barConfig.yaml # ポートフォワードします(vcluster connectでの挙動を一緒) # https://github.com/loft-sh/vcluster/blob/v0.2.0/cmd/vclusterctl/cmd/connect.go#L170 ❯ kubectl port-forward -n bar bar-0 8443:8443 Forwarding from 127.0.0.1:8443 -> 8443 Forwarding from [::1]:8443 -> 8443 ... # 別のセッションを起動 ❯ kubectl get all -A --kubeconfig ./barConfig.yaml NAMESPACE NAME READY STATUS RESTARTS AGE kube-system pod/coredns-66c464876b-qrm6t 1/1 Running 0 102m NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default service/kubernetes ClusterIP 10.100.50.206 <none> 443/TCP 102m kube-system service/kube-dns ClusterIP 10.100.38.116 <none> 53/UDP,53/TCP,9153/TCP 102m NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE kube-system deployment.apps/coredns 1/1 1 1 102m NAMESPACE NAME DESIRED CURRENT READY AGE kube-system replicaset.apps/coredns-66c464876b 1 1 1 102m
おわりに
ざっくりとvclusterを触ってみましたが、名前空間ごとの環境分離がいい感じにできそうなツールに見えました。
さすがにこれを本番環境に導入、は今の段階では想定できませんが……例えば社内の勉強用/検証用の環境として用意したりする分には非常に便利だろうなと感じます。 仮想クラスターを作成して証明書情報を取得、LoadBalancerを立ててポートフォワード抜きで接続できるように設定すればお手軽Kubernetes as a Serviceの完成ですね。