要約
- Kubernetes (API)で仮想マシンが扱える
- 仮想マシンでアプリケーションを実行できる
- 仮想マシン(QEMU-KVM)でできることはいろいろやれそう
各ソフトウェアの概要
KubeadmはKubernetesクラスターを作成するツールの一つです。Kubernetesは大規模なコンテナーをデプロイおよび管理するためのオープンソースソフトウェアです。基本的にアプリケーションはコンテナーで動作します。コンテナーは仮想マシンと比べて軽量なところが魅力ですが、仮想マシンと比べて分離されていないため一つのコンテナーの不具合が同じノード上の他のコンテナーに影響を与えたり、セキュリティ上の様々な問題が発生するという懸念があります。
もちろんそうならないために日頃から様々なセキュリティ対策(パッチを適用するなど)を施す必要があるわけですが、今回は数多くある対策の中から、KubeVirtを取り上げてみようと思います。
Kubernetes 1.21が4月8日にリリースされましたが、この内容はリリース前に行われたため、バージョン1.20.5で確認しています。
KubeVirtは名前からなんとなく想像つくかもしれませんが、Kubernetesで仮想マシンを使うためのものです。KubeVirtを使うとアプリケーションは仮想マシンの中で動作します。 文章でずらずらっと書くより、アーキテクチャー図を見てもらったほうが分かりやすいと思いますので、公式のリンクを貼ってみます。
ポイントとしてはKubeVirtが使えるKubernetesクラスターはアプリケーションの実行環境として、仮想マシンとコンテナーが使えるというところでしょうか。それらは同じネットワークで接続されているため、仮想マシン上のアプリケーションとコンテナー上のアプリケーションはネットワークアクセスできます。
今回はシングルノードのKubernetesクラスターをkubeadmで作成して、そのクラスターでKubeVirtを使えるような構成を取りたいと思います。
Kubeadmを使ったKubernetesクラスターの構築
検証に使ったマシン
めちゃくちゃ古いマシンで申し訳なさを感じますが、次のようなスペックのマシンを使いました。 この環境でも動かすことができるので、今の世代のマシンだったら大抵のマシンでも動くでしょう。
ハードウェア | スペック |
---|---|
CPU | Intel(R) Core(TM) i5 CPU 650 @ 3.20GHz |
メモリー | 16GB (PC3-10600E 4GB x4) |
ストレージ | 160GB SATA HDD |
OSのセットアップ
まず試しに動かしてみようと思い、KubeVirt on Kubernetes with CRI-O from scratchを参考にしたため、以下の構成でセットアップしています。OSはアップデートして最新の状態にしてから試したので7.9を使いました。CNIは参考情報からアレンジしています。
オブジェクト | 種類バージョン |
---|---|
OS | CentOS 7.9 |
CRI | CRI-O |
CNI | Calico |
CentOS向けのCRI-Oは安定稼働は難しいため、containerdを推奨します。CRIを変えても同じように実行可能です。
2021/05/07追記
Ubuntu 20.04およびOracle Linux 8でもKubeVirtの動作を手元の環境で確認。
CRIのセットアップ
公式のドキュメントContainer runtimesに従って、CRI-Oをインストールしました。 細かい話は以下にまとめてあります。
クラスターの作成
Installing kubeadmに従ってツールをインストールしたあと、Creating a cluster with kubeadmに従ってクラスターを作成します。 細かい話は以下にまとめてあります。
CNIとしてCalicoの導入
参考にしたKubeVirt on Kubernetes with CRI-O from scratch - Installing Kubernetes はMultus CNI+FlannelをCNIとして追加しているのですが、今回はCalicoを使うことにしました。
導入方法についてはCalico公式のドキュメントに従います。
永続ストレージの準備
ここまでで、普通のKubernetesのセットアップは出来上がっています。 後々仮想マシンには永続ストレージを提供する必要があるため、Rancherが開発しているLocal Path Provisionerを使っています(複数のノードを構成する場合は、永続データの提供方法の検討が必要なので注意)。
SELinux有効でも動作するようにするため、以下の作業を事前に行ってから上記のInstallationを実行しました。
$ sudo mkdir /opt/local-path-provisioner $ sudo chmod 777 /opt/local-path-provisioner $ sudo chcon -R -t svirt_sandbox_file_t /opt/local-path-provisioner
KubeVirtプロジェクトもほぼ同様のものを用意しているのでこっちを使うのもありです。
こちらの場合は事前に次のように実行してから導入します。
$ sudo mkdir /var/hpvolumes $ sudo chmod 777 /var/hpvolumes $ sudo chcon -t container_file_t -R /var/hpvolumes
KubeVirtをクラスターに追加
KubeVirtのインストール
KubeVirtのインストールについては基本的にはKubeVirt on Kubernetes with CRI-O from scratch - Installing KubeVirtを参考にセットアップしました。セットアップしたKubeVirtは導入時時点で最新だったバージョンを使いました。virtctlツールも同様のバージョンを/usr/local/bin/
のパスにインストールしています。
[4/21/2021 追記]
v0.39.0にはいくつか問題があり、その修正が行われたv0.40.0がリリースされたため、バージョンを変更しました。
$ kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/v0.40.0/kubevirt-operator.yaml $ kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/v0.40.0/kubevirt-cr.yaml $ curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/v0.40.0/virtctl-v0.40.0-linux-amd64 $ chmod +x virtctl $ sudo cp virtctl /usr/local/bin
KubeVirtを試す
引き続き、KubeVirt on Kubernetes with CRI-O from scratch - Installing KubeVirtの「Installing the first Virtual Machine in KubeVirt」に従って、Kubernetesで仮想マシンを使ってみたいと思います。
とは言いつつ、コマンドを実行するだけです。
$ kubectl apply -f https://raw.githubusercontent.com/kubevirt/kubevirt.github.io/master/labs/manifests/vm.yaml $ kubectl get vms NAME AGE RUNNING VOLUME testvm 13s false
vm.yamlという名前のYAMLをブラウザーなどで見てみると、testvmという名前のCirrOSというテスト用のOSイメージを使った仮想マシンを作成するマニフェストであることがわかります。メモリーは64MBを割り当てているようです。
仮想マシンを作ったら、このマシンをKubernetesから利用するために仮想マシンを起動する必要があります。次のように実行します。
$ virtctl start testvm VM testvm was scheduled to start
コマンドを実行して仮想マシンが起動したこと、IPアドレスが割り当てられたことを確認します。このIPアドレスはKubernetesに追加したCNIによって提供されます。
$ kubectl get vms NAME AGE RUNNING VOLUME testvm 7m11s true $ kubectl get vmis kubectl get vmis NAME AGE PHASE IP NODENAME testvm 14s Scheduling $ kubectl get vmis NAME AGE PHASE IP NODENAME testvm 63s Running 10.244.16.151 k8smaster
あとはいつものように仮想マシンにsshでアクセスするだけです。 ssh接続後の環境でコマンドを実行することで、ホストのカーネルを利用していないことがわかります。
$ uname -a Linux k8smaster 3.10.0-1160.21.1.el7.x86_64 #1 SMP Tue Mar 16 18:28:22 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux $ ssh cirros@10.244.16.151 cirros@10.244.16.151's password: gocubsgo $ uname -a Linux testvm 4.4.0-28-generic #47-Ubuntu SMP Fri Jun 24 10:09:13 UTC 2016 x86_64 GNU/Linux $ exit
これでKubernetesで仮想マシンが使える環境ができました。
OSイメージを追加する
前の手順でKubernetesで仮想マシンが使える環境ができましたが、テスト用のOSであるCirrOSが動いただけでは特に何もできないと思うので、別のOSを動かしてみます。 その方法は公式のドキュメントとしてまとまっているため、Experiment with the Containerized Data Importer (CDI)を参考にします。
永続ストレージはRancherが開発しているLocal Path Provisionerを事前に準備しましたので、 storage-setup.yml
というマニフェストは実行しないことにします。
$ export VERSION=$(curl -s https://github.com/kubevirt/containerized-data-importer/releases/latest | grep -o "v[0-9]\.[0-9]*\.[0-9]*") $ 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
上記実行後コマンドを実行して、全てのプロセスがRunningになったことを確認します。
$ kubectl get pods -n cdi NAME READY STATUS RESTARTS AGE cdi-apiserver-847d4bc7dc-2z976 1/1 Running 0 138m cdi-deployment-66d7555b79-hcr59 1/1 Running 0 138m cdi-operator-895bb5c74-pdpl8 1/1 Running 0 138m cdi-uploadproxy-6c8698cd8b-t9q6w 1/1 Running 0 138m
OSイメージを使ってKubernetesでVMを使う
まず、次のようなマニフェストを作成してクラウドイメージをベースとしてストレージボリュームに登録します。
今回は公式の手順と異なりRancherのLocal Path Provisionerを使っているため、storageClassNameをlocal-pathに書き換えます。
$ kubectl get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE local-path rancher.io/local-path Delete WaitForFirstConsumer false 127m $ cat 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/33/Cloud/x86_64/images/Fedora-Cloud-Base-33-1.2.x86_64.raw.xz" spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi storageClassName: local-path
もし kubevirtのhostpath-provisionerを使った場合は、storageClassName
で適切な名前を指定する他、リポジトリーの例のようにannotations
にノード名をkubevirt.io/provisionOnNode: kubevirt-demo
のように記述を追加する。
カンが良いかたは気がついたかと思いますが、イメージ、名前、ストレージサイズを適切に指定すれば、その他のOSも動くはずです(QEMU用のクラウドイメージさえあればいけると思います)。
あとは編集したマニフェストを使って次のように実行します。これにより、ボリュームにOSイメージが書き込まれます。
$ kubectl create -f pvc_fedora.yml
実行後、いくつかのコマンドを実行して、仮想マシンの作成状況を確認します。
$ kubectl get pvc fedora $ kubectl get pod # Make note of the pod name assigned to the import process
イメージインポートの進捗はkubectl logs
で確認できるようです。
$ kubectl logs -f importer-fedora # Substitute your importer-fedora pod name here. I0409 01:36:11.712534 1 importer.go:52] Starting importer I0409 01:36:11.712737 1 importer.go:134] begin import process I0409 01:36:13.325912 1 data-processor.go:356] Calculating available size I0409 01:36:13.326597 1 data-processor.go:368] Checking out file system volume size. I0409 01:36:13.326741 1 data-processor.go:376] Request image size not empty. I0409 01:36:13.326787 1 data-processor.go:381] Target size 5Gi. I0409 01:36:13.402364 1 nbdkit.go:240] Waiting for nbdkit PID. I0409 01:36:13.903065 1 nbdkit.go:261] nbdkit ready. I0409 01:36:13.903109 1 data-processor.go:238] New phase: Convert I0409 01:36:13.903153 1 data-processor.go:244] Validating image I0409 01:36:17.103228 1 qemu.go:259] 0.00 I0409 01:36:21.507712 1 qemu.go:259] 1.03 I0409 01:36:22.573000 1 qemu.go:259] 2.05 ... I0409 01:41:09.399546 1 qemu.go:259] 99.46 I0409 01:41:10.027139 1 data-processor.go:238] New phase: Resize W0409 01:41:10.061203 1 data-processor.go:342] Available space less than requested size, resizing image to available space 5073430016. I0409 01:41:10.061349 1 data-processor.go:348] Expanding image size to: 5073430016 I0409 01:41:10.092411 1 data-processor.go:244] Validating image I0409 01:41:10.109496 1 data-processor.go:238] New phase: Complete I0409 01:41:10.109634 1 importer.go:212] Import Complete
Fedoraイメージのインポートが終わったら、このイメージを使って仮想マシンを作ってみます。 サンプルのマニフェストを使って、仮想マシンを作ってみます。
$ curl -LO https://raw.githubusercontent.com/kubevirt/kubevirt.github.io/master/labs/manifests/vm1_pvc.yml $ cat vm1_pvc.yml
vm1_pvc.yml
をエディターで開いて、ssh_authorized_keysにSSHアクセス用の公開鍵を貼ってください(改行などに注意)。
#cloud-config hostname: vm1 ssh_pwauth: True disable_root: false ssh_authorized_keys: - ssh-rsa AAAAB3Y3v....zurqCPYEf ytooyama@k8smaster
編集したマニフェストを使って、仮想マシンを作成します。
$ kubectl create -f vm1_pvc.yml virtualmachine.kubevirt.io/vm1 created $ kubectl get -f vm1_pvc.yml NAME AGE VOLUME vm1 5s $ kubectl get vmi NAME AGE PHASE IP NODENAME testvm 35m Running 10.244.16.151 k8smaster vm1 2m9s Running 10.244.16.152 k8smaster
vm1にsshアクセスしてみます。Fedoraのクラウドイメージのユーザーはfedora
です。
$ ssh fedora@10.244.16.152 Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '10.244.16.152' (ECDSA) to the list of known hosts. Last login: Thu Apr 8 07:36:17 2021 from 192.168.0.120 [fedora@vm1 ~]$
うまく行きました。VMから外部への疎通も問題なさそうです。
[fedora@vm1 ~]$ ping -c1 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=113 time=13.5 ms --- 8.8.8.8 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 13.455/13.455/13.455/0.000 ms [fedora@vm1 ~]$ ping -c1 virtualtech.jp PING virtualtech.jp (124.35.85.83) 56(84) bytes of data. 64 bytes from main.begi.net (124.35.85.83): icmp_seq=1 ttl=49 time=16.9 ms --- virtualtech.jp ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 16.902/16.902/16.902/0.000 ms
長くなりましたが、KubernetesでKubeVirtが使えるようになりました。 今回は基本的なKubeVirtの利用方法について実証実験してみましたが、KubeVirtはMultus CNIを使ったり)、Host DevicesでPCI Passthroughして仮想マシンに引き込んだりいろいろできる(vGPUとかも使えるようです)らしいです。
去年の年末、頑張ったけどうまくいかなかったことができそうかも? 今回は「味見」をした程度なので、今後とも引き続き調査したいと思っています。いずれホストOSをUbuntu Serverにして実験などもしたいですね。
参考にした情報
- Kubernetes Production environment
- KubeVirt on Kubernetes with CRI-O from scratch
- KubeVirt on Kubernetes with CRI-O from scratch - Installing Kubernetes
- KubeVirt on Kubernetes with CRI-O from scratch - Installing KubeVirt
- Use KubeVirt
- KubeVirt Example
- GitHub Rancher/local-path-provisioner
付録
CentOS Stream 8も試して、動作を確認しました。Fedoraイメージと比べてイメージの圧縮率が高いこともあり、インポートには相応の時間がかかりました。ストレージ10GBでは不足したため、12GBを指定しています。app nameとラベルを書き換えています。ログインユーザーはcentos
でした。
$ cat pvc_centos-stream8.yml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: "centost8" labels: app: containerized-data-importer2 annotations: cdi.kubevirt.io/storage.import.endpoint: "http://cloud.centos.org/centos/8-stream/x86_64/images/CentOS-Stream-GenericCloud-8-20210210.0.x86_64.qcow2" spec: accessModes: - ReadWriteOnce resources: requests: storage: 12Gi storageClassName: local-path $ ssh centos@10.244.16.150 Activate the web console with: systemctl enable --now cockpit.socket [centos@vm2 ~]$ cat /etc/centos-release CentOS Stream release 8