20
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

KubeVirtとKubernetesオブジェクトの連携

Last updated at Posted at 2020-12-19

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 virtctlkubectl 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の中身が気になる方は、公式ドキュメントを読まれるとよいかと思います。

testvm.yaml
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)PHASERunningになるまで待ち、Console接続をします。
Console接続中も、Fedoraのセットアップが終わるまで多少時間がかかります。
しばらく待ちましょう。

$ kubectl virt console testvm
Successfully connected to testvm console. The escape sequence is ^]
testvm login: fedora
Password:          <- fedoraです

testvm.yamlで指定した場所にConfigMapSecretが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 

スクリーンショット 2020-12-16 15.42.43.png

いきました。これで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との連携がさらに強化され、便利なツールなど提供されるとよいな、と期待しています。

  1. https://kccnceu20.sched.com/event/ZevE/kubevirt-intro-using-kubevirt-to-run-vms-at-scale-fabian-deutsch-red-hat-marcus-sorensen-apple

  2. https://kubevirt.io/user-guide/#/creation/disks-and-volumes?id=configmap

  3. https://kubevirt.io/2019/More-about-Kubevirt-metrics.html

  4. https://kubevirt.io/user-guide/#/installation/monitoring?id=metrics-about-virtual-machines

  5. https://kubevirt.io/2018/Run-Istio-with-kubevirt.html

20
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?