写点什么

智汇华云 | kata container virtiofs 测试和技术分析

用户头像
华云数据
关注
发布于: 40 分钟前


云原生技术已经成为加快企业数字化转型的一个不折不扣的风向标。而以容器为代表的云原生技术正在成为释放云价值的最短路径。本期智汇华云,带大家一起了解和体验安全容器 kata container 的⼀些特性。

前⾔

当前云原⽣技术发展的如⽕如荼,容器化技术以及 kubernetes 容器化应⽤管理平台,带来了全新的⽣ 态系统. 随着容器技术的发展,容器运⾏时规范 OCI 也逐渐脱离 Docker 被单独提了出来,kubernetes⻓期使⽤的容器运⾏时是 runC,轻量,性能⾼,但因为共⽤linux 内核以及 namespace 机制隔离的不彻 底,存在⼀定的安全问题,业界涌现了好⼏个安全容器的解决⽅案,⽐如 Google 的 gVisor, Amazon 的 Firecracker,vmware 的 CRX 等, 开源世界⾥⾯⽐较有名的解决⽅案是 kata container,本⽂主要就关 注在安全容器 kata container 的⼀些特性了解和体验。 kata Container 

kata container 由⼀系列技术组成,来源于两个项⽬的合并,Intel Clear Containers 和 Hyper runV,然 后⼜加上 redhat 贡献的 virtio-fs,⼀种在 guest 和宿主机之间共享⽂件系统的⽅案。涉及的技术有 QEMU/KVM,Linux kernel,⽂件系统,容器运⾏时,容器⽹络等,是⼀项⽐较复杂的组合产品,⽽ 且还保持着很⾼的新特性开发进度,本次主要体验和梳理 virtiofs 相关的内容,这些内容有部分是来源 于 kata 的社区邮件列表以及 slack,还有微信群,⾮常感慨,kata 的社区真是⾮常的 Nice,专业程度和 热⼼都令⼈感动。

virtiofs ⽂件系统结构 

默认容器是使⽤cgroup 和 namespace 做进程,⽹络,⽂件系统挂载点等隔离,是⾮常轻量级的。⽽ kata container 为了实现安全容器,使⽤了 VM 虚拟机作为强隔离⼿段,有独⽴的内核和虚拟机镜像, 为了降低资源消耗,hypervisor 使⽤了⾮常⼩的 guest kernel 和 guest image,⾼度优化了内核的启动 时间,最⼩化了内存占⽤,只提供容器负载所需要的最基本的服务。virtio-fs⽂件系统,在资源消耗上 也使⽤了多种优化特性。 下⾯就来看⼀看具体是怎么使⽤的,以及特性理解 kata container 作为除了 runC 之外另⼀种 runtime,需要先在 kubernetes 环境中做集成部署,以便做各 种观察。当然直接使⽤他的 CTR⼯具也是可以,只是缺少 CNI 的⽀持,⽹络⽅⾯不能⾃动配置。kata container configuration.toml 配置参数有两种共享⽂件系统类型可以选择,之前默认是 virtio-9p ,现 在基本上都会选择 virtio-fs,有若⼲优点。 virtio- 9p 和 virtio-fs ⽂件系统对⽐ 

1. virtio-9p 基于现存的⽹络协议,并没有虚拟化场景提供优化 

2. virtio-fs 利⽤了 hypervisor 和虚拟机处于相同节点的优势 

DAX 特性,⽂件内容能够映射到宿主机的内存窗⼝,允许客户机直接访问宿主机的 page cache

减少内存占⽤,因为客户机 cache 已经被绕过了 

不需要⽹络节点通信,提⾼了 IO 性能


测试

kata container 与整合使⽤virtiofsd,把宿主机⽬录共享给微虚拟机使⽤。测试环境版本: Centos 8 

qemu-kvm 5.1 

kubernetes 1.18 

containerd 1.4.4 

kata container 2.0.4 

使⽤kubernetes 新版本的 RuntimeClass 对象,指定 handler:kata 

创建 pod 

1 apiVersion: v1 

2 kind: Pod 

3 metadata: 

4 name: kata-alpine 

5 spec: 

6 runtimeClassName: kataclass 

7 containers: 

8 - name: alpine 

9 image: alpine:latest 

10 imagePullPolicy: IfNotPresent 

11 command: 

12 - /bin/sh 

13 - "-c" 

14 - "sleep 60m" 

15 restartPolicy: Always 

16 nodeSelector: 

17 kubernetes.io/hostname: 

k8s05 k8s05 节点宿主机进程查看 

1 [root@k8s05 ~]# ps aux|grep kata 

2 root 500086 0.0 0.1 1412184 47796 ? Sl Jun18 14:00 /usr/bin/conta 

3 root 500117 0.0 0.0 129064 4960 ? Sl Jun18 0:00 /usr/libexec/k 

4 root 500155 0.2 3.2 4367516 1059672 ? Sl Jun18 41:47 /usr/bin/qemu- 

5 root 500158 0.0 0.8 5667064 271696 ? Sl Jun18 0:03 /usr/libexec/k

可以看到 kata container v2 启动了新的与 kubernetes 对接的 CRI 进程 containerd-shim-kata-v2, KVM 虚拟机,还有 2 个 virtiofsd 进程。为什么会启动 2 个 virtiofs 呢?为了提⾼安全性,virtiofsd 进程 fork⾃⼰,以便进⼊新的 mount/pid/net 命名空间,只有⼦进程内存映射,CPU 使⽤⽅⾯处于活跃状 态。

可以看到 qemu 虚拟化了设备 vhost-user-fs-pci,有⼀个 tag 为 kataShared,这个就是虚拟机要 mount 的源。tag 就是⼀个⾃定义⽂件系统需要接收的参数,定义路径⽤的。 

Note that Linux 4.19-based virtio-fs kernels required a different mount syntax. mount -t virtio_fs none /mnt -o tag=myfs,rootmode=040000,user_id=0,group_id=0 instead. mount_tag: A tag which acts as a hint to the guest OS and is used to mount this exported path. 

1 -device vhost-user-fs-pci,chardev=char-538bb1c14588b18e,tag=kataShared 

进⼊kata 容器观察 

1 [root@k8s01 kata-container]# kubectl exec -it kata-alpine -- sh 

2 / # df -h  

3 Filesystem Size Used Available Use% Mounted on 

4 kataShared 74.0G 49.5G 24.4G 67% / 

5 tmpfs 64.0M 0 64.0M 0% /dev 

6 tmpfs 1.4G 0 1.4G 0% /sys/fs/cgroup 

7 kataShared 74.0G 49.5G 24.4G 67% /etc/hosts 

8 kataShared 74.0G 49.5G 24.4G 67% /dev/termination-log 

9 kataShared 74.0G 49.5G 24.4G 67% /etc/hostname 

10 kataShared 74.0G 49.5G 24.4G 67% /etc/resolv.conf 

11 shm 1.4G 0 1.4G 0% /dev/shm 

12 kataShared 15.7G 12.0K 15.7G 0% /run/secrets/kubernetes.i 

13 tmpfs 64.0M 0 64.0M 0% /proc/keys 

14 tmpfs 64.0M 0 64.0M 0% /proc/timer_list

可以看到容器已经挂载了 tag 定义的⽂件系统。tag 名字可以是任意取的,只要能 guest 挂载时候指定相同的就可以。对于 kata 来说,已经整合好了,不需要⼿⼯指定

有时候如果处于调试⽬的或者是想看⼀下虚拟机的信息,可以配置 kata 开启 debug 模式,qemu 暴露 vsock 接⼝,虚拟机通过 agent 开启 shell 

1 configuration.toml 

2

3 [agent.kata] 

4 debug_console_enabled = true 

宿主机再⼿动启动 kata-monitor 进程,获取 sandbox 的 vsock 地址,⾃⼰监听 localhost:8090 端⼝,就可以通 过 kata-runtime exec $sandboxId 接⼊虚拟机 bash 了 

1 [root@k8s05 kata]# /usr/bin/kata-monitor  

2 INFO[0010] add sandbox to cache container=7e4cef94733381a9d9c509aa2a0be87e0c0bd 

3 INFO[0020] delete sandbox from cache container=5246e787b17eeab4ca83e9e73583a1b5

进⼊虚拟机查看 

kata 根据 sandbox 的 ID,指定唯⼀的宿主机内存共享⽬录 source, 传递给 virtiofsd. 虚拟机挂载 virtiofsd 导出的共享的⽬录到/run/kata-containers/shared/containers,然后再以 bind mount 的⽅ 式挂载进容器。 

1 [root@k8s05 ~]# kata-runtime exec 0965321e164975f01c85f997fbb0183773a9e97cb5767d9 

2 bash-4.2# df -h  

3 Filesystem Size Used Avail Use% Mounted on 

4 rootfs 1.4G 350M 1.1G 25% / 

5 dev 1.4G 0 1.4G 0% /dev 

6 tmpfs 1.5G 0 1.5G 0% /dev/shm 

7 tmpfs 1.5G 16K 1.5G 1% /run 

8 tmpfs 1.5G 0 1.5G 0% /sys/fs/cgroup 

9 kataShared 16G 1.6G 15G 11% /run/kata-containers/shared/containers 

10 shm 1.5G 0 1.5G 0% /run/kata-containers/sandbox/shm

kata 根据 sandbox 的 ID,指定唯⼀的宿主机内存共享⽬录 source, 传递给 virtiofsd. 虚拟机挂载 virtiofsd 导出的共享的⽬录到/run/kata-containers/shared/containers,然后再以 bind mount 的⽅ 式挂载进容器。

持久卷挂载 

⽹络持久卷⽐如 iscsi 也是能直接挂载进 kata 容器的,例如创建⼀个 pod,直接指定 pvc,pv 使⽤某⼀个 iscsi lun,然后在容器中创建⼀个⽂件 

1 [root@k8s01 kata-container]# kubectl exec -it iscsipd -- sh  

2 / # cd /mnt/iscsipd 

3 /mnt/iscsipd # ls 

4 lost+found 

5 /mnt/iscsipd # touch aaaa.txt 

在虚拟机⾥⾯已经通过 virtiofs 挂载在了⼀个容器 id⽬录之下 

1 [root@k8s05 ~]# kata-runtime exec e96d0ce2249e9027f0e1102e66a0c0013473ac48a088240 

2 bash-4.2# mount 

3 rootfs on / type rootfs (rw,size=1444588k,nr_inodes=361147) 

4 kataShared on /run/kata-containers/shared/containers type virtiofs (rw,relatime)

5 kataShared on /run/kata-containers/e96d0ce2249e9027f0e1102e66a0c0013473ac48a08824 

6 kataShared on /run/kata-containers/839162f25b7907bf91ecb027305e64dd5ccf36f55b15b6 

7 bash-4.2# find / -name aaaa.txt 

8 /run/kata-containers/shared/containers/839162f25b7907bf91ecb027305e64dd5ccf36f55b

在宿主机上是识别为硬盘块设备/dev/sdd,被 kubelet 挂载在特定⽬录下⾯,⼀个是 sandbox_id,⼀ 个是应⽤container_id

1 [root@k8s05 ~]# lsblk 

2 NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT 

3 sda 8:0 0 50G 0 disk  

4 ├─sda1 8:1 0 1G 0 part /boot 

5 sdc 8:32 0 2G 0 disk  

6 sdd 8:48 0 2G 0 disk /run/kata-containers/shared/sandboxes/e96d0ce2

就如前⽂说的,所有数据都要经过 virtiofs,不管是镜像数据还是⽹络存储卷。虚拟机要和宿主机数据 交互,就必须要穿过 qemu,virtiofs 就是穿过 qemu 的桥梁,提供共享⽂件机制。数据相关的操作最终 还是在宿主机上,⽐如镜像层的合并,仍然是 containerd 的存储层插件 snapshotter 完成,底层仍然是 调⽤了 overlayfs⽂件系统

1 /etc/containerd/config.toml

2 [plugins."io.containerd.grpc.v1.cri".containerd] 

3 snapshotter = "overlayfs" 

4 default_runtime_name = "runc" 

5 no_pivot = false 

6 disable_snapshot_annotations = true 

7 discard_unpacked_layers = false 

8 ... 

9 [plugins."io.containerd.service.v1.diff-service"] 10 default = ["walking"]

另外 kata container 整合使⽤virtiofs 的⽅式,与独⽴⼿⼯操作 virtiofs 的⽅式,有些地⽅稍有不同,⽐ 如按 virtiofs 官⽅说明,可以指定 host⽂件⽬录 $TESTDIR 作为源

1 ./virtiofsd --socket-path=/tmp/vhostqemu -o source=$TESTDIR -o cache=alwa

⽽在 kata runtime⾥⾯是不允许的,⽐如在 configuration.toml ⾥配置

1 virtio_fs_extra_args = ["-o","--thread-pool-size=1","-o","/opt/kata-instance"]

kubelet 会报错:

Warning FailedCreatePodSandBox 1s (x14 over 15s) kubelet, k8s05 Failed to create pod sandbox: rpc error: code = Unknown desc = failed to create containerd task: failed to launch qemu: exit status 1, error messages from qemu log: qemu-system- x86_64: cannot create PID fifile: Cannot open pid fifile: No such fifile or directory

kata 还会打开 vhost-user socket ⽂件描述符,传递给 virtiofsd。可能有⼈会有疑问,为什么每⼀个 virtiofsd 进程的 fd 参数都等于 3,会不会有冲突。其实不会,因为⽂件描述符是进程独⽴的,STDIO 占 据了 0,1 和 2,那么 virtiofsd 第⼀个可⽤的 fd num 就是 3 了

virtiofsd cache guest 

guest ⽣成是会指定内存⼤⼩,virtiofsd 会共享使⽤guest 的内存。默认使⽤memory-backend-fifile 内存对象 

virtiofsd 共享使⽤VM 内存,configuration.toml 配置参数 default_memory 

qemu 命令⾏接受的参数

1 -object memory-backend-file,id=dimm1,size=3072M,mem-path=/dev/shm,share=on -numa  

guest 和 host 数据传输都是通过 virtio-fs,包括容器镜像和容器卷,读写权限取决于 virtiofsd 进程的权限

DAX(直接访问)

dax

DAX windows 是⼀块虚拟内存区域,通过 PCI Bar 把⽂件映射到 guest⾥⾯,并不真正的占⽤主机那 么多内存,即使有 100 个虚拟机,设置的 DAX cache 是 1G,也不会真的使⽤100G 内存。只是⼀种 page 映射机制,所以也不需要任何的 cache 策略。

要任何的 cache 策

kata container 官⽅下载的版本默认没有不⽀持,需要编译安装 gitlab 托管的 virtio-fs qemu 项⽬ qemu5.0-virtiofs-dax 分⽀

configuration.toml 设置 virtio_fs_cache_size dax window ⼤⼩,qemu 初始化 vhost-user-fs-pci 设 备

1 -machine q35,accel=kvm,kernel_irqchip,nvdimm 

2 -device nvdimm,id=nv0,memdev=mem0 -object memory-backend-file,id=mem0,mem-path=/o

3 -device vhost-user-fs-pci,chardev=char-ea09ec33d071ac42,tag=kataShared,cache-size

如果是 rootfs image 可以看到模拟的 nvdimm 信息,⾃⼰编译镜像的话,image_builder.sh 构建脚本会 在 image⽂件头部写⼊dax metadata 信息。initrd image 看不到。image 相当于是直接⽤rootfs 引导, initramfs(initrd)是把 cpio+gz 打包压缩的 rootfs 解压到内存⾥,再⽤内存⾥的 initramfs 来引导。前 者可以利⽤dax,让 guest os 认为这就是个 dimm 内存,不需要再加载到另⼀块内存⾥,后者就是需要 解压到⼀块内存。 

DAX 背后的想法是避免在 guest 中使⽤ _second_ 缓冲区缓存。⼀般对于常规⽂件,guest 会维护⾃⼰ ⽂件系统缓冲区。 

现在呢,对于与 virtiofs 共享的⽂件,由于它们驻留在主机上,您将在主机上拥有⼀份缓存副本,然后 virtiofs 会将其发送给 guest ,然后 guest 就也有了⼀份副本,成本就⽐较⾼。 DAX 就是为了解决这个问题的,它所做的就是将主机 buffffer map 到客户机中,guest 使⽤与主机相同 的物理内存,即使它使⽤不同的虚拟地址。如果没有 DAX,内存使⽤量可能会⾮常⼤,因为每个 guest 都有⾃⼰的⽂件缓冲区。例如,如果您启动⼀个 Fedora 容器并“dnf install something”,您将 看到内存使⽤量增加了约 300M,因为在该操作期间读取/写⼊了许多⽂件。如果没有 DAX,那么 guest 也需要分配 350M。使⽤ DAX,它就会直接使⽤宿主机中已为这些⽂件缓冲分配的 350M 内存 

性能测试 

测测 DAX window⼤⼩对性能的影响(virtio_fs_cache_size = 512) 

⼩⽂件测试

1 fio --name=small-file-multi-read --directory=/usr/share/nginx/html \ 

2 --rw=randread --file_service_type=sequential \ 

3 --bs=4k --filesize=10M --nrfiles=100 \ 

4 --runtime=60 --time_based --numjobs=1 

5 ... 

6 small-file-multi-read: (groupid=0, jobs=1): err= 0: pid=190: Mon Jul 5 07:05:18 

7 read: IOPS=12.0k, BW=46.0MiB/s (49.2MB/s)(212MiB/4505msec)

⼤⽂件测试

1 fio --name=5G-bigfile-rand-read --directory=/usr/share/nginx/html --rw=ra 

2 ... 

3 5G-bigfile-rand-read: (groupid=0, jobs=1): err= 0: pid=184: Mon Jul 5 06:57:08 2 4 read: IOPS=1255, BW=5024KiB/s (5144kB/s)(294MiB/60002msec)

从压测观察到当读写的⽂件超过 dax window 后,性能下降很多,原因和 dax 映射机制有关,dax 窗⼝的 回收是⽐较慢的,当活跃数据超过 dax window ,产⽣很多的回收⼯作会导致性能很差。另外 dax window 的映射单元是按每 2MB 内存⼤⼩映射的,具体来说就是由 512 个 struct page (one struct page for each 4K range) 和⼀个 struct fuse_dax_mapping 。每个单元映射范围⼤约需要消耗 32K 额外内存。⽐如 1GB of dax window: 512 * 32K = 16MB of memory usage.

同时还观察到 virtiofsd 占⽤内存很⾼,⽐如本实例 qemu-system-x86_64 rss 内存 3.1G,virtiofsd rss 1.6G,⽣产中对资源的控制需要细节控制。

总结

kata container 还是⼀项⽐较复杂的技术组合,实践⽂档较少,有问题要不就是求教于社区,要不就 得翻源码,源码牵涉到 linux kernel,qemu/kvm,fifilesystem,内存映射,容器等多种技术,⽽且还是⽐ 较新的技术。但云原⽣安全⼜是未来越来越受到重视的领域,极⼤影响业务容器化的决策。希望⾏业 能更多的参与到安全容器上来,增加实践场景,畅快投⼊云原⽣的怀抱。

用户头像

华云数据

关注

还未添加个人签名 2020.11.02 加入

还未添加个人简介

评论

发布
暂无评论
智汇华云 | kata container virtiofs测试和技术分析