在《kubevirt(一)虚拟化技术》一文中,我们对libevirt
+qemu
+kvm
虚拟化做了一些简单的说明,本文基于这些内容,来看看kubevirt是怎么结合kubernetes平台实现虚拟化的。
kubevirt项目地址:https://github.com/kubevirt/kubevirt,在介绍kubevirt之前,我们先对kubernetes的informer
和CRD
两个概念再做一个简单的说明。
informer
informer简单来说是客户端通过list+watch机制,当关注的资源对象有变化时能得到ADD、UPDATE、DELETE三种事件,并且可以注册这三种事件的处理函数。这里说的资源对象可以是kubernetes原生支持的,例如namespace、deployment、node、pod等资源,也可以是用户自定义资源(CRD)。informer完整原理与实现可参考下图:
CRD
CRD的全称是Custom Resource Definition,即用户自定义资源,它是kubernetes可扩展的重要基础,CRD通常和webhook、informer(controller)一起使用来实现用户自定义逻辑。CRD可以理解为一种抽象,例如kubevirt把虚拟机抽象成一个vmi对象,创建一个vmi对象,就会触发informer中的ADD事件处理函数去创建一台真正的虚拟机。
kubevirt
以下内容基于[email protected]
kubevirt是基于CRD、controller等kubernetes特性,与容器并行地表示和管理传统虚拟机,kubevirt核心CRD VMI(Virtual Machine Instance)对应的就是虚拟机实例。
架构
在kubevirt代码仓库的文档中,我们能看到如下信息,从该图示可以看出,kubevirt运行在kubernetes之上,且调度(scheduling)也是由kubernetes负责
,底层则是在物理资源的基础上,利用虚拟化技术运行不同的操作系统(operating System)。
+---------------------+
| KubeVirt |
~~+---------------------+~~
| Orchestration (K8s) |
+---------------------+
| Scheduling (K8s) |
+---------------------+
| Container Runtime |
~~+---------------------+~~
| Operating System |
+---------------------+
| (Virtual) |
~~+---------------------+~~
| Physical |
+---------------------+
虚拟机与普通pod container
同一个kubernetes平台可以同时运行kubevirt虚拟机和普通pod container,它们有如下关系:
VMI
一个VMI对象对应一台虚拟机,实际上这台虚拟机是在一个特殊的kubernetes pod中,该pod里有virt-launcher、libvirtd和qemu进程,其中libvirtd和qemu是上篇文章提到的用于虚拟化的组件,因此也就是可以看作kubevirt把虚拟机运行在了pod中
。
kubevirt组件
kubevirt组件包括virt-controller
、virt-api
、virt-handler
、virt-launcher
等,整体关系如下:
其中各个组件大致功能如下:
virt-controller
基于informer的list-watch机制,监听VM、VMI等资源的事件,转换成VMI对应的pod,从而达到管理虚拟机的生命周期的功能。
virt-api
virt-api以deployment的形式运行,逻辑上主要包含以下功能:
1、提供VM、VMI等CRD资源mutating+validating webhook接口;
2、通过kubernetes的aggregator
特性,对外暴露虚拟机操作的相关接口,包括restart、migrate、start、stop、freeze/unfreeze、softReboot、pause/unpause、console、vnc等操作。
virt-handler
:
virt-handler以deamonSet的形式运行在每个节点上(配置hostNetwork+hostPID),逻辑上包含以下功能:
1、提供rest接口供virt-api
调用,接口功能包括console、vnc、pause/unpause、freeze/unfreeze等功能,这些接口最终操作本节点上对应的VMI对应的虚拟机。virt-api通过VMI的status.nodeName+约定好的端口找到VMI对应节点上的virt-handler https服务
,这也是为什么virt-handler要配置hostNetwork网络;
2、通过list-watch机制确保相应的libvirt domain的启动或停止,但是这个过程并不是virt-handler与对应的libvirtd直接交互,而是virt-handler通过unix sock文件访问virt-launcher,virt-launcher再与libvirtd交互。virt-handler与virt-launcher交互的sock文件为/var/run/kubevirt/sockets/{vmi_uid}_sock。
virt-launcher
:
每一个VMI对应的pod中都运行着一个virt-launcher进程,该进程逻辑上提供如下功能:
1、以unix sock形式启动一个grpc server,该server负责提供接口供virt-hanlder调用;
2、管理VMI对应的pod内的libvirtd、qemu进程的生命周期,例如qemu进程异常后会自动重启。
流程分析
我们选取两个场景,加深对kubevirt各个组件的理解。
场景一、创建VMI
- 用户执行kubectl apply一个vmi对象的yaml;
- apiServer把vmi对象保存到etcd;
- virt-controller通过list-watch机制监听到vmi对象的创建事件;
virt-controller根据vmi信息构造一个pod对象
,并调apiServer接口进行创建;- apiServer把该pod对象存入etcd;
- k8s scheduler通过list-watch机制监听到有pod创建;
- k8s scheduler发现pod.spec.nodeName为空,根据调度算法选择一个合适的节点给该pod,把节点名称写到pod.spec.nodeName字段,并调apiServer接口更新pod信息;
- apiServer更新etcd中pod对象信息;
- 上述节点上的kubelet通过list-watch机制发现有新的pod调度到本节点上;
- kubelet在本节点上创建一个pod,该pod中启动virt-launcher、libvirtd、qemu三个进程,此时一个kubevirt虚拟机就创建出来了。
场景二、vnc连接虚拟机
- 用户执行virtctl vnc命令;
- 请求到达apiServer后通过aggregator机制转发到virt-api;
- virt-api根据vmi中的节点信息找到该节点的ip,并通过约定好的端口请求对应节点上的virt-handler;
- virt-handler通过unix sock文件找到虚拟机对应的virt-launcher,发起vnc请求;
- virt-launcher调libvirtd的vnc接口。
总结
通过本文的介绍,我们简单地了解了kubevirt原理以及核心CRD vmi,其实kubevirt还有很多CRD来处理多副本、快照、迁移等场景,这些内容有待读者自行探索。
如果用一句话来概述kubevirt,那就是在pod中跑虚拟机
,github上还有一个开源的容器项目:kata container
,与kubevirt相似的是,底层也用到了qemu虚拟化技术,但是kata container是在虚拟机中跑pod
,正如kubevirt FAQ中对两者区别的描述:
Kata containers allow you to run containers inside virtual machines.
KubeVirt allows you to run full virtual machines on Kubernetes alongside regular containers.
One could say they are opposites:
Kata containers are containers inside virtual machines.
KubeVirt is a virtual machine inside a container.
后续会有文章分析docker container、kubevirt和kata container的差异,敬请期待。
微信公众号卡巴斯同步发布,欢迎大家关注。