仮想化通信

日本仮想化技術株式会社の公式エンジニアブログ

Minikubeを使ってKubeVirtを試す環境を作ってみる

このブログではKubeVirtについて何度か取り上げていました。 主にkubeadmでクラスターを作って、KubeVirtを導入する手順もご紹介しています。

tech.virtualtech.jp

ちなみになぜKubeVirtを頻繁に取り上げるかというと、筆者がKubeVirtに個人的に興味があるからです。

さて今回はMinikubeを使って、KubeVirtの評価ができる環境づくりを試してみようと思います。 Minikubeを利用することにより、KubernetesでKVM VMを利用できるKubeVirtを手軽に試すことができます。

Minikubeで手軽にKubeVirtを利用できるようになることで、コンテナーとVMでアプリケーションを実行して色々なテストをしたり、 コンテナーとVMでアプリケーションをそれぞれ動かして、連携のテストをしたりなどできるようになるので、おそらく便利だと思います。

KubeVirtの公式ドキュメントに「Labs - KubeVirt quickstart with Minikube」がありますが、前提環境について詳しく書かれていないため、Ubuntuをセットアップした後から順を追って説明する形でこの記事を書いています。

KubeVirtについておさらい

KubeVirtは、Kubernetesで仮想マシン利用が可能になるソリューションです。 コンテナ利用の需要の高まりによってKubernetesが使われることが多くなりましたが、 その一方で、コンテナ化はしたいがコンテナ化が難しいアプリケーションは少なからずあるのが現状です。

KubeVirtは、KubernetesクラスターにVM管理という追加機能を提供するソフトウェアです。 一般のコンテナアプリケーションの実行と同じようにkubectlコマンドラインなどを使ってVMの作成を依頼すると、KubeVirtのコントローラーがVMIが指定したホストにスケジュールされるように命令します。

デーモン(virt-handler)はkubeletと一緒にホストを処理してVMIを起動し、「環境」をセットアップします。

公式サイトのアーキテクチャーより引用

Kubernetesのノードではvirt-handlerというプロセスが動作しており、VMIの作成が実行されるとPodが作られてvirt-lancherというプロセスがまず実行されます。 このプロセスがその環境にQEMUとLibVirtのプロセスを実行します。そして最終的にマニフェスト要求に従ってVMにOSやリモートアクセスのための設定をセットアップします。

KubeVirtの良いところはVMとコンテナーで動くアプリケーションを共存できるという点です。KubeVirtで作成したVMは単独でも利用できますが、一つのVMIにコンテナとVMで動くアプリケーションを共存させたり、別のPodで動くコンテナ内のアプリケーションとVMで動くアプリケーションを連携させて一つのシステムを稼働させるといったことが可能にあります。

もうちょっと詳しく知りたい場合はこちらをご覧ください。

kubevirt.io

KubeVirt環境のセットアップ概要

今回は次のような流れでセットアップしておきます。

OSをインストール

ベースOSとして、今回はUbuntu Server 20.04をインストールしました。

Minikubeをインストール

minikube startを開いて、OSなどをポチポチ設定して表示された方法で導入。 今回はUbuntuをベースOSとしてインストールしたので、次のようにMinikubeをインストールします。

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb

Linux KVM環境をセットアップ

次に、Minikubeをどの環境上で動かすか「Driver」を選択します。 OSによって使えるDriverは異なっており、実装仕様も異なります。「preferred」と表記されているものを選べばたいてい問題ないようですが、 何か作業をしてみてうまくいかない場合は他のDriverに変更した方が良いかもしれません(経験上)。

今回はLinuxでMinikubeを使うときにはスタンダートなKVM2 Driverを使います。 KVM2 Driverは名前の通り、Linux KVM環境をホストに導入する必要があります。

あくまでMinikubeがVMの中で動作するだけで、コンテナは後述の「minikube start」で指定(--container-runtime)したランタイムを介して実行されます。 指定できるランタイムはhelpを確認すると確認できます。

まずはOSにLinux KVMをインストールする。Ubuntuの場合は...

$ sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils
$ sudo adduser `id -un` libvirt && sudo adduser `id -un` kvm

これについての詳細は、KVM/Installationを参照してください。

Minikubeを「kvm2」ドライバーでクラスター起動

次のように実行してKVM VMでminikubeを動かし、その中でKubernetesクラスターとして稼働させます。 今回はコンテナランタイムとしてcontainerd、ネットワークインターフェイスとしてflannelを使う場合の例です。

$ minikube config set driver kvm2 
$ minikube start --container-runtime=containerd --cni=flannel

virshコマンドを実行すると、KVM VMとしてMinikubeが動いていることがわかります。「minikube ssh」でこのVMの中に入れます。

$ virsh list
 Id   Name       State
--------------------------
 2    minikube   running

$ minikube ssh sudo crictl pods
POD ID              CREATED             STATE               NAME                                  NAMESPACE           ATTEMPT             RUNTIME
4f34dddc45fe5       6 days ago          NotReady            hello-node-6b89d599b9-st5v2           default             0                   (default)
e4893ab6d4e9d       6 days ago          NotReady            virt-launcher-vm1-x928v               default             0                   (default)
799ff1520788d       6 days ago          Ready               cdi-uploadproxy-668fd6f45-nxqkv       cdi                 0                   (default)
3a37ef5de5c65       6 days ago          Ready               cdi-deployment-7f984664d4-n2d9d       cdi                 0                   (default)
a199c366627fe       6 days ago          Ready               cdi-apiserver-69467b7bc-74jzc         cdi                 0                   (default)
dcc5a052caa96       6 days ago          Ready               cdi-operator-556d7499d5-w4qj2         cdi                 0                   (default)
ef4540cef599d       6 days ago          NotReady            virt-launcher-testvmi-nocloud-khjqj   default             0                   (default)
21517d7be5dd7       6 days ago          Ready               virt-controller-75fd85c84b-7vnmj      kubevirt            0                   (default)
9475b307b6870       6 days ago          Ready               virt-controller-75fd85c84b-ppdbl      kubevirt            0                   (default)
122a06662baea       6 days ago          Ready               virt-handler-vhmzh                    kubevirt            0                   (default)
0bd9149f4692f       6 days ago          Ready               virt-api-667fb5b669-955ln             kubevirt            0                   (default)
930676ce916bc       6 days ago          Ready               virt-operator-97778fbf8-m4lct         kubevirt            0                   (default)
571e6832904c4       6 days ago          Ready               virt-operator-97778fbf8-ktrsz         kubevirt            0                   (default)
39a41e6855e8b       6 days ago          Ready               kubevirt-install-manager              kube-system         0                   (default)
1fc97cea4b4bf       6 days ago          Ready               coredns-64897985d-j9zdh               kube-system         0                   (default)
69abc84a9e23c       6 days ago          Ready               kube-flannel-ds-amd64-mzkbw           kube-system         0                   (default)
a2d2b2f1dfeb7       6 days ago          Ready               kube-proxy-hjjbr                      kube-system         0                   (default)
32bcdac176a91       6 days ago          Ready               storage-provisioner                   kube-system         0                   (default)
c3c740c09b293       6 days ago          Ready               etcd-minikube                         kube-system         0                   (default)
21c7ab0322258       6 days ago          Ready               kube-scheduler-minikube               kube-system         0                   (default)
8668461751341       6 days ago          Ready               kube-controller-manager-minikube      kube-system         0                   (default)
5d29fddbd62df       6 days ago          Ready               kube-apiserver-minikube               kube-system         0                   (default)

MinikubeでデプロイしたKubernetesを使う

次のコマンドを実行して、kubectlコマンドを実行したときにminikube kubectlが実行されるようにエイリアスを張ります。 これを行うことで、デプロイしたKubernetesクラスターのバージョンに合わせたCLIバイナリーをダウンロードしてくれるようになります。

Minikubeバイナリーでは色々なバージョンのKubernetesを実行できます。 Kubernetes APIとアクセスするインターフェイスとしてkubectlがありますが、一定の互換性は持っているもののあまりバージョンが離れたクラスターへのアクセスは問題が発生する場合があります。Minikubeを使う場合は特定のバージョンのkubectlをセットアップするよりもminikube kubectlのエイリアスを貼っておいた方が便利です。

エイリアスを永続的に設定するには、.bashrcのaliasのあたりに追記もしておくと良さそうです。

$ alias kubectl='minikube kubectl --'

この設定をしておくと、Kubernetes APIにアクセスしたときに適切なバージョンのkubectlが${HOME}/.minikube/cacheにインストールされます。 コマンドを実行して、普通のコンテナを実行できることを確認します。

$ kubectl create deployment hello-node --image=k8s.gcr.io/echoserver:1.4
$ kubectl get deployment
$ kubectl delete deployment hello-node

MinikubeのKubevirtアドオンを有効

次に、このMinikubeで構築したK8sでKubeVirtを使えるようにします。 詳細やマニュアルインストール方法はLabs - KubeVirt quickstart with Minikubeに書かれていますが、 以下のコマンドでアドオンを有効化できます。

$ minikube addons enable kubevirt
$ kubectl get all -n kubevirt      # 有効化されたか確認

インストールされたKuberVirtのバージョンは kubectl describe kubevirt -n kubevirtの実行結果から確認できます。

次のコマンドを使ってvirtctl CLIをインストールします。 VM作成などはkubectlを利用しますが、VMのアクセスや制御にはvirtctlを利用するためです。

VERSION=$(kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.observedKubeVirtVersion}")
ARCH=$(uname -s | tr A-Z a-z)-$(uname -m | sed 's/x86_64/amd64/') || windows-amd64.exe
echo ${ARCH}
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-${ARCH}
chmod +x virtctl
sudo install virtctl /usr/local/bin

KubeVirt環境のテスト

ここまででKubeVirtの実行環境は整っていますので、次にKubeVirtを使ってみたいと思います。 KubeVirtではコンテナーイメージを使ってVMを動かす方法もあるのですが、好きなLinux OSを動かしたいと思いますので 今回はContainerized Data Importer (CDI)を使って、イメージとVMの作成を試してみたいと思います。

まずはCDI機能をクラスターに追加するため、次のようなコマンドを実行します(この記事を書いた時点で最新であったCDIのv1.50.0をインストールする例)。 システムコンテナーはcdiという名前空間で動くので、そのように指定してステータスを確認しましょう。

$ export VERSION=v1.50.0
$ kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
$ kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml

$ kubectl get cdi cdi -n cdi -w
NAME   AGE   PHASE
cdi    2s    
cdi    13s   Deploying
cdi    66s   Deployed

$ kubectl get pods -n cdi
cdi-apiserver-69467b7bc-wvzrx     1/1     Running   0          81s
cdi-deployment-7f984664d4-6kdxj   1/1     Running   0          75s
cdi-operator-556d7499d5-h2w7b     1/1     Running   0          102s
cdi-uploadproxy-668fd6f45-mcx4k   1/1     Running   0          70s

Minikubeでクラスターを構築するとローカルストレージが利用可能なストレージクラスが定義されているはずです。 CDIではVMを作る毎にストレージボリュームイメージをこのストレージに作成して、VMを作成する際にマニフェストにイメージボリュームを指定することで VMにOSイメージを流し込むようなイメージになります。

まずはストレージクラスターを確認しましょう。

$ kubectl get sc
NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  34m

次にVMイメージを作成してみます。 VMイメージはストレージクラスを通じてVMに割り当てるストレージを要求し、VMと紐付けます。

先ほどざっくり説明したように、一度起動するとデータがストレージに書き込まれます。そのため、動かすVM毎にこの作業が必要です。

$ cat <<EOF > pvc_fedora.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: "fedora"
  labels:
    app: containerized-data-importer
  annotations:
    cdi.kubevirt.io/storage.import.endpoint: "https://download.fedoraproject.org/pub/fedora/linux/releases/36/Cloud/x86_64/images/Fedora-Cloud-Base-36-1.5.x86_64.raw.xz"
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: standard      #ストレージクラスを指定します
  resources:
    requests:
      storage: 5Gi                #適切なボリュームサイズを指定します
EOF

イメージソースURLに作成ごとに取りに行くと非効率なので、イメージをローカルネットワーク上のWebサーバーなどで公開しておき、それを使うと良いと思います。 ローカルネットワーク内にNGINXやApacheといったようなWebサーバーをセットアップして、公開ディレクトリーにイメージを置いておけば良いでしょう。

OSイメージはOpenStackなどで利用するイメージと同じものを使います。本例はFedoraの例なので、ここを別のクラウドイメージに置き換えれば他のOSを動かすこともできます。 イメージとして最低限必要なのは、cloud-initが入っていることでしょうか。

マニフェストファイルの準備が終わったら、通常のようにkubectl createしてください。処理が開始されます。 Import Completeと出力されれば、イメージ作成は完了です。

$ kubectl create -f pvc_fedora.yml

$ kubectl get pod
NAME              READY   STATUS    RESTARTS   AGE
importer-fedora   1/1     Running   0          45s

$ kubectl logs -f importer-fedora
I0609 02:42:58.381176       1 importer.go:83] Starting importer
I0609 02:42:58.382145       1 importer.go:150] begin import process
...
I0608 08:32:19.667671       1 data-processor.go:288] Validating image
I0608 08:32:19.674689       1 data-processor.go:282] New phase: Complete
I0608 08:32:19.674907       1 importer.go:194] Import Complete

さて、KubeVirtではVMに対してシリアルコンソールやVNC、SSHでアクセスできます。 今回はSSHでアクセスしてみたいと思います。

SSHでVMにアクセスする場合、一般的にオフィシャルに用意されているクラウドイメージは公開鍵認証を許可していることが多いため、 ホストからVMにSSHできるようにするため、事前にキーペアを作ってVM作成時に指定することにします。

キーペアは今回は次のように作成しました。

$ ssh-keygen -t ed25519 
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAHogeeeHugaaa ubuntu@bay10-gen9      #みたいな感じで公開鍵が出力される

VM作成テンプレートを使ってVMを作成してみます。公開鍵をcatで出力して、内容をYAMLファイル内に書き込みます。

$ wget https://kubevirt.io/labs/manifests/vm1_pvc.yml
$ vi vm1_pvc.yml
...
      volumes:
      - name: disk0
        persistentVolumeClaim:
          claimName: fedora        #PVC名を確認
...
            ssh_authorized_keys:
            - ssh-ed25519 AAAAHogeeeHugaaa ubuntu@bay10-gen9   #こんな感じでペースト

先ほど編集したマニフェストファイルを使って、VMを作成します。

$ kubectl create -f vm1_pvc.yml

$ kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
virt-launcher-vm1-pl4hr   1/1     Running   0          14s

$ kubectl get vm
NAME   AGE     STATUS    READY
vm1    3m45s   Running   True
$ kubectl get vmi
NAME   AGE     PHASE     IP            NODENAME     READY
vm1    3m49s   Running   10.244.0.25   bay10-gen9   True

VMにアクセスします。指定するのはPod名ではなくVM名なので注意。

$ virtctl ssh fedora@vm1 -i ~/.ssh/id_ed25519
The authenticity of host 'vmi/vm1.default:22 (172.17.28.62:6443)' can't be established.
ECDSA key fingerprint is SHA256:qc5I4Q1GOHu/dYotwJvZxZwJD2oqJ5huWp7qH8ZmMjE.
Are you sure you want to continue connecting (yes/no)? yes
[fedora@vm1 ~]$ sudo yum check-update
Fedora 36 - x86_64                                                                                                                               18 MB/s |  81 MB     00:04    
Fedora 36 openh264 (From Cisco) - x86_64                                                                                                        1.6 kB/s | 2.5 kB     00:01    
Fedora Modular 36 - x86_64                                                                                                                      4.5 MB/s | 2.4 MB     00:00    
Fedora 36 - x86_64 - Updates                                                                                                                    9.4 MB/s |  17 MB     00:01    
Fedora Modular 36 - x86_64 - Updates                                                                                                            1.4 MB/s | 2.2 MB     00:01
...

SSHでVMにアクセス、VM内でyumコマンドを実行してもちゃんとリポジトリと繋がるのでネットワーク的にも問題ありませんでした。 これで、KubernetesでVMの作成ができるKubeVirtを動かせる環境ができたわけです。