微信号:godocker

介绍:关于Docker容器虚拟化的技术及实践都在这里!

Docker1.11版本升级内容详解(二)

2016-04-27 18:33 精灵云


2. runC工作原理与实现方式

2.1 runC从libcontainer的变迁

       runC的前身实际上是Docker的libcontainer项目演化而来。runC实际上就是libcontainer配上了一个轻型的客户端。

       从本质上来说,容器是提供一个与宿主机系统共享内核但与系统中的其他进程资源相隔离的执行环境。Docker通过调用libcontainer包对namespaces、cgroups、capabilities以及文件系统的管理和分配来“隔离”出一个上述执行环境。同样的,runC也是对libcontainer包进行调用,去除了Docker包含的诸如镜像、Volume等高级特性,以最朴素简洁的方式达到符合OCF标准的容器管理实现。

总体而言,从libcontainer项目转变为runC项目至今,其功能和特性并没有太多变化,具体有如下几点。

  • 把原先的nsinit移除,放到外面,命令名称改为runc,同样使用cli.go实现,一目了然。

  • 按照开放容器标准把原先所有信息混在一起的一个配置文件拆分成config.json和runtime.json两个。

  • 增加了按照开放容器标准设定的容器运行前和停止后执行的hook脚本功能。

  • 相比原先的nsinit时期的指令,增加了runc kill命令,用于发送一个SIG_KILL信号给指定容器ID的init进程。

  总体而言,runC希望包含的特征有:

  • 支持所有的Linux namespaces,包括user namespaces。目前user namespaces尚未包含。

  • 支持Linux系统上原有的所有安全相关的功能,包括Selinux、 Apparmor、seccomp、cgroups、capability drop、pivot_root、 uid/gid dropping等等。目前已完成上述功能的支持。

  • 支持容器热迁移,通过CRIU技术实现。目前功能已经实现,但是使用起来还会产生问题。

  • 支持Windows 10 平台上的容器运行,由微软的工程师开发中。目前只支持Linux平台。

  • 支持Arm、Power、Sparc硬件架构,将由Arm、Intel、Qualcomm、IBM及整个硬件制造商生态圈提供支持。

  • 计划支持尖端的硬件功能,如DPDK、sr-iov、tpm、secure enclave等等。

  • 生产环境下的高性能适配优化,由Google工程师基于他们在生产环境下的容器部署经验而贡献。

  • 作为一个正式真实而全面具体的标准存在!

2.2 runC是如何启动容器的?

  从开放容器标准中我们已经定义了关于容器的两份配置文件和一个依赖包,runc就是通过这些来启动一个容器的。首先我们按照官方的步骤来操作一下。

  runc运行时需要有rootfs,最简单的就是你本地已经安装好了Docker,通过

docker pull busybox

  下载一个基本的镜像,然后通过

  docker export $(docker create busybox) > busybox.tar

  导出容器镜像的rootfs文件压缩包,命名为busybox.tar。然后解压缩为rootfs目录。

  mkdir rootfstar -C rootfs -xf busybox.tar

  这时我们就有了OCF标准的rootfs目录,需要说明的是,我们使用Docker只是为了获取rootfs目录的方便,runc的运行本身不依赖Docker。

接下来你还需要config.json和runtime.json,使用

runc spec

  可以生成一份标准的config.json和runtime.json配置文件,当然你也可以按照格式自己编写。

  如果你还没有安装runc,那就需要按照如下步骤安装一下,目前runc暂时只支持Linux平台。

  create a 'github.com/opencontainers' in your GOPATH/srccd github.com/opencontainersgit clone https://github.com/opencontainers/runccdruncmakesudo make install

  最后执行

  runc start

  你就启动了一个容器了。


2.3 runC start运行原理

  上面说到过runC就是libcontainer外面裹上了一层很薄的cli。其中的Cli是为了快速开发go语言的命令行应用而实现的开发包,它可以为你处理诸如子命令定义,标志位定义和设置帮助信息等等。并且cli也是托管在git上面的一个开源项目,地址为:github.com/codegangsta/cli。

从源码角度,分析runC start的执行流程,整个分析过程如下图:


2.3.1.一切从main()函数开始

  整个程序首先执行main.go中的main()函数,在这个函数中,程序通过cli包对runc的各个子命令,参数,版本号以及帮助信息进行规定。然后程序会通过用户输入的子命令来调用对应的处理函数,这里则调用start.go中的startContainer()函数。


2.3.2.创建逻辑容器Container与逻辑进程process

  所谓的逻辑容器container和逻辑进程process并非时真正运行着的容器和进程,而是libcontainer中所定义的结构体。逻辑容器container中包含了namespace,cgroups,device和mountpoint等各种配置信息。逻辑进程process中则包含了容器中所要运行的指令以其参数和环境变量等。

  对于runC来说,容器的定义只需要一种就够了,不同的容器只是实例的内容(属性和参数)不一样而已。对于libcontainer来说,由于它需要与底层打交道,不同的平台上就需要创建出完全异构的“逻辑容器对象”(比如Linux容器和Windows容器),这也就解释了为什么这里会使用“工厂模式”:今后libcontainer可以支持更多平台上各种类型容器的实现,而不必改变调用接口。

  下面解释一下逻辑容器Container与逻辑进程process的创建过程。

在startContainer()函数中,程序首先将*.json装入可以被libcontainer使用的结构体config中。然后使用config作为参数来调用libcontainer.New()生成用来产生container的工厂factory。再调用factory.Create(config),就会生成一个将config包含其中的逻辑容器container。接下来调用newProcess(config)来将config中关于容器内所要运行命令的相关信息填充到process结构体中,这个结构体即为逻辑进程process。使用container.Start(process)来启动逻辑容器。


2.3.3.启动逻辑容器container

  runC会调用Start(),Start()函数位于libcontainer/container_linux.go中,主要工作就是调用newParentProcess()来生成parentprocess实例(结构体)和用于runC与容器内init进程相互通信的管道。

  在parentprocess实例中,除了有记录了将来与容器内进程进行通信的管道与各种基本配置等,还有一个极为重要的字段就是其中的cmd。

  cmd字段是定义在os/exec包中的一个结构体。os/exec包主要用于创建一个新的进程,并在这个进程中执行指定的命令。开发者可以在工程中导入os/exec包,然后将cmd结构体进行填充,即将所需运行程序的路径和程序名,程序所需参数,环境变量,各种操作系统特有的属性和拓展的文件描述符等。

  在runC中程序将cmd的应用路径字段Path填充为/proc/self/exe(即为应用程序本身,runC)。参数字段Args填充为init,表示对容器进行初始化。SysProcAttr字段中则填充了各种runC所需启用的namespace等属性。

  然后调用parentprocess.cmd.Start()启动物理容器中的init进程。接下来将物理容器中init进程的进程号加入到Cgroup控制组中,对容器内的进程实施资源控制。再把配置参数通过管道传送给init进程。最后通过管道等待init进程根据上述配置完成所有的初始化工作,或者出错退出。


2.3.4.物理容器的配置和创建

  容器中的init进程首先会调用StartInitialization()函数,通过管道从父进程接收各种配置参数。然后对容器进行如下配置:

  1.如果用户指定,则将init进程加入其指定的namespace。

  2.设置进程的会话ID。

  3.初始化网络设备。

  4.对指定目录下的文件系统进行挂载,并切换根目录到新挂载的文件系统下。设置hostname,加载profile信息。

最后使用exec系统调用来执行用户所指定的在容器中运行的程序。



 
GoDocker 更多文章 乐视:基于Docker的RDS,我们是这样做的 Docker为整个软件生命周期提供安全保障 应用程序跑在Docker容器中会更安全 什么是持续集成?持续交付?持续部署? Docker生态不会重蹈Hadoop的覆辙
猜您喜欢 PHP中exec、system等函数调用linux命令问题 微信JS-SDK实例分享 综合指南:何时使用 Em 与 Rem 高薪来袭,传智播客重磅推出Python学科! 从菜鸟成为数据科学家的 9步养成方案