容器那些事

在使用k8s时,经常会遇到docker、oci、runc、containerd、oci-o、cri、kata等概念。本文对相关名词和其之间的联系进行一下梳理和总结,以此作为后续
对于kubelet相关分析的铺垫。

容器相关

container与docker

container的概念早于docker,其源于LXC技术,当然在此需要感谢docker社区对于容器技术的贡献,因为docker的出现,container的概念深入人心,容器
的特性类似sandbox,进程受到资源的限制,并且彼此间隔离,当然容器区别与虚拟机的技术,主要区别在于内核的是否共享,上述的论断可能不太精确,因此后期如
gvisor技术,其通过隔离层实现系统调用。通俗来说容器技术主要指资源限制的cgroup、用于隔离的namespace以及共享内核。

11月13日,Mirantis收购Docker的消息引爆开源届。

OCI标准

Open Container Initiative,也就是常说的OCI,是由多家公司共同成立的项目,并由linux基金会进行管理,致力于container runtime的标准的制定和runc的开发等工作。
所谓container runtime,主要负责的是容器的生命周期的管理。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来实际运行容器。

container engine

容器引擎,或者说容器平台,不仅包含对于容器的生命周期的管理,还包括了对于容器生态的管理,比如对于镜像等。现在的docker、rkt以及阿里推出的pouch均可属于此范畴。
docker,随着docker的发展,docker就不再局限于容器的管理,还囊括了存储(volume)、网络(net)等的管理,因此后来的docker是一个容器的管理平台。而runtime逐渐插件化,如下图的runc与kata runtime。

docker runtime plugin


Kubernetes相关

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

在k8s 1.5版本之后,kubernetes推出了自己的运行时接口CRI(container runtime interface)。cri接口的推出,隔离了各个容器引擎之间的差异,而通过统一的接口与各个容器引擎之间进行互动。

与oci不同,cri与kubernetes的概念更加贴合,并紧密绑定。cri不仅定义了容器的生命周期的管理,还引入了k8s中pod的概念,并定义了管理pod的生命周期。在kubernetes中,pod是由一组进行了资源限制的,在隔离环境中的容器组成。而这个隔离环境,称之为PodSandbox。在cri开始之初,主要是支持docker和rkt两种。其中kubelet是通过cri接口,调用dockershim,并进一步调用docker api实现的。

如上文所述,docker独立出来了containerd。kubernetes也顺应潮流,孵化了cri-containerd项目,用以将containerd接入到cri的标准中。

在之后的社区发展中,kubelet支持docker及remote两种类型,其中docker即内置的dockershim以满足CRI接口,remote方式同样需要满足CRI接口规范。

下图为各runtime与kubelet的通信

kubelet CRI

其中的CRI-O插件实现kubelet CRI接口,并能够兼容OCI标准,作为一种可替换docker的轻量级运行时方案,其允许kubelet允许任一符合OCI标准的runtime来运行Pod。
下图为CRI-O与OCI接口通信图示。

CRI-O通信

CRI-O插件主要有以下功能:

  • 镜像服务
  • runtime服务
  • CNI
  • OCI 生成器

最后,kubelet与CRI-O的交互流程如下:

  1. kubelet通过list接口获悉启动POD,其中POD是kubernetes的一个概念,由一个或多个共享IPC,NET和PID名称空间并位于同一cgroup中的容器组成。
  2. kubelet将请求下发到kubernetes CRI-O服务,即kubelet启动时的remote配置(容器运行时接口),以启动新的POD。
  3. CRI-O使用containers/image库从镜像仓库拉取镜像。
  4. 下载后的镜像解压存储到为容器创建rootfs,即存储在COW文件系统中。
  5. 在rootfs创建后,CRI-O会生成OCI运行时规范json文件,该文件描述了如何使用OCI Generate工具运行容器。
  6. CRI-O使用该配置启动OCI兼容的运行时,以运行容器进程。默认的OCI运行时是runc。
  7. 每个容器都由单独的通用程序进行监控。 conmon进程保留了容器进程的PID1。它处理容器的日志记录并记录容器过程的退出代码。
  8. 通过CNI设置Pod的网络,因此任何CNI插件均可与CRI-O一起使用。

下图为CRI的功能图。
CRI-O功能


后续

后续将推出kubernetes runtimeclass文章


参考资料