TECHSTEP

ITインフラ関連の記事を公開してます。

Kubevirt(v0.21.0)を触ってみる

はじめに

Docker、Kubernetesをはじめとするコンテナ技術が普及するにつれ、現行でVirtual Machine(VM)が稼働している環境からコンテナへの移行を、どうやって実現するかが課題になってきました。

例えば、リフト&シフトなどの方法で基盤を移行させることを検討する際、VMとコンテナが共通基盤上にあるほうが、移行はやりやすくなるだろうと考えられました。

Kubevirtはそんなソリューションを提供するツールの一つになります。

今回はKubevirtについて簡単に紹介し、新しくリリースされたv0.21.0で利用可能となったvirtctl migrateコマンドを試してみました。

結論

  • KubevirtはVMとコンテナをKubernetes基盤上で共通管理するツールである。
  • v0.21.0よりvirtctl migrateコマンドが利用可能になり、VMのライブマイグレーションがより簡単にできるようになった。

Kubevirtとは

kubevirt-logo

KubevirtVMとコンテナを共通の基盤で管理できるようなツールです。Kubevirtは例えば以下のような機能を提供します。

  • Kubernetes上でVMとコンテナを共通リソースとして管理する
    • kubectlコマンドによってVMを管理可能にする
  • virtctlコマンドによってVMの起動・停止等の操作を行う
  • CDI (Containerized Data Importer)にVMイメージをインポートし、保存したイメージを利用してVMを起動することができる
  • VMをあるノードから別のノードへとライブマイグレーションを行うことができる

Kubevirtのコンポーネントは、以下の図のようになります。 kubevirt_components

  • virt-controllerクラスターワイドな仮想化機能を提供するKubernetes Operator。新しいVMオブジェクトがKubernetes APIサーバにポストされると、VMを配置するPodを作成します。Podが特定のノードにスケジューリングされると、virt-controllerVMオブジェクトをアップデートし、virt-handlerに引き渡します。
  • virt-handlerKubernetesノードごとに配置されたKubevirtコンポーネントvirt-controllerと同様、VMオブジェクトの変化を検知し、Required StateにVMを変更します。またVMの配置されたPod上にあるlibvirtdインスタンスによって、VMに対応するdomain(resources, devices等のコンフィグ情報)が作成されます。
  • virt-launcherVMをホストするために使用するcgroupsとnamespaceを提供します。virt-handlerVMのCRDオブジェクトをvirt-launcherに渡すことでVMの起動を通知し、それを受けてvirt-launcherlibvirtdを用いてVMを起動します。そこからvirt-launcherVMプロセスを監視します。
  • libvirtd:各VM Podに配置されます。virt-launcherVMプロセスのライフサイクルを管理するためにlibvirtdを利用します。

Kubevirtのデプロイ

ここからKubevirtのデプロイを進めます。手順はKubevirtの公式ドキュメントに紹介されいますので、そちらを参照して進めていきます。

※参考ドキュメント:

Easy install using Minikube

構築環境

Kubevirt Operatorのデプロイ

まずKubevirt Operatorをデプロイします。Kubernetes Operatorについては様々なブログ・ドキュメントで説明されていますが、Kubernetes向けのStatefulアプリケーションを自動で実行・管理するCustom Controllerです。

※参考ドキュメント:

CoreOS: Operators

CoreOS: Introducing Operators: Putting Operational Knowledge into Software

Qiita - Kubernetes Operator

Kubernetes Operator を理解する

# KUBEVIRT_VERSIONの設定

[root@kube-master01 kubevirt]# export KUBEVIRT_VERSION=$(curl -s https://api.github.com/repos/kubevirt/kubevirt/releases|grep tag_name|sort -V | tail -1 | awk -F':' '{print $2}' | sed 's/,//' | xargs | cut -d'-' -f1)
[root@kube-master01 kubevirt]# echo $KUBEVIRT_VERSION
v0.21.0
[root@kube-master01 kubevirt]#


# kubevirt-operatorのデプロイ

[root@kube-master01 kubevirt]# kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-operator.yaml
namespace/kubevirt created
customresourcedefinition.apiextensions.k8s.io/kubevirts.kubevirt.io created
clusterrole.rbac.authorization.k8s.io/kubevirt.io:operator created
serviceaccount/kubevirt-operator created
clusterrole.rbac.authorization.k8s.io/kubevirt-operator created
clusterrolebinding.rbac.authorization.k8s.io/kubevirt-operator created
deployment.apps/virt-operator created
[root@kube-master01 kubevirt]#


# デプロイ後のリソース確認
[root@kube-master01 kubevirt]# kubectl get ns
NAME              STATUS   AGE
default           Active   43d
kube-node-lease   Active   43d
kube-public       Active   43d
kube-system       Active   43d
kubevirt          Active   9s
metallb-system    Active   43d
[root@kube-master01 kubevirt]# 
[root@kube-master01 kubevirt]# kubectl get pods -n kubevirt -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP                NODE            NOMINATED NODE   READINESS GATES
virt-operator-5757956476-j7nsc   1/1     Running   0          27s   192.168.132.23    kube-worker01   <none>           <none>
virt-operator-5757956476-zqc77   1/1     Running   0          27s   192.168.247.219   kube-worker02   <none>           <none>
[root@kube-master01 kubevirt]# kubectl get all -n kubevirt
NAME                                 READY   STATUS    RESTARTS   AGE
pod/virt-operator-5757956476-j7nsc   1/1     Running   0          61s
pod/virt-operator-5757956476-zqc77   1/1     Running   0          61s




NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/virt-operator   2/2     2            2           61s

NAME                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/virt-operator-5757956476   2         2         2       61s




[root@kube-master01 kubevirt]#

Virtualization Extensionsの確認

次に利用しているマシンのCPUがVirtualization Extensionsに対応しているかを確認します。対応していない場合、kubevirt-configというConfigMapを作成し、Kubevirtのemulation modeを利用できるようにします。

#Virtualization Extensionsに対応しているかを確認

[root@kube-master01 kubevirt]# egrep 'svm|vmx' /proc/cpuinfo
[root@kube-master01 kubevirt]#

# 対応していないのでConfigMapを作成

[root@kube-master01 kubevirt]# kubectl create configmap kubevirt-config -n kubevirt --from-literal debug.useEmulation=true
configmap/kubevirt-config created
[root@kube-master01 kubevirt]#

[root@kube-master01 kubevirt]# kubectl get cm -n kubevirt
NAME              DATA   AGE
kubevirt-config   1      17s
[root@kube-master01 kubevirt]# 

Kubevirtのデプロイ

次にKubevirtをデプロイします。ここではCustom ResourceをデプロイすることでKubevirtを利用可能にします。まずvirt-apiがデプロイされ、その後virt-controller virt-handlerがデプロイされます。

# Kubevirt Custom Resourceをデプロイ

[root@kube-master01 kubevirt]# kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/kubevirt-cr.yaml
kubevirt.kubevirt.io/kubevirt created
[root@kube-master01 kubevirt]#

[root@kube-master01 kubevirt]# kubectl get pods -n kubevirt
NAME                               READY   STATUS    RESTARTS   AGE
virt-api-6f88888558-8s5b8          1/1     Running   0          73s
virt-api-6f88888558-gqzl9          1/1     Running   0          73s
virt-controller-58f4858df6-j4z96   1/1     Running   0          45s
virt-controller-58f4858df6-nwkbl   1/1     Running   0          45s
virt-handler-ks6ng                 1/1     Running   0          45s
virt-handler-m7vwv                 1/1     Running   0          45s
virt-operator-5757956476-j7nsc     1/1     Running   0          3m57s
virt-operator-5757956476-zqc77     1/1     Running   0          3m57s
[root@kube-master01 kubevirt]#

virtctlのインストール

最後にvirtctlをインストールします。

# virtctlのインストール

[root@kube-master01 kubevirt]# curl -L -o virtctl \
>     https://github.com/kubevirt/kubevirt/releases/download/${KUBEVIRT_VERSION}/virtctl-${KUBEVIRT_VERSION}-linux-amd64
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   617    0   617    0     0   1102      0 --:--:-- --:--:-- --:--:--  1101
100 37.1M  100 37.1M    0     0  7646k      0  0:00:04  0:00:04 --:--:-- 10.8M
[root@kube-master01 kubevirt]# chmod +x virtctl
[root@kube-master01 kubevirt]# mv virtctl /usr/local/bin/virtctl


# インストール後の確認

[root@kube-master01 kubevirt]# virtctl version
Client Version: version.Info{GitVersion:"v0.21.0", GitCommit:"e220f16d7a2aae7295199bbbc3594a5b50b7a3f4", GitTreeState:"clean", BuildDate:"2019-09-07T07:33:17Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{GitVersion:"v0.21.0", GitCommit:"e220f16d7a2aae7295199bbbc3594a5b50b7a3f4", GitTreeState:"clean", BuildDate:"2019-09-09T08:09:15Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}
[root@kube-master01 kubevirt]#
[root@kube-master01 kubevirt]# virtctl help
virtctl controls virtual machine related operations on your kubernetes cluster.

Available Commands:
  console      Connect to a console of a virtual machine instance.
  expose       Expose a virtual machine instance, virtual machine, or virtual machine instance replica set as a new service.
  help         Help about any command
  image-upload Upload a VM image to a PersistentVolumeClaim.
  migrate      Migrate a virtual machine.
  restart      Restart a virtual machine.
  start        Start a virtual machine.
  stop         Stop a virtual machine.
  version      Print the client and server version information.
  vnc          Open a vnc connection to a virtual machine instance.

Use "virtctl <command> --help" for more information about a given command.
Use "virtctl options" for a list of global command-line options (applies to all commands).
[root@kube-master01 kubevirt]#

VMのデプロイ

次にVMをデプロイします。ここでも公式ドキュメントで紹介されたデモ用VMのデプロイをやってみます。

※参考ドキュメント:

Use Kubevirt

Virtual Machineの作成

VMのデプロイでは、デモ用のyamlファイルが用意されているので、それをデプロイします。

デモ用のデータでは、まずVirtualMachineリソースをデプロイします。ここではCirrOSを利用したVirtualMachineをデプロイします。

# wgetでyamlファイルをダウンロード
[root@kube-master01 kubevirt]# wget https://raw.githubusercontent.com/kubevirt/kubevirt.github.io/master/labs/manifests/vm.yaml
--2019-09-16 02:27:54--  https://raw.githubusercontent.com/kubevirt/kubevirt.github.io/master/labs/manifests/vm.yaml
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.108.133
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 840 [text/plain]
Saving to: ‘vm.yaml’

100%[==============================================================================>] 840         --.-K/s   in 0s

2019-09-16 02:27:54 (159 MB/s) - ‘vm.yaml’ saved [840/840]

[root@kube-master01 kubevirt]#
[root@kube-master01 kubevirt]# ls -l
total 16
-rw-r--r-- 1 root root 10204 Sep  9 08:32 kubevirt-operator.yaml
-rw-r--r-- 1 root root   840 Sep 16 02:27 vm.yaml
[root@kube-master01 kubevirt]# 

# VirtualMachineのデプロイ

[root@kube-master01 kubevirt]# kubectl apply -f vm.yaml
virtualmachine.kubevirt.io/testvm created
[root@kube-master01 kubevirt]# 

# デプロイ後の確認

[root@kube-master01 kubevirt]# kubectl get vms
NAME     AGE   RUNNING   VOLUME
testvm   7s    false
[root@kube-master01 kubevirt]# 

上記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
          interfaces:
          - name: default
            bridge: {}
        resources:
          requests:
            memory: 64M
      networks:
      - name: default
        pod: {}
      volumes:
        - name: containerdisk
          containerDisk:
            image: kubevirt/cirros-registry-disk-demo
        - name: cloudinitdisk
          cloudInitNoCloud:
            userDataBase64: SGkuXG4=

なお、デプロイしたVirtualMachinekubectl get vm -o yaml kubectl describeで表示した結果も、併せて載せておきます。

kubectl get vms testvm -o yaml

apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachine
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"kubevirt.io/v1alpha3","kind":"VirtualMachine","metadata":{"annotations":{},"name":"testvm","namespace":"default"},"spec":{"running":false,"template":{"metadata":{"labels":{"kubevirt.io/domain":"testvm","kubevirt.io/size":"small"}},"spec":{"domain":{"devices":{"disks":[{"disk":{"bus":"virtio"},"name":"containerdisk"},{"disk":{"bus":"virtio"},"name":"cloudinitdisk"}],"interfaces":[{"bridge":{},"name":"default"}]},"resources":{"requests":{"memory":"64M"}}},"networks":[{"name":"default","pod":{}}],"volumes":[{"containerDisk":{"image":"kubevirt/cirros-registry-disk-demo"},"name":"containerdisk"},{"cloudInitNoCloud":{"userDataBase64":"SGkuXG4="},"name":"cloudinitdisk"}]}}}}
    kubevirt.io/latest-observed-api-version: v1alpha3
    kubevirt.io/storage-observed-api-version: v1alpha3
  creationTimestamp: "2019-09-16T02:28:42Z"
  generation: 2
  name: testvm
  namespace: default
  resourceVersion: "755876"
  selfLink: /apis/kubevirt.io/v1alpha3/namespaces/default/virtualmachines/testvm
  uid: af647f7a-b860-4390-85a3-ae1255a1ec14
spec:
  running: false
  template:
    metadata:
      creationTimestamp: null
      labels:
        kubevirt.io/domain: testvm
        kubevirt.io/size: small
    spec:
      domain:
        devices:
          disks:
          - disk:
              bus: virtio
            name: containerdisk
          - disk:
              bus: virtio
            name: cloudinitdisk
          interfaces:
          - bridge: {}
            name: default
        machine:
          type: ""
        resources:
          requests:
            memory: 64M
      networks:
      - name: default
        pod: {}
      volumes:
      - containerDisk:
          image: kubevirt/cirros-registry-disk-demo
        name: containerdisk
      - cloudInitNoCloud:
          userDataBase64: SGkuXG4=
        name: cloudinitdisk
status: {}

kubectl describe vms testvm

Name:         testvm
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"kubevirt.io/v1alpha3","kind":"VirtualMachine","metadata":{"annotations":{},"name":"testvm","namespace":"default"},"spec":{"...
              kubevirt.io/latest-observed-api-version: v1alpha3
              kubevirt.io/storage-observed-api-version: v1alpha3
API Version:  kubevirt.io/v1alpha3
Kind:         VirtualMachine
Metadata:
  Creation Timestamp:  2019-09-16T02:28:42Z
  Generation:          2
  Resource Version:    755876
  Self Link:           /apis/kubevirt.io/v1alpha3/namespaces/default/virtualmachines/testvm
  UID:                 af647f7a-b860-4390-85a3-ae1255a1ec14
Spec:
  Running:  false
  Template:
    Metadata:
      Creation Timestamp:  <nil>
      Labels:
        kubevirt.io/domain:  testvm
        kubevirt.io/size:    small
    Spec:
      Domain:
        Devices:
          Disks:
            Disk:
              Bus:  virtio
            Name:   containerdisk
            Disk:
              Bus:  virtio
            Name:   cloudinitdisk
          Interfaces:
            Bridge:
            Name:  default
        Machine:
          Type:
        Resources:
          Requests:
            Memory:  64M
      Networks:
        Name:  default
        Pod:
      Volumes:
        Container Disk:
          Image:  kubevirt/cirros-registry-disk-demo
        Name:     containerdisk
        Cloud Init No Cloud:
          userDataBase64:  SGkuXG4=
        Name:              cloudinitdisk
Status:
Events:  <none>

VirtualMachineの管理

次にデプロイしたVirtualMachineを起動します。VirtualMachineの起動にはvirtctlコマンドを利用するか、kubectl patchでリソースの編集を行います。

# virtctlコマンドで起動

[root@kube-master01 kubevirt]# virtctl start testvm
VM testvm was scheduled to start

# VirtualMachineの状態確認

[root@kube-master01 kubevirt]# kubectl get vms
NAME     AGE     RUNNING   VOLUME
testvm   3m22s   true
[root@kube-master01 kubevirt]# 

VirtualMachineを起動すると、VirtualMachineInstanceというリソースが作成されます。次にVirtualMachineInstanceリソースを確認します。

# VirtualMachineInstanceの確認

[root@kube-master01 kubevirt]# kubectl get vmis
NAME     AGE   PHASE     IP                NODENAME
testvm   16s   Running   192.168.247.212   kube-worker02
[root@kube-master01 kubevirt]#

上記のようにPHASERunningになっていれば起動しているので、VMにログインします。ログインにはSSHも利用可能ですが、virtctl consoleコマンドも用意されています。

# VMにログイン
[root@kube-master01 kubevirt]# virtctl console testvm
Successfully connected to testvm console. The escape sequence is ^]

[中略]


# コンソールの出力が停止したらEnterボタンを押すと、ログインIDを入力できる

login as 'cirros' user. default password: 'gocubsgo'. use 'sudo' for root.
testvm login:

上記VirtualMachineへのログインは、初期IDであるcirrosと初期PWであるgocubsgoを入力します。

# CirrOSにログイン
testvm login: cirros
Password:
$
$ pwd
/home/cirros
$ 

VirtualMachineから抜けるには、Ctrl + ]ボタンを入力します。

次にVirtualMachineを停止します。停止にはvirtctl stopコマンドを入力します。VirtualMachineを停止するとVirtualMachineInstanceリソースが削除されます。

# VirtualMachineの停止

[root@kube-master01 kubevirt]# virtctl stop testvm
VM testvm was scheduled to stop
[root@kube-master01 kubevirt]# kubectl get vm
NAME     AGE     RUNNING   VOLUME
testvm   6m24s   false
[root@kube-master01 kubevirt]# 

# VirtualMachineInstanceの確認

[root@kube-master01 kubevirt]# kubectl get vmi
No resources found.
[root@kube-master01 kubevirt]#

最後にVirtualMachineリソースを削除します。

[root@kube-master01 kubevirt]# kubectl delete -f vm.yaml
virtualmachine.kubevirt.io "testvm" deleted
[root@kube-master01 kubevirt]#
[root@kube-master01 kubevirt]# kubectl get vm
No resources found.
[root@kube-master01 kubevirt]#

こちらもkubectl get vmi -o yaml kubectl describeの結果を載せておきます。

kubectl get vmi testvm -o yaml

apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachineInstance
metadata:
  annotations:
    kubevirt.io/latest-observed-api-version: v1alpha3
    kubevirt.io/storage-observed-api-version: v1alpha3
  creationTimestamp: "2019-09-16T02:35:49Z"
  finalizers:
  - foregroundDeleteVirtualMachine
  generateName: testvm
  generation: 8
  labels:
    kubevirt.io/domain: testvm
    kubevirt.io/nodeName: kube-worker02
    kubevirt.io/size: small
  name: testvm
  namespace: default
  ownerReferences:
  - apiVersion: kubevirt.io/v1alpha3
    blockOwnerDeletion: true
    controller: true
    kind: VirtualMachine
    name: testvm
    uid: af647f7a-b860-4390-85a3-ae1255a1ec14
  resourceVersion: "757035"
  selfLink: /apis/kubevirt.io/v1alpha3/namespaces/default/virtualmachineinstances/testvm
  uid: c28c1261-fcfd-4be6-9425-28a323d2924d
spec:
  domain:
    devices:
      disks:
      - disk:
          bus: virtio
        name: containerdisk
      - disk:
          bus: virtio
        name: cloudinitdisk
      interfaces:
      - bridge: {}
        name: default
    features:
      acpi:
        enabled: true
    firmware:
      uuid: 5a9fc181-957e-5c32-9e5a-2de5e9673531
    machine:
      type: q35
    resources:
      requests:
        cpu: 100m
        memory: 64M
  networks:
  - name: default
    pod: {}
  volumes:
  - containerDisk:
      image: kubevirt/cirros-registry-disk-demo
      imagePullPolicy: Always
    name: containerdisk
  - cloudInitNoCloud:
      userDataBase64: SGkuXG4=
    name: cloudinitdisk
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: null
    message: cannot migrate VMI with a bridge interface connected to a pod network
    reason: InterfaceNotLiveMigratable
    status: "False"
    type: LiveMigratable
  - lastProbeTime: null
    lastTransitionTime: "2019-09-16T02:35:56Z"
    status: "True"
    type: Ready
  interfaces:
  - ipAddress: 192.168.247.208
    mac: e6:78:21:db:1c:bc
    name: default
  migrationMethod: BlockMigration
  nodeName: kube-worker02
  phase: Running
  qosClass: Burstable

kubectl describe vmi testvm

Name:         testvm
Namespace:    default
Labels:       kubevirt.io/domain=testvm
              kubevirt.io/nodeName=kube-worker02
              kubevirt.io/size=small
Annotations:  kubevirt.io/latest-observed-api-version: v1alpha3
              kubevirt.io/storage-observed-api-version: v1alpha3
API Version:  kubevirt.io/v1alpha3
Kind:         VirtualMachineInstance
Metadata:
  Creation Timestamp:  2019-09-16T02:35:49Z
  Finalizers:
    foregroundDeleteVirtualMachine
  Generate Name:  testvm
  Generation:     8
  Owner References:
    API Version:           kubevirt.io/v1alpha3
    Block Owner Deletion:  true
    Controller:            true
    Kind:                  VirtualMachine
    Name:                  testvm
    UID:                   af647f7a-b860-4390-85a3-ae1255a1ec14
  Resource Version:        757035
  Self Link:               /apis/kubevirt.io/v1alpha3/namespaces/default/virtualmachineinstances/testvm
  UID:                     c28c1261-fcfd-4be6-9425-28a323d2924d
Spec:
  Domain:
    Devices:
      Disks:
        Disk:
          Bus:  virtio
        Name:   containerdisk
        Disk:
          Bus:  virtio
        Name:   cloudinitdisk
      Interfaces:
        Bridge:
        Name:  default
    Features:
      Acpi:
        Enabled:  true
    Firmware:
      Uuid:  5a9fc181-957e-5c32-9e5a-2de5e9673531
    Machine:
      Type:  q35
    Resources:
      Requests:
        Cpu:     100m
        Memory:  64M
  Networks:
    Name:  default
    Pod:
  Volumes:
    Container Disk:
      Image:              kubevirt/cirros-registry-disk-demo
      Image Pull Policy:  Always
    Name:                 containerdisk
    Cloud Init No Cloud:
      userDataBase64:  SGkuXG4=
    Name:              cloudinitdisk
Status:
  Conditions:
    Last Probe Time:       <nil>
    Last Transition Time:  <nil>
    Message:               cannot migrate VMI with a bridge interface connected to a pod network
    Reason:                InterfaceNotLiveMigratable
    Status:                False
    Type:                  LiveMigratable
    Last Probe Time:       <nil>
    Last Transition Time:  2019-09-16T02:35:56Z
    Status:                True
    Type:                  Ready
  Interfaces:
    Ip Address:      192.168.247.208
    Mac:             e6:78:21:db:1c:bc
    Name:            default
  Migration Method:  BlockMigration
  Node Name:         kube-worker02
  Phase:             Running
  Qos Class:         Burstable
Events:
  Type    Reason            Age                  From                         Message
  ----    ------            ----                 ----                         -------
  Normal  SuccessfulCreate  2m8s                 virtualmachine-controller    Created virtual machine pod virt-launcher-testvm-4x4xf
  Normal  Created           2m1s (x3 over 2m1s)  virt-handler, kube-worker02  VirtualMachineInstance defined.
  Normal  Started           2m1s                 virt-handler, kube-worker02  VirtualMachineInstance started.

マイグレーション

ここからKubevirt v0.21.0から利用できるようになったvirtctl migrateコマンドを試してみます。これは、あるVMリソースを別のノードに移動させるコマンドです。これまではマイグレーション用のリソースであるVirtualMachineInstanceMigrationを毎回デプロイする必要がありましたが、virtctl migrateコマンドによりその手間を軽減することができます。

※参考ドキュメント:

Kubevirt - Live Migration

Kubevirt - Interfaces and Networks

LiveMigrationの有効化

まずLiveMigrationを有効にするため、kubevirt-configfeature-gatesという項目を追加します。

feature-gatesでLiveMigrationを有効にしていない場合、以下のようなメッセージが出力されます。なお、ここでは前章で利用したvm.yamlをデプロイし、VMを起動済みの状態です。

[root@kube-master01 kubevirt]# virtctl migrate testvm
Error migrating VirtualMachine an error on the server ("admission webhook \"migration-create-validator.kubevirt.io\" denied the request: LiveMigration feature gate is not enabled in kubevirt-config") has prevented the request from succeeding
[root@kube-master01 kubevirt]#

LiveMigrationを有効にするため、kubectl editコマンドでkubevirt-configを編集します。

# kubevirt-configの内容確認
[root@kube-master01 kubevirt]# kubectl get cm kubevirt-config -n kubevirt -o yaml
apiVersion: v1
data:
  debug.useEmulation: "true"
kind: ConfigMap
metadata:
  creationTimestamp: "2019-09-16T02:19:25Z"
  name: kubevirt-config
  namespace: kubevirt
  resourceVersion: "754300"
  selfLink: /api/v1/namespaces/kubevirt/configmaps/kubevirt-config
  uid: f504ca79-d38d-496e-b66f-67e7dc01af15
[root@kube-master01 kubevirt]# 

# 編集

[root@kube-master01 kubevirt]# kubectl edit cm kubevirt-config -n kubevirt
configmap/kubevirt-config edited
[root@kube-master01 kubevirt]#

# 結果確認
[root@kube-master01 kubevirt]# kubectl get cm kubevirt-config -n kubevirt -o yaml
apiVersion: v1
data:
  debug.useEmulation: "true"
  feature-gates: LiveMigration
kind: ConfigMap
metadata:
  creationTimestamp: "2019-09-16T02:19:25Z"
  name: kubevirt-config
  namespace: kubevirt
  resourceVersion: "759389"
  selfLink: /api/v1/namespaces/kubevirt/configmaps/kubevirt-config
  uid: f504ca79-d38d-496e-b66f-67e7dc01af15
[root@kube-master01 kubevirt]#

マイグレーションできるVMの条件

上記の通りLiveMigrationを有効化したので、改めてvirtctl migrateコマンドを実行してみます。すると、別のエラーメッセージが出力されます。

[root@kube-master01 kubevirt]# virtctl migrate testvm
Error migrating VirtualMachine an error on the server ("admission webhook \"migration-create-validator.kubevirt.io\" denied the request: Cannot migrate VMI, Reason: InterfaceNotLiveMigratable, Message: cannot migrate VMI with a bridge interface connected to a pod network") has prevented the request from succeeding
[root@kube-master01 kubevirt]#

上記メッセージより、Podネットワークと接続されたbridgeインターフェイスに紐づけられたVMマイグレーションできないことがわかります。

※また、VirtualMachineInstanceのみで動いている場合も、マイグレーションは実行できず、以下のようなメッセージが出力されます。

[root@kube-master01 kubevirt]# virtctl migrate help
Error migrating VirtualMachine the server could not find the requested resource
[root@kube-master01 kubevirt]#

ここでは以下のようなyamlファイルを作成し、VirtualMachineリソースをデプロイします。ポイントはinterfacesの部分で、何も指定しない場合はデフォルトでbridgeインターフェイスが利用されます。

※下記リソースはあくまでvirtctl migrateコマンドでマイグレーションをするためだけに作ったものであり、VMへのログインはできません。

apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachine
metadata:
  name: testvm-nocloud
spec:
  running: false
  template:
    metadata:
      labels:
        kubevirt.io/domain: testvm-nocloud
    spec:
      terminationGracePeriodSeconds: 30
      domain:
        resources:
          requests:
            memory: 1024M
        devices:
          disks:
          - name: containerdisk
            disk:
              bus: virtio
          - name: emptydisk
            disk:
              bus: virtio
          - disk:
              bus: virtio
            name: cloudinitdisk
          interfaces:        # ここからInterfaceの設定
          - name: default
            model: e1000
            masquerade: {}
            ports:
              - name: http
                port: 80
      networks:
      - name: default
        pod: {}
      volumes:
      - name: containerdisk
        containerDisk:
          image: kubevirt/fedora-cloud-container-disk-demo:latest
      - name: emptydisk
        emptyDisk:
          capacity: "2Gi"
      - name: cloudinitdisk
        cloudInitNoCloud:
          userData: |-
            #cloud-config
            password: fedora
            chpasswd: { expire: False }

KubevirtでデプロイするVMのInterfaceにはFrontendBackendの2種類があり、ここではFrontend側を明示的にmasqueradeを指定しています。

  • Backend

    • pod:デフォルトのKubernetesネットワーク
    • multus:Multusを利用した場合のネットワーク
    • genie:Genieを利用した場合のネットワーク
  • Frontend

    • bridge:Linux bridgeを利用して接続
    • slirp:QEMUを利用して接続
    • sriov:SR-IOV PCIバイスを利用
    • masquerade:Iptablesルールを利用して接続

なお、Interfaceについてはこちらに記載されています

上記yamlファイルをデプロイし、virtctl migrateコマンドも実行してみます。ここでVirtualMachineInstanceの存在するノードがkube-worker02であることに注目しておきます。

# VirtualMachineデプロイ
[root@kube-master01 kubevirt]# kubectl apply -f testvm-nocloud.yaml
virtualmachine.kubevirt.io/testvm-nocloud created
[root@kube-master01 kubevirt]# kubectl get vm
NAME             AGE   RUNNING   VOLUME
testvm-nocloud   4s    false
[root@kube-master01 kubevirt]#


# VirtualMachineの起動
[root@kube-master01 kubevirt]# virtctl start testvm-nocloud
VM testvm-nocloud was scheduled to start
[root@kube-master01 kubevirt]# kubectl get vmi
NAME             AGE   PHASE     IP                NODENAME
testvm-nocloud   22s   Running   192.168.247.225   kube-worker02
[root@kube-master01 kubevirt]# 


# virtctl migrate
[root@kube-master01 kubevirt]# virtctl migrate testvm-nocloud
VM testvm-nocloud was scheduled to migrate
[root@kube-master01 kubevirt]# 

virtctl migrateコマンドを実行すると、VirtualMachineInstanceMigrationリソースが作成されます。

[root@kube-master01 kubevirt]# kubectl get vmim
NAME                        AGE
kubevirt-migrate-vm-z74pf   42s
[root@kube-master01 kubevirt]# kubectl get vmim kubevirt-migrate-vm-z74pf -o yaml
apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachineInstanceMigration
metadata:
  annotations:
    kubevirt.io/latest-observed-api-version: v1alpha3
    kubevirt.io/storage-observed-api-version: v1alpha3
  creationTimestamp: "2019-09-16T08:42:12Z"
  generateName: kubevirt-migrate-vm-
  generation: 8
  name: kubevirt-migrate-vm-z74pf
  namespace: default
  resourceVersion: "811626"
  selfLink: /apis/kubevirt.io/v1alpha3/namespaces/default/virtualmachineinstancemigrations/kubevirt-migrate-vm-z74pf
  uid: 357ae1ad-afa3-4566-9672-cb83eb1b45d2
spec:
  vmiName: testvm-nocloud
status:
  phase: Succeeded
[root@kube-master01 kubevirt]#

上記出力結果のうちstatus.phaseSucceededになっていればマイグレーションは完了しています。もう一度VirtualMachineInstanceを確認してみると、ノードが変化しkube-worker01となっているのがわかります。

[root@kube-master01 kubevirt]# kubectl get vmi
NAME             AGE     PHASE     IP                NODENAME
testvm-nocloud   2m35s   Running   192.168.247.225   kube-worker01
[root@kube-master01 kubevirt]# 

またマイグレーション実行後にVirtualMachineInstanceyaml形式で出力すると、status.migrationStateが追加され、マイグレーションに関する情報が確認できます。

kubectl get vmi testvm-nocloud -o yaml

apiVersion: v1
items:
- apiVersion: kubevirt.io/v1alpha3
  kind: VirtualMachineInstance
  metadata:
    annotations:
      kubevirt.io/latest-observed-api-version: v1alpha3
      kubevirt.io/storage-observed-api-version: v1alpha3
    creationTimestamp: "2019-09-16T08:41:14Z"
    finalizers:
    - foregroundDeleteVirtualMachine
    generateName: testvm-nocloud
    generation: 14
    labels:
      kubevirt.io/domain: testvm-nocloud
      kubevirt.io/migrationTargetNodeName: kube-worker01
      kubevirt.io/nodeName: kube-worker01
    name: testvm-nocloud
    namespace: default
    ownerReferences:
    - apiVersion: kubevirt.io/v1alpha3
      blockOwnerDeletion: true
      controller: true
      kind: VirtualMachine
      name: testvm-nocloud
      uid: 8e6456b2-680f-4a32-bdfe-ae854c00d303
    resourceVersion: "811625"
    selfLink: /apis/kubevirt.io/v1alpha3/namespaces/default/virtualmachineinstances/testvm-nocloud
    uid: aaa2d6eb-0792-43e8-a60e-90cb6611154e
  spec:
    domain:
      devices:
        disks:
        - disk:
            bus: virtio
          name: containerdisk
        - disk:
            bus: virtio
          name: emptydisk
        - disk:
            bus: virtio
          name: cloudinitdisk
        interfaces:
        - masquerade: {}
          model: e1000
          name: default
          ports:
          - name: http
            port: 80
      features:
        acpi:
          enabled: true
      firmware:
        uuid: 2ce78280-4328-516d-b2db-3fac09b758d4
      machine:
        type: q35
      resources:
        requests:
          cpu: 100m
          memory: 1024M
    networks:
    - name: default
      pod: {}
    terminationGracePeriodSeconds: 30
    volumes:
    - containerDisk:
        image: kubevirt/fedora-cloud-container-disk-demo:latest
        imagePullPolicy: Always
      name: containerdisk
    - emptyDisk:
        capacity: 2Gi
      name: emptydisk
    - cloudInitNoCloud:
        userData: |-
          #cloud-config
          password: fedora
          chpasswd: { expire: False }
      name: cloudinitdisk
  status:
    conditions:
    - lastProbeTime: null
      lastTransitionTime: null
      status: "True"
      type: LiveMigratable
    - lastProbeTime: null
      lastTransitionTime: "2019-09-16T08:42:53Z"
      status: "True"
      type: Ready
    interfaces:
    - ipAddress: 192.168.247.225
      mac: 02:00:00:4c:c8:23
      name: default
    migrationMethod: BlockMigration
    migrationState:
      completed: true
      endTimestamp: "2019-09-16T08:42:59Z"
      migrationUid: 357ae1ad-afa3-4566-9672-cb83eb1b45d2
      sourceNode: kube-worker02
      startTimestamp: "2019-09-16T08:42:53Z"
      targetDirectMigrationNodePorts:
        "34438": 49152
        "35614": 0
        "38218": 49153
      targetNode: kube-worker01
      targetNodeAddress: 192.168.132.22
      targetNodeDomainDetected: true
      targetPod: virt-launcher-testvm-nocloud-887f9
    nodeName: kube-worker01
    phase: Running
    qosClass: Burstable
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

なお、マイグレーションに関する設定を変更するにはkubevirt-configを変更すればよいようです。

apiVersion: v1
kind: ConfigMap
metadata:
  name: kubevirt-config
  namespace: kubevirt
  labels:
    kubevirt.io: ""
data:
  feature-gates: "LiveMigration"
  migrations: |-
    parallelMigrationsPerCluster: 5
    parallelOutboundMigrationsPerNode: 2
    bandwidthPerMigration: 64Mi
    completionTimeoutPerGiB: 800
    progressTimeout: 150

最後に

今回はKubevirt v0.21.0について、少しだけ紹介しました。ここで紹介しただけでなく、CDI (Containerized Data Importer)を利用してVMイメージを管理したりもできます。

まだ検証が進められていないので何とも言えませんが、VMとコンテナを共通基盤・共通コマンドで管理することができる、というコンセプトは、やはり一定の需要があると思われます。本番環境で利用するにはまだ不安定な部分があるようですが、今後開発が進めば本番環境での利用もあり得るのでは、と考えています。今後もKubevirtについてはウォッチしていこうと思います。

参考ドキュメント

Kubernetes - Getting to Know Kubevirt

Kubevirt公式ページ

Kubevirt Githubページ

Kubevirt v0.21.0 release page

Easy install using minikube

KubeVirtを使ってみる

OpenStackもいらなくなる?kubevirt がどんなものか触って見た。