一.系统环境
本文主要基于Kubernetes1.22.2和Linux操作系统Ubuntu 18.04。
| 服务器版本 | docker软件版本 | Kubernetes(k8s)集群版本 | Kata软件版本 | containerd软件版本 | CPU架构 |
|---|---|---|---|---|---|
| Ubuntu 18.04.5 LTS | Docker version 20.10.14 | v1.22.2 | 1.11.5 | 1.6.4 | x86_64 |
Kubernetes集群架构:k8scludes1作为master节点,k8scludes2,k8scludes3作为worker节点。
| 服务器 | 操作系统版本 | CPU架构 | 进程 | 功能描述 |
|---|---|---|---|---|
| k8scludes1/192.168.110.128 | Ubuntu 18.04.5 LTS | x86_64 | docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico | k8s master节点 |
| k8scludes2/192.168.110.129 | Ubuntu 18.04.5 LTS | x86_64 | docker,kubelet,kube-proxy,calico | k8s worker节点 |
| k8scludes3/192.168.110.130 | Ubuntu 18.04.5 LTS | x86_64 | docker,kubelet,kube-proxy,calico | k8s worker节点 |
二.前言
容器技术因其轻量级、可移植和易于管理的特点而受到广泛欢迎。然而,传统容器技术在安全方面存在一定的缺陷。为了解决这些问题,安全容器技术应运而生。本文将重点介绍一种名为 Kata Containers 的安全容器解决方案。另外一种安全容器为gVisor,相关详细操作请查看博客《以沙箱的方式运行容器:安全容器gvisor》。
安全容器是一种运行时技术,为容器应用提供一个完整的操作系统执行环境,但将应用的执行与宿主机操作系统隔离开,避免应用直接访问主机资源,从而可以在容器主机之间或容器之间提供额外的保护。
以沙箱的方式运行容器的前提是已经有一套可以正常运行的Kubernetes集群,关于Kubernetes(k8s)集群的安装部署,可以查看博客《Ubuntu 安装部署Kubernetes(k8s)集群》https://www.cnblogs.com/renshengdezheli/p/17632858.html。
三.Kata Containers 简介
Kata源自希腊文Καταπστευμα(ka-ta-PI-stev-ma),原意是值得信任的人,kata container正是解容器安全的问题而诞生的。传统的容器是基于namespace和cgroup进行隔离,在带来轻量简洁的同时,也带来了安全的隐患。事实上容器虽然提供一个与系统中的其它进程资源相隔离的执行环境,但是与宿主机系统是共享内核的,一旦容器里的应用逃逸到内核,后果不堪设想,尤其是在多租户的场景下。Kata就是在这样的背景下应运而生,kata很好的权衡了传统虚拟机的隔离性、安全性与容器的简洁、轻量。这一点和firecracker很相似,都是轻量的虚拟机。但是他们的本质的区别在于:kata虽然是基于虚机,但是其表现的却跟容器是一样的,可以像使用容器一样使用kata;而firecracker虽然具备容器的轻量、极简性,但是其依然是虚机,一种比QEMU更轻量的VMM,暂时不能兼容容器生态。
Kata的基本原理是,为每一个容器单独开一个虚机(如果是k8s下作为runtime,则是一个pod对应一个虚机而不是容器),具有独立的内核,这样交付的容器就具备了虚机级别的隔离和安全性。kata的原理图如下所示:

图片
Kata Containers结合了虚拟机和容器的优势,提供了一种更加安全和隔离的容器运行时环境。Kata Containers使用轻量级的虚拟机(如QEMU)来运行容器,每个容器都有一个独立的虚拟机实例,这样可以实现容器之间的完全隔离。
由于Kata Containers使用了虚拟机来运行容器,所以相对于gVisor来说,它的性能开销更大。根据测试数据,Kata Containers的性能损失在20%左右。这主要是因为每个容器都运行在一个独立的虚拟机实例中,需要额外的资源和开销。Kata Containers支持多核并发,可以在多核系统上实现更好的性能。

图片
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 Containers 项目会启动一系列跟用户容器对应的 shim 进程,来负责操作这些用户容器的生命周期。当然,这些操作,实际上还是要靠虚拟机里的 Init 进程来帮你做到。
四.Gvisor与Kata区别对比
无论是 Kata Containers,还是 gVisor,它们实现安全容器的方法其实是殊途同归的。这两种容器实现的本质,都是给进程分配了一个独立的操作系统内核,从而避免了让容器共享宿主机的内核。这样,容器进程能够看到的攻击面,就从整个宿主机内核变成了一个极小的、独立的、以容器为单位的内核,从而有效解决了容器进程发生“逃逸”或者夺取整个宿主机的控制权的问题。
它们的区别在于,Kata Containers 使用的是传统的虚拟化技术,通过虚拟硬件模拟出了一台“小虚拟机”,然后在这个小虚拟机里安装了一个裁剪后的 Linux 内核来实现强隔离。
而 gVisor 的做法则更加激进,Google 的工程师直接用 Go 语言“模拟”出了一个运行在用户态的操作系统内核,然后通过这个模拟的内核来代替容器进程向宿主机发起有限的、可控的系统调用。
五.配置docker使用kata作为runtime
注意:kata本质上是以虚拟机的方式运行的,需要开启虚拟化引擎功能,VMware Workstation需要如下设置:

图片
安装kata网址:https://github.com/kata-containers/kata-containers/tree/main/docs/install 。

图片
5.1 安装docker
我们在客户端机器etcd2(centos系统)上安装docker。
设置docker开机自启动并现在启动docker。
查看docker版本。
配置docker镜像加速器。
重启docker。
设置iptables不对bridge的数据进行处理,启用IP路由转发功能。
使配置生效。
现在docker默认的runtime为runc。
接下来配置docker使用kata作为runtime,思路还是首先安装所需的runtime,接着配置docker支持多个runtime。
5.2 安装kata
5.2.1 在线安装(不推荐,下载非常慢)
编辑下载源。
安装kata。
5.2.2 使用rpm包离线安装(推荐)
创建存放文件的目录,先下载centos7的kata 1.11.5 RPM包。
安装kata。
此时kata就安装好了。
5.3 配置docker支持kata作为runtime
kata安装好之后,要配置docker支持kata作为runtime。
查看docker状态。
需要修改docker的启动参数:ExecStart=/usr/bin/dockerd -D --add-runtime kata-runtime=/usr/bin/kata-runtime --default-runtime=kata- runtime -H fd:// --containerd=/run/containerd/containerd.sock
-D --add-runtime kata-runtime=/usr/bin/kata-runtime 表示让docker支持kata作为runtime。
--default-runtime=kata-runtime表示让docker默认以kata作为runtime。
重新加载配置文件,重启docker。
查看docker runtime,可以发现,现在docker支持kata-runtime了。
5.4 docker使用kata作为runtime创建容器
现在有nginx镜像。
--runtime=kata-runtime 表示使用kata作为runtime创建容器,不过报错了,由报错可知内存不够了,因为kata本质上是kvm虚拟机,所以内存要求比容器大。
内存空间不足,需要扩容,我把机器内存从1.2G扩容到2G。
再次创建容器。
容器创建成功。
docker使用kata作为runtime,以沙箱的方式运行容器,在宿主机里就看不到容器里运行的进程了。
查看容器属性,可以发现,现在容器的Runtime为kata-runtime。
删除容器。
六.配置containerd使用kata作为runtime
6.1 安装containerd
如果你熟悉docker,但是不了解containerd,请查看博客《在centos下使用containerd管理容器:5分钟从docker转型到containerd》,里面有详细讲解。
我们在客户端机器ubuntuk8sclient(ubuntu系统)上安装containerd。
更新软件源。
安装containerd。
设置containerd开机自启动并现在启动containerd。
查看containerd状态。
containerd的配置文件为/etc/containerd/config.toml 。
containerd的配置文件为/etc/containerd/config.toml ,可以使用containerd config default > /etc/containerd/config.toml生成默认的配置文件,containerd config default生成的配置文件内容还是挺多的。
containerd config dump显示当前的配置。
查看containerd版本。
修改配置文件,添加阿里云镜像加速器。
SystemdCgroup = false修改为SystemdCgroup = true。
有个sandbox的镜像,k8s.gcr.io/pause:3.6访问不了。
修改sandbox镜像为可以访问的阿里云镜像。
重新加载配置文件并重启containerd服务。
containerd 客户端工具有 ctr 和 crictl ,如果使用 crictl 命令的话,需要执行 crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock ,不然会有告警。ctr和crictl更多命令细节,请查看博客《在centos下使用containerd管理容器:5分钟从docker转型到containerd》。
查看containerd信息。
containerd 客户端工具 ctr 和 crictl 不好用,推荐使用nerdctl,nerdctl是containerd的cli客户端工具,与docker cli大部分兼容,用法类似docker命令。
使用nerdctl命令需要两个安装包nerdctl-0.20.0-linux-amd64.tar.gz和cni-plugins-linux-amd64-v1.1.1.tgz。
nerdctl-0.20.0-linux-amd64.tar.gz下载地址:https://github.com/containerd/nerdctl/releases 。
网络插件cni-plugins-linux-amd64-v1.1.1.tgz下载地址:https://github.com/containernetworking/plugins/releases 。
分别进行解压。
配置nerdctl命令tab自动补全,添加source <(nerdctl completion bash)。
使配置文件/etc/profile生效。
查看镜像。
查看命名空间。
nerdctl的命令和docker命令很相似,只要把docker命令里的docker换成nerdctl,基本都能执行成功。
拉取镜像。
查看containerd信息。
6.2 安装kata
Ubuntu系统的kata 1.11.5 软件包下载网址:
https://download.opensuse.org/repositories/home:/katacontainers:/releases:/x8664:/stable-1.11/xUbuntu18.04/amd64/ 。
提前下载好软件包。
更新软件源。
安装kata。
现在kata就安装好了。
6.3 配置containerd支持kata作为runtime
kata安装好之后,现在要配置containerd支持kata作为runtime。
修改containerd配置文件,runtime_type = "io.containerd.kata.v2" 。
重新加载配置文件,重启containerd。
查看runtime,现在就可以看到containerd支持三种runtime了:runc,runsc(这个看不到正常),kata-runtime。
6.4 containerd使用kata作为runtime创建容器
查看镜像。
创建容器,--runtime=kata-runtime 表示containerd使用kata-runtime创建容器。
containerd使用kata作为runtime,以沙箱的方式运行容器,在宿主机里就看不到容器里运行的进程了。
停止容器。
删除容器。
七.在k8s环境里,配置containerd作为高级别runtime,containerd使用kata作为低级别runtime
7.1 把ubuntuk8sclient节点加入k8s集群
注意docker作为k8s的高级别runtime的时候,不支持kata作为docker的低级别runtime,只有单机版的时候,kata才能作为docker的低级别runtime。
描述一下当前的系统环境:现在有一个k8s集群,1个master,2个worker,三台机器都是使用docker作为高级别runtime,现在添加一个新的worker节点,新的worker节点使用containerd作为高级别runtime,kata作为containerd的低级别runtime。
现在把ubuntuk8sclient机器加入k8s集群,ubuntuk8sclient的CONTAINER-RUNTIME为containerd。
查看集群节点。
先在所有的机器配置IP主机名映射(以ubuntuk8sclient为例)。
配置软件源,软件源如下,最后三行是k8s源。
apt-key.gpg是k8s的deb源公钥,加载k8s的deb源公钥 apt-key add apt-key.gpg。
下载并加载k8s的deb源公钥:curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - ; apt-get update。但是谷歌的网址访问不了,我们直接去网上下载apt-key.gpg文件,加载k8s的deb源公钥。
更新软件源。
Linux swapoff命令用于关闭系统交换分区(swap area)。如果不关闭swap,就会在kubeadm初始化Kubernetes的时候报错:“with swap on is not supported. Please disable swap”。
设置containerd当前命名空间为k8s.io。
加载overlay和br_netfilter模块。
设置iptables不对bridge的数据进行处理,启用IP路由转发功能。
使配置生效。
为了k8s节点间的通信,需要安装cni网络插件,提前下载好calico镜像,calico镜像版本要和k8s的那三个节点的calico版本一致。
安装kubelet,kubeadm,kubectl。
- Kubelet 是 kubernetes 工作节点上的一个代理组件,运行在每个节点上;
- Kubeadm 是一个快捷搭建kubernetes(k8s)的安装工具,它提供了 kubeadm init 以及 kubeadm join 这两个命令来快速创建 kubernetes 集群;kubeadm 通过执行必要的操作来启动和运行一个最小可用的集群;
- kubectl是Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。
设置kubelet开机自启动并现在启动。
在k8s的master节点,查看k8s worker节点加入k8s集群的token。
把ubuntuk8sclient节点加入k8s集群。
去k8s master节点查看是否加入k8s集群,可以看到ubuntuk8sclient成功加入k8s集群,并且CONTAINER-RUNTIME为containerd://1.6.4。
7.2 配置kubelet使其支持Kata
配置kubelet,使其可以支持Kata作为containerd的低级别runtime,修改kubelet参数,让其支持Kata作为runtime。
重新加载配置文件并重启kubelet。
7.3 创建容器运行时类(Runtime Class)
在k8s里使用kata创建pod,需要使用到容器运行时类(Runtime Class)。
RuntimeClass 是一个用于选择容器运行时配置的特性,容器运行时配置用于运行 Pod 中的容器。你可以在不同的 Pod 设置不同的 RuntimeClass,以提供性能与安全性之间的平衡。 例如,如果你的部分工作负载需要高级别的信息安全保证,你可以决定在调度这些 Pod 时,尽量使它们在使用硬件虚拟化的容器运行时中运行。 这样,你将从这些不同运行时所提供的额外隔离中获益,代价是一些额外的开销。
你还可以使用 RuntimeClass 运行具有相同容器运行时,但具有不同设置的 Pod。
注意RuntimeClass是全局生效的,不受命名空间限制。
编辑RuntimeClass配置文件,handler后面写runtime的名字,我们要使用kata就写kata-runtime,handler: kata-runtime表示指定使用kata-runtime作为runtime。
创建RuntimeClass。
给ubuntuk8sclient节点创建一个标签con=kata。
7.4 使用kata创建pod
编辑pod配置文件,在ubuntuk8sclient节点创建pod。
runtimeClassName: mykataruntimeclass 表示使用mykataruntimeclass里的kata作为runtime。
nodeSelector:con: kata指定pod运行在标签为con=kata的ubuntuk8sclient节点。
创建pod,pod运行在ubuntuk8sclient节点。
创建好pod之后,去ubuntuk8sclient节点查看nginx进程,kata以沙箱的方式运行容器,在宿主机里就看不到容器里运行的进程了。
kata本质上是kvm虚拟机,需要开启CPU虚拟化功能,显示vmx或者svm,表示已经开启CPU虚拟化功能了。
删除pod。
可以把ubuntuk8sclient节点从k8s集群删掉。
八.总结
Kata Containers 作为一种安全容器技术,通过硬件虚拟化和沙箱隔离为容器提供了更高的安全性。与传统容器技术相比,Kata Containers 在安全性能方面具有明显优势。在本文中,我们以 Kubernetes 1.22.2 和 Ubuntu 18.04 为例,介绍了如何在 Linux 操作系统上使用 Kata Containers 以沙箱的方式运行容器。希望本文能为您的容器安全部署提供参考。