kata containers调研

极客时间-47

简介

安全问题的唯一正解在于允许那些(导致安全问题的)Bug发生,但通过额外的隔离层来阻拦住它们。

​ ——LinuxCon NA 2015, Linus Torvalds

不论是gVisor还是Kata Container, 其实现的本质,是给进程分配一个独立的操作系统内核,从而避免让容器共享宿主机的内核。这样,容器进程能看到的攻击面,就从整个宿主机内核变成了一个极小的、独立的、以容器为单位的内核。从而有效解决了容器进程发生“逃逸”或者夺取整个宿主机的控制权的问题。

image-20201214102615029

Kata Container和gVisor的区别在于,Kata Container使用的是传统的虚拟化技术,通过虚拟硬件模拟出了一台“小虚拟机”,然后再这个小虚拟机里安装了一个裁剪后的Linux内核来实现强隔离。而gVisor的做法更加激进,Google的工程师直接用Go语言“模拟”了一个运行在用户态的操作系统内核,然后通过这个模拟的内核来代替容器进程向宿主机发起有限的、可控的系统调用。

详细解读kata containers

Kata Containers的工作原理可以用如下所示的示意图来描述:

image-20201214103352421

Kata Containers的本质就是一个轻量化虚拟机。当启动一个Kata Containers之后,会看到一个正常的虚拟机在运行。其意味着,一个标准的虚拟机管理程序(Virtual Machine Manager, VMM)是运行Kata Containers必备的一个组件。在上图中,使用的VMM就是Qemu。

使用了虚拟机作为进程的隔离环境之后,Kata Containers原生就带有了Pod的概念。即:这个Kata Containers启动的虚拟机,就是一个Pod;而用户定义的容器,就是运行在这个轻量级虚拟机里的进程。在具体实现上,Kata Containers的虚拟机里会有一个特殊的Init进程负责管理虚拟机里面的用户容器,并只为这些容器开启Mount Namespace。所以这些用户容器之间,原生就是共享Network以及其他Namespace。

此外,为了跟上层编排框架比如Kubernetes进行对接,Kata Container项目会启动一系列跟用户容器对应的shim进程,来负责操作这些用户容器的生命周期。当然,这些操作,实际上还是靠虚拟机里的Init进程来完成。Kata Containers默认使用的VMM是Qemu。

gVisor设计原理

image-20201214111314334

gVisor工作的核心,在于它为应用进程、也就是用户容器,启动了一个名叫Sentry的进程。而Sentry进程的主要职责,就是提供一个传统的操作系统内核的能力,即:运行用户程序,执行系统调用。所以说,Sentry并不是使用Go语言重新实现一个完整的Linux内核,而只是一个对应用进程“冒充”内核的系统组件。

CNCF补充

基础术语

OCI(开放容器标准),规定了2点:

  • 容器镜像要长什么样,即ImageSpec,里面大致规定就是你这个东西需要的是一个压缩的文件夹,文件夹里以xxx结构放xxx文件;
  • 容器需要接收哪些指令,这些指令的行为是什么么,即RuntimeSpec。这里面的大致内容是是“容器”要能够执行"create", “start”, “stop”, "delete"命令,并且行为要规范。(OCI的runtime spec对于容器的状态描述,以及对于容器的创建、删除、查看等操作进行了定义)

runc,是对于OCI标准的一个参考实现,是一个可以用于创建和运行容器的CLI(command-line interface)工具,runc直接与容器所依赖的cgroup/linux kernel等进行交互,负责为容器配置cgroup/namespace等启动容器所需的环境,创建启动容器的相关进程。

为了兼容OCI标准,docker也做了架构调整,将容器运行时相关的程序从docker daemon剥离出来,形成了containerd。Containerd向docker提供运行容器的API,二者通过grpc进行交互,containerd最后会通过runc来实际运行容器。

image-20201214154047760

kubernetes在初期版本中,就对多个容器引擎做了兼容,因此可以使用docker、rkt对容器进行管理,以docker为例,kubelet会启动一个docker manager,通过直接调用docker的api进行容器的创建等操作。

在k8s 1.5版本之后,k8s推出了自己的运行时接口api—CRI(container runtime interface)。cri接口的推出,隔离了各个容器引擎之间的差异,而通过统一的接口与各个容器引擎之间进行互动。与oci不同, cri与k8s的概念更加贴合,并紧密绑定,cri不仅定义了容器的生命周期管理,还引入了k8s中pod的概念,并定义了管理pod的生命周期。在k8s中,pod是由一组进行了资源限制,在隔离环境中的容器组成。而这个隔离环境,称之为PodSandbox。在cri开始之处,主要是支持docker和rkt两种,其中kubelet是通过cri接口,调用docker-shim,并进一步调用docker api实现的。docker独立出来containerd,kubernetes也顺应潮流,孵化了cri-containerd项目,用以将containerd接入到cri的标准中。

image-20201214155233771

为了进一步与oci兼容,kubernetes还孵化了cri-o,成为了架设在cri和oci之间的一座桥梁。通过这种方式,可以方便更多符合oci标准的容器运行时,接入kubernetes进行集成使用。可以预见到,通过cri-o,k8s在使用的兼容性和广泛性上将会得到进一步的加强。

image-20201214155435068

Kata Containers: 云原生化的虚拟化

image-20201214152746972

具体过程为:

  • 当containerd拿到一个请求的时候,它会首先创建一个shim-v2,这个shim-v2就是一个PodSandbox的代表,也就是那个VMM的代表;
  • 每个Pod都会有一个shim-v2来为containerd/CRI-O来执行各种各样的操作。shim-v2会为这个Pod启动一个虚拟机,在里面运行着一个linux kernel,也就是图里面的guest kernel。如果这个里面用的是qemu,会通过一些配置和一些补丁,让其变得更小,同时这里面没有额外的Guest操作系统,不会跑一个完整的像Centos,Ubuntu这样的操作系统;
  • 之后会把这个容器的spec以及这个容器本身打包起来的存储,包括rootfs和文件系统,交给这个PodSandbox。这个PodSandbox会在虚拟机中由kata-agent把这个容器启动起来;
  • 依照CRI语义和OCI规范,在一个Pod里面可以启动多个相关联的容器,它们会被放在同一个虚拟机里面,并且可以依据需求共享某些namespace;
  • 除了这些之外,其他的一些外置的存储和卷也可以通过热插拔的方式来插到这个PodSandbox里面;
  • 对于网络,目前使用tcfilter就可以无缝地接入几乎所有的k8s的CNI插件,而且还提供了一个enlightened的模式,这样会有一个特制的CNI插件来提供容器的网络能力。

Github: Documentation

https://github.com/kata-containers/documentation/blob/master/design/architecture.md

The container process is then spawned by agent, an agent process running as a daemon inside the virtual machine. kata-agent runs a gRPC server in the guest using a VIRTIO serial or VSOCK interface which QEMU exposes as a socket file on the host. kata-runtime uses a gRPC protocol to communicate with the agent. This protocol allows the runtime to send container management commands to the agent. The protocol is also used to carry the I/O streams(stdout, stderr, stdin) between the containers and the manage engines. (e.g. Docker Engine).

For any given container, both the init process and all potentially executed commands within that container, together with their related I/O streams, need to go through the VIRTIO serial or VSOCK interface exported by QEMU. In the VIRTIO serial case, a Kata Container proxy (kata-proxy) instance is launched for each virtual machine to handle multiplexing and demultiplexing those commands and streams.

A kata-shim instance will both forward signals and stdin streams to the container process on the guest and pass the container stdout and stderr streams back up the stack to the CRI shim or Docker via the container process reaper. Kata-runtime creates a kata-shim daemon for each container and for each OCI command received to run within an already running container(example, docker exec).

Guest image(Root filesystem image)

当运行docker run -ti ubuntu date命令时:

  • hypervisor将使用guest kernel启动mini-OS
  • systemd运行在mini-OS上下文中,将在相同的环境下启动kata-agent
  • 代理将创建一个新的受限的上下文来运行指定的命令(在此实例为date)
  • 代理将在这个新的上下文中执行命令,并首先将根文件系统设置为预期的Ubuntu根文件系统

Agent

Kata-agent是一个进程,运行在客户操作系统中用于管理容器以及容器内运行的进程。kata-agent执行单元是由一系列命名空间(NS, UTS, IPC, PID)定义的沙箱。kata-runtime能够在每个虚拟机中运行多个容器。在使用docker的情形中,kata-runtime在每个pod中创造单一的容器。kata-agent通过gRPC和其他的kata组件进行通信,其在同样的gRPC URL上运行一个yamux server. kata-agent基于libcontainer来管理容器的生命周期,这样kata-agent就能够重用runc的大部分代码。

Runtime

kata-runtime是一个OCI兼容的容器运行时并负责处理所有OCI运行时规范定义的指令,启动kata-shim实例。运行时的行为由configuration.toml文件控制,该文件默认安装在/usr/share/defaults/kata-containers环境下面。下面将介绍kata-runtime如何处理一些最重要的OCI指令。

create

当处理OCIcreate命令时,kata-runtime要经过下列步骤:

  1. 创建网络命名空间用于启动虚拟机和shim进程;

  2. 执行pre-start hooks,其中一个负责在主机命名空间和新创建的网络命名空间之间建立veth pair

  3. 扫描新创建的网络命名空间,创建一个MACVTAP设备用于连接veth接口和虚拟机内的tap接口;(引入MACVTAP设备的目标是:简化虚拟机环境中的交换网络,代替传统的Linux Tap设备加Bridge设备的组合,更多相关信息可以参考:https://opengers.github.io/openstack/openstack-base-virtual-network-devices-tuntap-veth/ ,https://cloud.tencent.com/developer/article/1472358 ,下图引自参考链接)

    image-20201215171646775
  4. 在创建的网络命名空间启动VM,并在VM中添加tap接口;

  5. 等待VM就绪;

  6. 启动kata-proxykata-proxy连接到创建好的虚拟机,该进程负责代理与虚拟机的所有通信流量。对于每个VM都对应着一个代理

  7. 通过代理与kata-agent通信在VM内部配置沙箱;

  8. kata-agent通信创建容器,依赖向kata-runtime提供的OCI配置文件config.json,将基于libcontainer在虚拟机内部启动容器主进程;

  9. 启动kata-shim,它将连接到kata-proxy提供的gRPC server上,kata-shim用于获取容器进程在终止之前的输出(ReadStdout, ReadStderr),以及VM内的容器进程终止时的返回码(WaitProcess)。注意,kata-shim是在网络命名空间内启动的,以允许上层确定已创建哪个网络命名空间并通过检查kata-shim进程来确定,这确保kata-shim进程随着容器进程的终止而被杀死。

image-20201216104313559

start

kata-runtime将经过下列步骤:

  1. 首先通过kata-proxykata-agent交互来启动VM内的容器工作进程。例如在容器内部执行的命令是topkata-shimReadStdOut()将会开始返回top命令的文本输出,waitProcess()只要top进程还在运行就会一直阻塞;
  2. 调用post-starthooks,通常并没有进行什么操作;

image-20201216105505321

exec

OCI exec命令允许在已经运行的容器中运行其他的命令,在kata containers中将执行下列步骤:

  1. kata-runtime通过代理向kata-agent发起请求来在VM中正在运行的容器中启动一个新的进程;
  2. 一个新的kata-shim进程被创建,并共享容器主进程对应的kata-shim进程所在的网络和PID名称空间,这个新的kata-shim被用于新的exec进程。(这个进程运行在VM内,与容器进程共享uts, pid, mnt, ipc命名空间)

image-20201216110052320

kill

当发送OCI kill命令时,容器运行时将发送一个UNIX信号给容器进程来终止容器进程,例如SIGKILL或者SIGTERM。在传统的容器中,这意味着终止一个容器的运行;在kata containers中,这意味着终止容器以及与之关联的VM。其经过下列步骤:

  1. 通过代理向kata-agent发送请求来杀死容器进程;
  2. 等待kata-shim进程退出;
  3. 如果kata-shim进程超时仍然没有退出,强行杀死容器进程。通过向VM内的容器进程发送SIGKILL信号;
  4. 通过代理与kata-agent通信从VM移除容器配置;
  5. 通过代理与kata-agent通信从VM移除沙箱配置;
  6. 停止VM;
  7. 移除网络命名空间中所有的网络配置并删除网络命名空间;
  8. 执行post-stop hooks;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值