本文发自 http://www.binss.me/blog/what-is-kata-containers/,转载请注明出处。

自从跟着老板开坑后就闷头搞 QEMU 和 KVM ,好久没碰 Docker 。直到最近在一个 talk 上听说了 Kata Containers ,瞬间来了精神 。于是搜索了一番资料,获得了一些新的资讯。

Kata Containers 是什么

基于轻量级虚拟机的容器,不同容器跑在一个个不同的虚拟机(kernel)上,比起传统容器提供了更好的隔离性和安全性。同时继承了容器快速启动和快速部署等优点。

轻量级虚拟机,就是号称我比你快安全性还比你更屌的那些,此类的 paper 比比皆是,比如 SOSP'17 的 My VM is Lighter (and Safer) than your Container 。但一直不成气候的原因,我个人觉得就是没有对接工业界的标准,比如说 OCI 。像 Docker 最成功的地方在于构建了一套快速部署的容器生态,比如说你需要跑一个 nginx 容器,那么只需 docker pull nginx 即可把 image 从仓库拉到本地,然后 run 一下就跑起来了。

如果我们能够利用起 Docker 构建的这套 “上层建筑”,再将其底层替换成我们自己的实现,岂不美哉?

根据 OCI 的 specification,虚拟机 runtime 阵营主要有 hyperhq/runvclearcontainers/runtime

runV 看名字就是和 docker 的 runC 对着干的。通过对接 Docker/Containerd 的接口,使其可以作为 docker 的 runtime 运行容器。

而 clearcontainers 是 Intel 搞出的一套 runtime。同样可以作为 docker 的 runtime 运行容器。

Kata Containers 是这两个项目合并,代表了虚拟机容器阵营的前进方向:

所以 Kata Containers 能够干什么呢?

关于该项目,官网 的内容也比较简单,网上也只有寥寥几篇新闻稿,通过以下几幅图我们可以先窥一斑:

区别于传统容器共享同一个 kernel ,Kata 容器每个容器都使用专有内核,更安全。

作为 runtime ,上接 docker / kubernetes / openstack 生态。

那么我如何才能使用 Kata Containers 呢?

还在开发中。根据 zhihu 的消息,比较稳定的版本,保守的话估计是今年(2018)H1,激进的话最早也要今年(2018)Q1,赶上 Ubuntu 18.04 。

相信看到这里的你和我一样索然无味,既然 Kata 还不能用,那么我们先来折腾下 runV 和 clearcontainers 。所以说本文其实是一篇安装配置文章。

runV

首先需要安装 docker ,这点就不多说了。

安装

首先下载、编译、安装 runV:

apt-get install autoconf automake pkg-config make gcc golang qemu libvirt-bin libvirt-dev
mkdir $GOPATH/src/github.com/hyperhq
cd $GOPATH/src/github.com/hyperhq
git clone https://github.com/hyperhq/runv/
cd runv
./autogen.sh
./configure --without-xen
make
sudo make install

注意除了当前用户需要配置 $GOPATH 外,root 也需要设置,否则在最后一步 sudo 时找不到 $GOPATH 会报错安装不上。

然后需要安装 hyperstart,来为运行容器的 Guest OS 提供 kernel 和 initrd ,这个 kernel 是定制过的,可能后续会做成 Unikernel 。

git clone https://github.com/hyperhq/hyperstart.git
cd hyperstart
./autogen.sh
./configure
make
sudo mkdir /var/lib/hyper/
sudo cp build/hyper-initrd.img build/arch/x86_64/kernel /var/lib/hyper

会将生成的 hyper-initrd.img 、kernel 拷贝到 /var/lib/hyper 目录下。

配置

修改 docker 默认的 runtime 为 runv ,即往 /etc/docker/daemon.json 写入:

{
    "default-runtime": "runv",
    "runtimes": {
        "runv": {
            "path": "runv"
        }
    }
}

然后重启 docker :

sudo systemctl restart docker

然后就可以通过 docker 来跑虚拟机容器了:

docker run --rm -it busybox

观察

我们查看运行的进程:

root      25711  0.0  0.0 486732 20488 ?        Ssl  20:21   0:00 runv --root /run/runv --log_dir /var/log/hyper proxy --vmid vm-JUmNdtgAMR --hyperstart-ctl-sock unix:///var/run/hyper/vm-JUmNdtgAMR/hyper.sock --hyperstart-stream-sock unix:///var/run/hyper/vm-JUmNdtgAMR/tty.sock --proxy-hyperstart /var/run/hyper/vm-JUmNdtgAMR/hyperstartgrpc.sock
root      25731  0.0  0.0 409768 22288 ?        Ssl  20:21   0:00 runv --root /run/runv --log_dir /var/log/hyper watcher --watch-vm-console /var/run/hyper/vm-JUmNdtgAMR/console.sock --console-proto telnet --watch-hyperstart --watch-vm
root      25732  0.0  0.0 500660 24220 pts/19   Ssl+ 20:21   0:00 runv --root /run/runv --log_dir /var/log/hyper/shim-1f2a463701d94e31b22b929c5136ae24c24c0081f4012d886ca42a59f4bc2410 shim --container 1f2a463701d94e31b22b929c5136ae24c24c0081f4012d886ca42a59f4bc2410 --process init --proxy-stdio --proxy-exit-code --proxy-signal --proxy-winsize
root     181072  0.0  0.0 412744 20768 ?        Ssl  Dec24   0:01 runv --root /run/runv --log_dir /var/log/hyper proxy --vmid vm-HhvgYYBcQT --hyperstart-ctl-sock unix:///var/run/hyper/vm-HhvgYYBcQT/hyper.sock --hyperstart-stream-sock unix:///var/run/hyper/vm-HhvgYYBcQT/tty.sock --proxy-hyperstart /var/run/hyper/vm-HhvgYYBcQT/hyperstartgrpc.sock

...

root      25722  0.1  0.1 677436 125788 ?       Sl   20:21   0:02 /usr/bin/qemu-system-x86_64 -machine pc-i440fx-2.1,accel=kvm,usb=off -global kvm-pit.lost_tick_policy=discard -cpu host -kernel /var/lib/hyper/kernel -initrd /var/lib/hyper/hyper-initrd.img -append console=ttyS0 panic=1 no_timer_check iommu=off -realtime mlock=off -no-user-config -nodefaults -no-hpet -rtc base=utc,clock=vm,driftfix=slew -no-reboot -display none -boot strict=on -m size=128,slots=1,maxmem=32768M -smp cpus=1,maxcpus=8 -numa node,nodeid=0,cpus=0-7,mem=128 -qmp unix:/var/run/hyper/vm-JUmNdtgAMR/qmp.sock,server,nowait -serial unix:/var/run/hyper/vm-JUmNdtgAMR/console.sock,server,nowait -device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x2 -device virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3 -chardev socket,id=charch0,path=/var/run/hyper/vm-JUmNdtgAMR/hyper.sock,server,nowait -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charch0,id=channel0,name=sh.hyper.channel.0 -chardev socket,id=charch1,path=/var/run/hyper/vm-JUmNdtgAMR/tty.sock,server,nowait -device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charch1,id=channel1,name=sh.hyper.channel.1 -fsdev local,id=virtio9p,path=/var/run/hyper/vm-JUmNdtgAMR/share_dir,security_model=none -device virtio-9p-pci,fsdev=virtio9p,mount_tag=share_dir -daemonize -pidfile /var/run/hyper/vm-JUmNdtgAMR/pidfile -D /var/log/hyper/qemu/vm-JUmNdtgAM.log

可见 runV 是作为一个中间件,底层跑的是 QEMU 和 KVM 。整体架构是:

docker - containerd - runv-shim - runv-proxy - QEMU - KVM

Intel Clear Container

安装

sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/clearcontainers:/clear-containers-3/xUbuntu_$(lsb_release -rs)/ /' >> /etc/apt/sources.list.d/clear-containers.list"
wget -qO - http://download.opensuse.org/repositories/home:/clearcontainers:/clear-containers-3/xUbuntu_$(lsb_release -rs)/Release.key | sudo apt-key add -
sudo -E apt-get update
sudo -E apt-get -y install cc-runtime cc-proxy cc-shim

配置

像 runV 一样,修改 docker 默认的 runtime 为 rcc-runtime ,即往 /etc/docker/daemon.json 写入:

{
    "default-runtime": "cc-runtime",
    "runtimes": {
        "cc-runtime": {
            "path": "/usr/bin/cc-runtime"
        }
    }
}

然后重启 docker :

sudo systemctl restart docker

启动 cc-proxy:

sudo systemctl enable cc-proxy.socket
sudo systemctl start cc-proxy.socket

然后就可以通过 docker 来跑虚拟机容器了:

docker run --rm -it busybox

观察

感觉 cc-runtime 明显比 runV 快。

我们查看运行的进程:

root     140668  0.0  0.0 413956  3904 ?        Sl   20:02   0:00 docker-containerd-shim 5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a /var/run/docker/libcontainerd/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a /usr/bin/cc-runtime
root     140785  0.0  0.0 187760  7252 ?        Sl   20:02   0:00 /usr/libexec/clear-containers/cc-proxy -uri unix:///run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/proxy.sock
root     140792  0.0  0.0   4368   664 pts/21   Ss+  20:02   0:00 /usr/libexec/clear-containers/cc-shim -c 5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a -t CxUmdrsTsov3PDMh0BWhRZlnV21mFB3ZboUSsjV9y8E= -u unix:///run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/proxy.sock
root     140793  0.0  0.0   4368   120 pts/21   S+   20:02   0:00 /usr/libexec/clear-containers/cc-shim -c 5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a -t CxUmdrsTsov3PDMh0BWhRZlnV21mFB3ZboUSsjV9y8E= -u unix:///run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/proxy.sock

root     140726  3.0  0.2 5935252 146892 ?      Sl   20:02   0:03 /usr/bin/qemu-lite-system-x86_64 -name pod-5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a -uuid a9ea1387-5e7c-4c7f-97ca-7288cde30a5a -machine pc,accel=kvm,kernel_irqchip,nvdimm -cpu host -qmp unix:/run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/a9ea1387-5e7c-4c7,server,nowait -qmp unix:/run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/a9ea1387-5e7c-4c7,server,nowait -m 2048M,slots=2,maxmem=65206M -device virtio-serial-pci,id=serial0 -device virtconsole,chardev=charconsole0,id=console0 -chardev socket,id=charconsole0,path=/run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/console.sock,server,nowait -device nvdimm,id=nv0,memdev=mem0 -object memory-backend-file,id=mem0,mem-path=/usr/share/clear-containers/clear-19790-containers.img,size=235929600 -device pci-bridge,bus=pci.0,id=pci-bridge-0,chassis_nr=1,shpc=on -device virtserialport,chardev=charch0,id=channel0,name=sh.hyper.channel.0 -chardev socket,id=charch0,path=/run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/hyper.sock,server,nowait -device virtserialport,chardev=charch1,id=channel1,name=sh.hyper.channel.1 -chardev socket,id=charch1,path=/run/virtcontainers/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a/tty.sock,server,nowait -device virtio-9p-pci,fsdev=extra-9p-hyperShared,mount_tag=hyperShared -fsdev local,id=extra-9p-hyperShared,path=/run/hyper/shared/pods/5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a,security_model=none -netdev tap,id=network-0,vhost=on,vhostfds=3:4:5:6:7:8:9:10,fds=11:12:13:14:15:16:17:18 -device driver=virtio-net-pci,netdev=network-0,mac=02:42:ac:11:00:02,mq=on,vectors=18 -rtc base=utc,driftfix=slew -global kvm-pit.lost_tick_policy=discard -vga none -no-user-config -nodefaults -nographic -daemonize -kernel /usr/share/clear-containers/vmlinuz-4.9.60-82.container -append root=/dev/pmem0p1 rootflags=dax,data=ordered,errors=remount-ro rw rootfstype=ext4 tsc=reliable no_timer_check rcupdate.rcu_expedited=1 i8042.direct=1 i8042.dumbkbd=1 i8042.nopnp=1 i8042.noaux=1 noreplace-smp reboot=k panic=1 console=hvc0 console=hvc1 initcall_debug iommu=off cryptomgr.notests net.ifnames=0 quiet systemd.show_status=false init=/usr/lib/systemd/systemd systemd.unit=clear-containers.target systemd.mask=systemd-networkd.service systemd.mask=systemd-networkd.socket ip=::::::5359409043a5fd13d9898a37af0422907c811d24661b2c4cc4ae6ff6db1aea8a::off:: -smp 48,cores=48,threads=1,sockets=1

嗯? qemu-lite-system-x86_64 是什么东西?Google 了一下,发现是 Intel 自己魔改的一个轻量级 QEMU 。

这时想起了 Intel Clear Container 吹的:

Clear Containers boot time and memory footprint are significantly optimized by using a specific QEMU version called qemu-lite

虽然它的默认 machine type 为 pc ,但可以指定为 pc-lite:

In the past pc-lite was utilized which provided the following improvements: Removed many of the legacy hardware devices support so that the guest kernel does not waste time initializing devices of no use for containers. Skipped the guest BIOS/firmware and jumped straight to the Clear Containers kernel.

整体架构是:

                                                          (connect)
docker - containerd - containerd-shim - cc-shim - cc-proxy   -
                                                          (create)
                                                  cc-runtime - QEMU-lite - KVM - VM(cc-agent)

2019.02.11 更新

看了 18 年 11 月份 Hyper.sh 在 OpenStack Summit 上的演讲,对该项目有了更多的了解,在此更新一发。

相比 gVisor,Kata 本质上还是跑 KVM 虚拟机,因此能够利用 QEMU-KVM 的诸多优化如 IO 能 Passthrough 或用 Vhost 系列等,IO 性能更好:

总结

runV 和 clear container 启动的 Guest 会暴露出 socket ,供 docker ,更准确来说是 runtime 进行管理。

它们都使用了自己的 kernel 和 initrd ,目的是加快启动速度。同时,都使用了 virtio 来提高 IO 设备的效率。

在模拟机器的类型选择上,runV 用的是 pc-i440fx ,而 clear container 默认为 pc,目前两者对 Q35 的支持还不完善。

Kata Containers 合并了 Intel Clear Containers 和 Hyper runV 两个项目,强强联手,代表了虚拟机容器阵营的前进方向。

希望在未来能够解决这两个项目当前的 Limitations ,期待有一天能达到或者接近容器的性能,又快又方便又安全,岂不美哉!