资讯专栏INFORMATION COLUMN

走进docker(04):什么是容器的runtime?

wangxinarhat / 4524人阅读

摘要:容器的注释,相当于容器标签,来自于容器的配置文件,格式。执行命令启动容器执行中配置的钩子执行中指定的程序,这时容器状态变成了执行钩子。容器由于某些原因退出,比如容器中的第一个进程主动退出,挂掉或者被掉等。

我们都知道runc是容器runtime的一个实现,那到底什么是runtime?包含了哪些内容?

容器的runtime和image一样,也有标准,也由OCI (Open Containers Initiative)负责维护,地址为Runtime Specification,了解runtime标准有利于我们更好的理解docker和runc的关系,本文将对该标准做一个简单的解释。

规范内容

在Linux平台上,跟runtime有关的规范主要有四个,分别是Runtime and Lifecycle (runtime.md)、Container Configuration file (config.md)、Linux Container Configuration (config-linux.md)和Linux Runtime (runtime-linux.md) .

除了这四个之外,还有一个Filesystem Bundle (bundle.md)。

下面分别介绍这些规范。

Filesystem Bundle

在上一篇中,已经用过bundle了,hello-world的bundle看起来是这个样子的:

dev@debian:~/images$ tree hello-world-bundle
hello-world-bundle
├── config.json
└── rootfs
    └── hello

1 directory, 2 files

bundle中包含了运行容器所需要的所有信息,有了这个bundle后,符合runtime标准的程序(比如runc)就可以根据bundle启动容器了。

bundle包含一个config.json文件和容器的根文件系统目录,config.json就是后面要介绍的Container Configuration file,标准要求该配置文件必须叫这个名字,不过对容器的根文件系统目录没有要求,只要在config.json里面将路径配置正确就可以了,不过一般约定俗成都叫rootfs。

实际使用过程中,根文件系统目录可能在其它的地方,只要config.json里面配置正确的路径就可以了,但如果bundle需要打包和其它人分享的话,必须将根文件系统和config.json打包在一起,并且不包含外层的文件夹。

Container Configuration file

该规范定义了上面介绍的config.json里面应该包含哪些内容,字段很多,这里不一一详细介绍,只简单说明一下,完整的示例请参考这里:

ociVersion(必须):对应的OCI标准版本

root(必须):根文件系统的位置

mounts:需要挂载哪些目录到容器里面。如果是Linux平台,这里面必须要包含/proc、/sys,/dev/pts,/dev/shm这四个目录

process:容器启动后执行什么命令

hostname:容器的主机名,相关原理可参考UTS namespace (CLONE_NEWUTS)

platform(必须):平台信息,如 amd64 + Linux

linux:Linux平台的特殊配置,这里包含下面要介绍的Linux Container Configuration里面的内容

hooks:配置容器运行生命周期中会调用的hooks,包括prestart、poststart和poststop,容器的生命周期见后面Runtime and Lifecycle介绍。

annotations:容器的注释,相当于容器标签,key:value格式

Linux Container Configuration

该规范是Linux平台上对Container Configuration file的扩展,这部分的内容也包含在上面的config.json文件中。

namespaces: namespace相关的配置,相关原理可参考Namespace概述及这些namespace(UTS、IPC、mount、pid、network、user 1、user 2)。

uidMappings,gidMappings:配置主机和容器用户/组之间的对应关系,原理可参考user namespace

devices:设置哪些设备可以在容器内被访问到。除了这里指定的设备外,/dev/null、/dev/zero、/dev/full、/dev/random、/dev/urandom、/dev/tty、/dev/console(如果在process的配置里面启动terminal的话)和/dev/ptmx这些设备默认就能在容器内访问到,即runtime的实现需要默认将这些设备bind到容器内,dev/tty和/dev/ptmx的原理可以参考TTY/PTS概述

cgroupsPath:cgroup的路径,可参考Cgroup概述

resources:Cgroup中具体子项的配置,包括device whitelist, memory, cpu, blockIO, hugepageLimits, network(net_cls cgroup和net_prio cgroup), pids

intelRdt:和Intel Resource Director Technology有关

sysctl:调整容器运行时的kernel参数,主要是一些网络参数,因为每个network namespace都有自己的协议栈,所以可以修改自己协议栈的参数而不影响别人

seccomp:和安全相关的配置,见Seccomp

rootfsPropagation:设置Propagation类型。可以参考Shared subtrees

maskedPaths:设置容器内的哪些目录对用户不可见

readonlyPaths:设置容器内的哪些目录是只读的

mountLabel:和Selinux有关。

Runtime and Lifecycle

该规范主要定义了跟容器运行时相关的三部分内容,容器的状态、容器相关的操作以及容器的生命周期。

容器的状态

当查询容器的状态时,返回的状态里面至少包含如下信息:

{
    "ociVersion": "0.2.0",
    "id": "oci-container1",
    "status": "running",
    "pid": 4422,
    "bundle": "/containers/redis",
    "annotations": {
        "myKey": "myValue"
    }
}

ociVersion (必须): 创建该容器时使用的OCI runtime的版本

id (必须): 容器ID,本机全局唯一

status (必须): 容器的运行时状态,包含如下状态:

creating: 创建中
created: 创建完成
running: 运行中
stopped: 运行结束

实现runtime时可以包含更多的状态,但不能改变这几个状态的含义

pid (容器是running状态时必须): 容器内第一个进程在系统初始pid namespace中的pid,即在容器外面看到的pid

bundle (REQUIRED): bundle所在位置的绝对路径。bundle里面包含了容器的配置文件和根文件系统。

annotations: 容器的注释,相当于容器标签,来自于容器的配置文件,key:value格式。

容器相关的操作

该部分定义了一个符合runtime标准的实现(如runc)至少需要实现下面这些命令:

state: 返回容器的状态,包含上面介绍的那些内容.

create: 创建容器,这一步执行完成后,容器创建完成,修改bundle中的config.json将不再对已创建的容器产生影响

start: 启动容器,执行config.json中process部分指定的进程

kill: 通过给容器发送信号来停止容器,信号的内容由kill命令的参数指定

delete: 删除容器,如果容器正在运行中,则删除失败。删除操作会删除掉create操作时创建的所有内容。

容器的生命周期

这里以runc为例,说明容器的生命周期

执行命令runc create创建容器,参数中指定bundle的位置以及容器的ID,容器的状态变为creating

runc根据bundle中的config.json,准备好容器运行时需要的环境和资源,但不运行process中指定的进程,这步执行完成之后,表示容器创建成功,修改config.json将不再对创建的容器产生影响,这时容器的状态变成created。

执行命令runc start启动容器

runc执行config.json中配置的prestart钩子

runc执行config.json中process指定的程序,这时容器状态变成了running

runc执行poststart钩子。

容器由于某些原因退出,比如容器中的第一个进程主动退出,挂掉或者被kill掉等。这时容器状态变成了stoped

执行命令runc delete删除容器,这时runc就会删除掉上面第2步所做的所有工作。

runc执行poststop钩子

Linux Runtime

该规范是Linux平台上对Runtime and Lifecycle的补充,目前该规范很简单,只要求容器运行起来后,里面必须建立下面这些软连接:

# ls -l /dev/fd /dev/std*
lrwxrwxrwx    1 root     root            13 May  4 12:32 /dev/fd -> /proc/self/fd
lrwxrwxrwx    1 root     root            15 May  4 12:32 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx    1 root     root            15 May  4 12:32 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx    1 root     root            15 May  4 12:32 /dev/stdout -> /proc/self/fd/1
结束语

简单点说,docker负责准备runtime的bundle,而runc负责运行该bundle,并管理容器的整个生命周期。

但对于docker来说,并不是只要准备好根文件系统和配置文件就可以了,比如对于网络,runtime没有做任何要求,只要在config.json中指定network namespace就行了(不指定就新建一个),而至于这个network namespace里面有哪些东西则完全由docker负责,docker需要保证新network namespace里面有合适的设备来和外界通信。

参考

Open Container Initiative Runtime Specification

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/26923.html

相关文章

  • 走进docker系列:开篇

    摘要:包含的内容本系列主要介绍三个上的项目由于只介绍核心的东西,所以不会包含下面这些项目使用语言开发,将多个相关的容器配置在一起,从而可以同时创建启动停止和监控它们。由于本人时间安排发生变化,本系列停止更新,后面不确定是否会继续,非常抱歉。 本人docker初学者,边学习边总结,一方面加深自己的理解,另一方面希望对其他想深入了解docker的同学有所帮助。 由于本人缺乏实战经验,错误在所难免...

    darkbug 评论0 收藏0
  • 走进docker(03):如何绕过docker运行hello-world?

    摘要:相关工具本文将用到三个工具,分别是和。根据生成的的就是运行容器时需要的东西的集合。使用运行该有了后,就可以用来运行该容器了这里直接用的代替命令,如果你自己编译了的,那么用命令也是一样的。 上一篇介绍了image的格式,这里我们就来用一下hello-world这个image,看怎么输出和docker run hello-world同样的内容。 相关工具 本文将用到三个工具,分别是skop...

    robin 评论0 收藏0
  • 走进docker(01):hello-world背后发生了什么

    摘要:进程启动后,就会按照的标准准备好相关运行时环境,然后启动进程。涉及到标准输入输出重定向,这里不细说这里是它们之间的交互流程流程对应的文字描述如下客户端发送创建容器请求给,收到请求后,发现本地没有相应的额,于是返回失败。 在程序员的世界里,hello world是个很特殊的存在,当我们接触一门新的语言、新的开发库或者框架时,第一时间想了解的一般都是怎么实现一个hello world,然后...

    cikenerd 评论0 收藏0
  • 走进docker(07):docker start命令背后发生了什么

    摘要:首先来看看收到客户端的启动容器请求后,做了些什么。待上面的文件都准备好了之后,通过的方式给发送请求,通知启动容器。主要功能是启动并管理运行时的所有。包含容器中进程相关的一些属性信息,后续在这个容器上执行命令时会用到这个文件。 在上一篇介绍过了docker create之后,这篇来看看docker start是怎么根据create之后的结果运行容器的。 启动容器 在这里我们先启动上一篇中...

    Thanatos 评论0 收藏0

发表评论

0条评论

wangxinarhat

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<