メモ - RyuSA

技術的なメモ書きとか試してみたこと

Kubernetesの仮想化ツールvclusterに触れてみる話

www.vcluster.com

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をインストールして動かしてみると良いと思います。

www.vcluster.com

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で管理されています。そして一部のリソースを仮想クラスターからホストクラスターに同期し、そのあとのリソースの管理をホストクラスター側に委譲しています。

f:id:RyuSA:20210522220820p:plain

仮想クラスターが実際にどのリソースをホストクラスターへ委譲しているのかは把握してませんが、ざっと見た限り仮想クラスターからホストクラスターへ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)

rancher.com

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の完成ですね。