KubeVirtは、Kubernetes上でVirtual Machineを作成する技術です。
OpenShift Container Platformに組み込まれており、OpenShift Virtualizationと呼ばれています。
Apple社がKubeCon+CloudNativeCon Europe 20201にてKubeVirtを利用していることを紹介していました。
「Kubernetes上でVMを作る?」
パブリッククラウド/プライベートクラウドを利用されている方はクラウドのIaaSを使ってVMを作ることが多いかと思います。そもそもManagedなKaaSを使っている方の多くは、Kubernetesクラスタ自体はVMのNodeで構成されている場合が多いかと思います。そのためKubernetes上でVMを作る必要性を感じることは少ないかもしれません。
ベアメタルでKubernetesクラスタを使っているが、どうしてもVMでないとサポートされない製品を稼働させる、とか、Legacyなシステムでコンテナ化が難しいものの移行先が欲しい、といったユースケースの場合、KubeVirtが候補として挙がってくるのかもしれません。
KubeVirtは大変興味深い技術です。
ググると、KubeVirtを使ってみたという情報はすぐ見つかります。しかしKubernetes上で稼働している点に注目し、Kubernetesのオブジェクトとどう連携できるのか?それによってVMのオペレーションがどう便利になるのか?という点に着目した情報はあまりありませんでした。
この記事では、具体的に以下に注目して掘り下げてみたいと思います。
- ConfigMap/Secret連携
- Service連携
- Ingress連携
- VMのメトリクス
環境
KubeVirtはVM上でもEmulateすることができるので、お手軽に試用することができます。
今回はminikube(hyperkit)上でVMを作りたいと思います。
- minikube 1.15.1
- Kubernetes 1.19.4
- kubevirt v0.34.0
- macOS Catalina 10.15.17
セットアップ
KubeVirtはminikubeのaddonに組み込まれており、今回はそちらを利用します。
minikube上で、VMを起動するため、メモリをデフォルトより増やしておきます。
また、後ほどIngressも試すので、nginx ingress controller
を有効にします。
$ minikube start --memory=6g
$ minikube addons enable kubevirt
$ minikube addons enable ingress
KubeVirtのコンポーネントがすべてRunning
になったか確認します。
$ kubectl get po --namespace kubevirt
NAME READY STATUS RESTARTS AGE
virt-api-7dd54c49d8-7gx9d 1/1 Running 0 3m19s
virt-api-7dd54c49d8-sj882 1/1 Running 0 3m19s
virt-controller-65d54bb555-6ddmv 1/1 Running 0 2m34s
virt-controller-65d54bb555-jrwn8 1/1 Running 0 2m34s
virt-handler-zf4zs 1/1 Running 0 2m34s
virt-operator-57f89f688f-47p92 1/1 Running 0 3m58s
virt-operator-57f89f688f-766ps 1/1 Running 0 3m58s
KubeVirtのVMを操作するCLI virtctl
はkubectl plugins
で提供されているので、今回はそちらを利用します。
$ kubectl krew install virt
$ kubectl plugin list
The following compatible plugins are available:
/Users/XXXX/.krew/bin/kubectl-virt
後ほどConfigMap, Secretとの連携を試すので、適当なConfigMap, Secretを作成します。
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: sample-configmap
data:
setup.sh: |
#!/bin/bash
echo "Do something!"
---
apiVersion: v1
kind: Secret
metadata:
name: sample-secret
type: Opaque
stringData:
secret.yaml: |-
secret: "important"
EOF
VMの作成
さっそくVMを作りますが、今回はゲストOSとしてKubeVirtがメンテナンスしているfedora
を使います。
以下のManifestを使います。
今回CloudinitでSecret, ConfigMap 2 のDiskをマウント、パスワードをfedora
にしています。
Manifestの中身が気になる方は、公式ドキュメントを読まれるとよいかと思います。
apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachine
metadata:
name: testvm
spec:
running: false
template:
metadata:
labels:
kubevirt.io/size: small
kubevirt.io/domain: testvm
spec:
domain:
devices:
disks:
- name: containerdisk
disk:
bus: virtio
- name: cloudinitdisk
disk:
bus: virtio
- name: configmap-disk
disk: {}
serial: CVLY623300HK240D
- name: secret-disk
disk: {}
serial: DVLY623300HK240D
interfaces:
- name: default
bridge: {}
resources:
requests:
memory: 4096M
networks:
- name: default
pod: {}
volumes:
- name: containerdisk
containerDisk:
image: kubevirt/fedora-cloud-container-disk-demo
- name: cloudinitdisk
cloudInitNoCloud:
userData: |-
#cloud-config
password: fedora
chpasswd: { expire: False }
bootcmd:
- "mkdir /mnt/configmap"
- "mount /dev/$(lsblk --nodeps -no name,serial | grep CVLY623300HK240D | cut -f1 -d' ') /mnt/configmap"
- "mkdir /mnt/secret"
- "mount /dev/$(lsblk --nodeps -no name,serial | grep DVLY623300HK240D | cut -f1 -d' ') /mnt/secret"
- configMap:
name: sample-configmap
name: configmap-disk
- secret:
secretName: sample-secret
name: secret-disk
$ kubectl apply -f testvm.yaml
# VM(testvm)起動
$ kubectl virt start testvm
$ kubectl get vmi
NAME AGE PHASE IP NODENAME
testvm 60s Running 172.17.0.12 minikube
vmi(Virtual Machine Instance)
のPHASE
がRunning
になるまで待ち、Console接続をします。
Console接続中も、Fedoraのセットアップが終わるまで多少時間がかかります。
しばらく待ちましょう。
$ kubectl virt console testvm
Successfully connected to testvm console. The escape sequence is ^]
testvm login: fedora
Password: <- fedoraです
testvm.yaml
で指定した場所にConfigMap
とSecret
がMountできているか確認します。
[fedora@testvm ~]$ ls /mnt/*
/mnt/configmap:
setup.sh
/mnt/secret:
secret.yaml
[fedora@testvm ~]$ cat /mnt/secret/secret.yaml
secret: "important"[fedora@testvm ~]$
ConfigMapもSecretもMountされていました。Kubernetesのオブジェクトがそのまま使えるのは便利ですね。
Ctrl + ]
でConsoleを抜けます。
Kubernetes自体のConfigMap,SecretをPodでファイルマウントする場合は、動的に更新が反映されますが、KubeVirtのVMでは作成時の状態が維持されます。つまり動的に反映されません。
一括で設定変更したり、証明書を差し替えたりといったことができると大変便利だな、と期待していたのですが、残念ながらそのような使い方には対応していません。
クラスタ外からVMにssh
続いてクラスタ外からVMにsshできるようにします。
今回はクラスタ外(Mac)からVMの22番ポートにsshできるようにType=NodePortで公開します。もちろん、Type=Loadbalancer, ClusterIPも使用可能です。
以下のvirtctl
コマンドでも操作できるのですが、KubernetesのServiceで操作する方が個人的にはわかりやすいので、Serviceを作ります。
$ kubectl virt expose vmi testvm --name=vm1-ssh --port=27017 --target-port=22 --type=NodePort
のちほど、80番ポートを使ってnginxを稼働させるので、設定しておきます。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: vm-service
spec:
externalTrafficPolicy: Cluster
ports:
- port: 27017
protocol: TCP
targetPort: 22
name: ssh
- port: 27013
protocol: TCP
targetPort: 80
name: http
selector:
kubevirt.io/domain: testvm
type: NodePort
EOF
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5h23m
vm-service NodePort 10.108.41.198 <none> 27017:30722/TCP,27013:30981/TCP 5h16m
$ ssh fedora@`minikube ip` -p 30722
The authenticity of host '[192.168.64.23]:30722 ([192.168.64.23]:30722)' can't be established.
ECDSA key fingerprint is SHA256:8ALbFD6Exh9DDywwAeTVEpYThohTAsYkDMT1StBVYhc.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '[192.168.64.23]:30722' (ECDSA) to the list of known hosts.
fedora@192.168.64.23's password:
[fedora@testvm ~]$
のちほど、Podからの通信を確認するために、nginxをインストール・起動します。
[fedora@testvm ~]$ sudo dnf install -y nginx
[fedora@testvm ~]$ sudo nginx
[fedora@testvm ~]$ curl localhost
…省略
Pod-VM間の通信
デフォルトでは、VMはPodのネットワークとLinux bridgeで接続されます。
他にもMultusを使いNICを追加したり、IP Masqueradeするようにしたりできるようです。
まず、Pod to VM です。
$ kubectl get vmi
NAME AGE PHASE IP NODENAME
testvm 20m Running 172.17.0.2 minikube
$ kubectl run -it --rm curl --image curlimages/curl sh
/ $ curl 172.17.0.2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Test Page for the HTTP Server on Fedora</title>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
<style type="text/css">
…省略
問題なく疎通できてますね。
続いてVM to Podです。
$ kubectl run -it --rm nginx --image nginx
$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 10s 172.17.0.12 minikube <none> <none>
virt-launcher-testvm-ft8pv 2/2 Running 0 23m 172.17.0.2 minikube <none> <none>
$ ssh fedora@`minikube ip` -p 30722 curl 172.17.0.12
fedora@192.168.64.23's password:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 612 100 612 0 0 22666 0 --:--:-- --:--:-- --:--:-- 36000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
…省略
問題なく疎通できています。
Ingress連携
さて、Serviceも動き、Pod-VM間通信も問題ないということは、Ingress連携もできるわけですね。
$ cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: vm-service
port:
number: 80
EOF
$ kubectl get ingress.v1.networking.k8s.io
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress <none> * 192.168.64.23 80 5h36m
$ open https://192.168.64.23/testpath
いきました。これでVMの前段でTLS終端をしたり、hostnameを設定したり色々できますね。
いやー、冬なのに熱いですね。
VMのメトリクス
KubeVirtのアプリケーション自体のメトリクスはもちろんですが、VMのメトリクスもデフォルトで取得することが可能です。
Namespace:kubevirt
Pod:virt-handler
Port:8443
で取得することができます。より詳細な情報はこちら3 4。
$ kubectl get po -o wide --namespace kubevirt
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
virt-api-7dd54c49d8-7gx9d 1/1 Running 0 63m 172.17.0.8 minikube <none> <none>
virt-api-7dd54c49d8-sj882 1/1 Running 0 63m 172.17.0.9 minikube <none> <none>
virt-controller-65d54bb555-6ddmv 1/1 Running 0 62m 172.17.0.10 minikube <none> <none>
virt-controller-65d54bb555-jrwn8 1/1 Running 0 62m 172.17.0.11 minikube <none> <none>
virt-handler-zf4zs 1/1 Running 0 62m 172.17.0.7 minikube <none> <none>
virt-operator-57f89f688f-47p92 1/1 Running 0 63m 172.17.0.4 minikube <none> <none>
virt-operator-57f89f688f-766ps 1/1 Running 0 63m 172.17.0.5 minikube <none> <none>
$ kubectl run -it --rm curl --image curlimages/curl sh
If you don't see a command prompt, try pressing enter.
/ $ curl --insecure https://172.17.0.7:8443/metrics
以下は対象のVMのメトリクス(CPU利用率)だけ抜粋したものです。labelにtestvm
が含まれており、対象のVMを識別することができます。
# HELP kubevirt_vmi_vcpu_seconds Vcpu elapsed time.-:-- --:--:-- 29742
# TYPE kubevirt_vmi_vcpu_seconds gauge
kubevirt_vmi_vcpu_seconds{id="0",kubernetes_vmi_label_kubevirt_io_domain="testvm",kubernetes_vmi_label_kubevirt_io_nodeName="minikube",kubernetes_vmi_label_kubevirt_io_size="small",name="testvm",namespace="default",node="minikube",state="running"} 1064
# HELP kubevirt_vmi_vcpu_wait_seconds vcpu time spent by waiting on I/O
# TYPE kubevirt_vmi_vcpu_wait_seconds gauge
kubevirt_vmi_vcpu_wait_seconds{id="0",kubernetes_vmi_label_kubevirt_io_domain="testvm",kubernetes_vmi_label_kubevirt_io_nodeName="minikube",kubernetes_vmi_label_kubevirt_io_size="small",name="testvm",namespace="default",node="minikube"} 0
…省略
VMにNode Exporterを設定せずとも、デフォルトでメトリクスが出力されているのは大変ありがたい。
既存のメトリクスモニタリングシステムと連携できますね。
Cleanup
vmオブジェクトを削除してもいいのですが、メモリも変更したので、minikubeごと壊してしまいます。
$ minikube delete
$ kubectl krew uninstall virt
所感
今回、KubeVirtで作成したVMがKubernetesオブジェクトをどう活用できるか、という点に注目して触ってみました。
結果として、思ったよりも多くKubernetesオブジェクトと連携することができ、VM-Pod間の通信、ConfigMapやSecretのマウント、Service、Ingressとの連携ができました。
さらにデフォルトでVMのMetricsも取得することができたのは、非常に便利です。
今回は触れていませんが、この他にもPVCやServiceAccountのマウントが可能です。
一方、試してみて残念だった点は、Configmap, Secretの動的な更新に対応できないことです。おそらくProjected Service Account Tokenにも対応できていないでしょう。PVにも制限がありそうです。
私は普段の業務でTerraformでIaaSを操作し、VMやLBをセットアップしていますが、その体験がKubeVirtにおいては、terraform=kubectl
に、Remote Storage=etcd
に。そんな印象を受けました。(terraform自体にもKubernetes Providerがあるので、そのあたりはなんとも言い難いのですが。。)
ところで、KubeVirtを仮に本番で安定稼働させるには悩ましい点がいっぱいです。
作成したVMの性能検証、Kubernetes・KubeVirtのバージョンアップ、Kubernetesクラスタ全体のコンピュートリソースの管理、CNI、実際の開発者が持つ権限、Kubernetesクラスタ間のVMの通信など、たくさんの検討箇所があります。Kubernetesクラスタの管理・操作そのものに長ける必要もありますし、そもそもKubernetesでVMを稼働させる前に、Podで稼働させられるようチャレンジした方がよいのでは?ということもあるでしょう。
色々考えさせられますが、非常に野心的な技術であることには間違いありません。
少し古い記事ですが、IstioとKubeVirtを連携するblog5が公開されていました。
このようなクラウドネイティブな技術と簡単に結合できるようになると、これまでのVMのオペレーションからさらに進化したオペレーションが生まれるのかもしれません。
今後さらに踏み込んで、KubeVirtとKubernetesとの連携がさらに強化され、便利なツールなど提供されるとよいな、と期待しています。
-
https://kccnceu20.sched.com/event/ZevE/kubevirt-intro-using-kubevirt-to-run-vms-at-scale-fabian-deutsch-red-hat-marcus-sorensen-apple ↩
-
https://kubevirt.io/user-guide/#/creation/disks-and-volumes?id=configmap ↩
-
https://kubevirt.io/user-guide/#/installation/monitoring?id=metrics-about-virtual-machines ↩