资讯专栏INFORMATION COLUMN

探索 runC (上)

Aomine / 577人阅读

摘要:当前业内比较有名的有,等。至少在笔者的主机上是这样。而第部加载,在上,就是返回一个结构。方法的实现如下第部分第部分上面的可分为两部分调用方法用创建注意第二个参数是,表示新创建的会作为新创建容器的第一个。

前言

容器运行时(Container Runtime)是指管理容器和容器镜像的软件。当前业内比较有名的有docker,rkt等。如果不同的运行时只能支持各自的容器,那么显然不利于整个容器技术的发展。于是在2015年6月,由Docker以及其他容器领域的领导者共同建立了围绕容器格式和运行时的开放的工业化标准,即Open Container Initiative(OCI),OCI具体包含两个标准:运行时标准(runtime-spec)和容器镜像标准(image-spec)。简单来说,容器镜像标准定义了容器镜像的打包形式(pack format),而运行时标准定义了如何去运行一个容器。

本文包含以下内容:

runC的概念和使用

runC运行容器的原理剖析

本文包含以下内容:

docker engine使用runC

runC概念

runC是一个遵循OCI标准的用来运行容器的命令行工具(CLI Tool),它也是一个Runtime的实现。尽管你可能对这个概念很陌生,但实际上,你的电脑上的docker底层可能正在使用它。至少在笔者的主机上是这样。

</>复制代码

  1. root@node-1:~# docker info
  2. .....
  3. Runtimes: runc
  4. Default Runtime: runc
  5. .....
安装runC

runC不仅可以被docker engine使用,它也可以多带带使用(它本身就是命令行工具),以下使用步骤完全来自runC"s README,如果

依赖项

Go version 1.6或更高版本

libseccomp库

</>复制代码

  1. yum install libseccomp-devel for CentOS
  2. apt-get install libseccomp-dev for Ubuntu

下载编译

</>复制代码

  1. # 在GOPATH/src目录创建"github.com/opencontainers"目录
  2. > cd github.com/opencontainers
  3. > git clone https://github.com/opencontainers/runc
  4. > cd runc
  5. > make
  6. > sudo make install

或者使用go get安装

</>复制代码

  1. # 在GOPATH/src目录创建github.com目录
  2. > go get github.com/opencontainers/runc
  3. > cd $GOPATH/src/github.com/opencontainers/runc
  4. > make
  5. > sudo make install

以上步骤完成后,runC将安装在/usr/local/sbin/runc目录

使用runC 创建一个OCI Bundle

OCI Bundle是指满足OCI标准的一系列文件,这些文件包含了运行容器所需要的所有数据,它们存放在一个共同的目录,该目录包含以下两项:

config.json:包含容器运行的配置数据

container 的 root filesystem

如果主机上安装了docker,那么可以使用docker export命令将已有镜像导出为OCI Bundle的格式

</>复制代码

  1. # create the top most bundle directory
  2. > mkdir /mycontainer
  3. > cd /mycontainer
  4. # create the rootfs directory
  5. > mkdir rootfs
  6. # export busybox via Docker into the rootfs directory
  7. > docker export $(docker create busybox) | tar -C rootfs -xvf -
  8. > ls rootfs
  9. bin dev etc home proc root sys tmp usr var

有了root filesystem,还需要config.json,runc spec可以生成一个基础模板,之后我们可以在模板基础上进行修改。

</>复制代码

  1. > runc spec
  2. > ls
  3. config.json rootfs

生成的config.json模板比较长,这里我将它process中的argterminal进行修改

</>复制代码

  1. {
  2. "process": {
  3. "terminal":false, <-- 这里改为 true
  4. "user": {
  5. "uid": 0,
  6. "gid": 0
  7. },
  8. "args": [
  9. "sh" <-- 这里改为 "sleep","5"
  10. ],
  11. "env": [
  12. "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  13. "TERM=xterm"
  14. ],
  15. "cwd": "/",
  16. },
  17. "root": {
  18. "path": "rootfs",
  19. "readonly": true
  20. },
  21. "linux": {
  22. "namespaces": [
  23. {
  24. "type": "pid"
  25. },
  26. {
  27. "type": "network"
  28. },
  29. {
  30. "type": "ipc"
  31. },
  32. {
  33. "type": "uts"
  34. },
  35. {
  36. "type": "mount"
  37. }
  38. ],
  39. }
  40. }

config.json 文件的内容都是 OCI Container Runtime 的订制,其中每一项值都可以在Runtime Spec找到具体含义,OCI Container Runtime 支持多种平台,因此其 Spec 也分为通用部分(在config.md中描述)以及平台相关的部分(如linux平台上就是config-linux)

process:指定容器启动后运行的进程运行环境,其中最重要的的子项就是args,它指定要运行的可执行程序, 在上面的修改后的模板中,我们将其改成了"sleep 5"

root:指定容器的根文件系统,其中path子项是指向前面导出的中root filesystem的路径

linux: 这一项是平台相关的。其中namespaces表示新创建的容器会额外创建或使用的namespace的类型

运行容器

现在我们使用create命令创建容器

</>复制代码

  1. # run as root
  2. > cd /mycontainer
  3. > runc create mycontainerid

使用list命令查看容器状态为created

</>复制代码

  1. # view the container is created and in the "created" state
  2. > runc list
  3. ID PID STATUS BUNDLE CREATED OWNER
  4. mycontainerid 12068 created /mycontainer 2018-12-25T19:45:37.346925609Z root

使用start命令查看容器状态

</>复制代码

  1. # start the process inside the container
  2. > runc start mycontainerid

在5s内 使用list命令查看容器状态为running

</>复制代码

  1. # within 5 seconds view that the container is running
  2. runc list
  3. ID PID STATUS BUNDLE CREATED OWNER
  4. mycontainerid 12068 running /mycontainer 2018-12-25T19:45:37.346925609Z root

在5s后 使用list命令查看容器状态为stopped

</>复制代码

  1. # after 5 seconds view that the container has exited and is now in the stopped state
  2. runc list
  3. ID PID STATUS BUNDLE CREATED OWNER
  4. mycontainerid 0 stopped /mycontainer 2018-12-25T19:45:37.346925609Z root

使用delete命令可以删除容器

</>复制代码

  1. # now delete the container
  2. runc delete mycontainerid
runC 实现

runC可以启动并管理符合OCI标准的容器。简单地说,runC需要利用OCI bundle创建一个独立的运行环境,并执行指定的程序。在Linux平台上,这个环境就是指各种类型的Namespace以及Capability等等配置

代码结构

runC由Go语言实现,当前(2018.12)最新版本是v1.0.0-rc6,代码的结构可分为两大块,一是根目录下的go文件,对应各个runC命令,二是负责创建/启动/管理容器的libcontainer,可以说runC的本质都在libcontainer

runc create 实现原理 (上)

以上面的例子为例,以"runc create"这条命令来看runC是如何完成从无到有创建容器,并运行用户指定的 "sleep 5" 这个进程的。

</>复制代码

  1. 创建容器,运行 sleep 5 就是我们的目标,请牢记

</>复制代码

  1. 本文涉及的调用关系如下,可随时翻阅

</>复制代码

  1. setupSpec(context)
  2. startContainer(context, spec, CT_ACT_CREATEnil)
  3. |- createContainer
  4. |- specconv.CreateLibcontainerConfig
  5. |- loadFactory(context)
  6. |- libcontainer.New(......)
  7. |- factory.Create(id, config)
  8. |- runner.run(spec.Process)
  9. |- newProcess(*config, r.init)
  10. |- r.container.Start(process)
  11. |- c.createExecFifo()
  12. |- c.start(process)
  13. |- c.newParentProcess(process)
  14. |- parent.start()

create命令的响应入口在 create.go, 我们直接关注其注册的Action的实现,当输入runc create mycontainerid时会执行注册的Action,并且参数存放在Context

</>复制代码

  1. /* run.go */
  2. Action: func(context *cli.Context) error {
  3. ......
  4.   spec, err := setupSpec(context) /* (sleep 5 在这里) */
  5.   status, err := startContainer(context, spec, CT_ACT_CREATEnil)
  6. .....
  7. }

setupSpec:从命令行输入中找到-b 指定的 OCI bundle 目录,若没有此参数,则默认是当前目录。读取config.json文件,将其中的内容转换为Go的数据结构specs.Spec,该结构定义在文件 github.com/opencontainers/runtime-spec/specs-go/config.go,里面的内容都是OCI标准描述的。

</>复制代码

  1. sleep 5 到了变量 spec

startContainer:尝试创建启动容器,注意这里的第三个参数是 CT_ACT_CREATE, 表示仅创建容器。本文使用linux平台,因此实际调用的是 utils_linux.go 中的startContainer()startContainer()根据用户将用户输入的 id 和刚才的得到的 spec 作为输入,调用 createContainer() 方法创建容器,再通过一个runner.run()方法启动它

</>复制代码

  1. /× utils_linux.go ×/
  2. func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOpts *libcontainer.CriuOpts) (int, error) {
  3. id := context.Args().First()
  4. container, err := createContainer(context, id, spec)
  5. r := &runner{
  6. container: container,
  7. action: action,
  8. init: true,
  9. ......
  10. }
  11. return r.run(spec.Process)
  12. }

这里需要先了解下runC中的几个重要数据结构的关系

Container 接口

runC中,Container用来表示一个容器对象,它是一个抽象接口,它内部包含了BaseContainer接口。从其内部的方法的名字就可以看出,都是管理容器的基本操作

</>复制代码

  1. /* libcontainer/container.go */
  2. type BaseContainer interface {
  3. ID() string
  4. Status() (Status, error)
  5. State() (*State, error)
  6. Config() configs.Config
  7. Processes() ([]int, error)
  8. Stats() (*Stats, error)
  9. Set(config configs.Config) error
  10. Start(process *Process) (err error)
  11. Run(process *Process) (err error)
  12. Destroy() error
  13. Signal(s os.Signal, all bool) error
  14. Exec() error
  15. }
  16. /* libcontainer/container_linux.go */
  17. type Container interface {
  18. BaseContainer
  19. Checkpoint(criuOpts *CriuOpts) error
  20. Restore(process *Process, criuOpts *CriuOpts) error
  21. Pause() error
  22. Resume() error
  23. NotifyOOM() (<-chan struct{}, error)
  24. NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error)
  25. }

有了抽象接口,那么一定有具体的实现,linuxContainer 就是一个实现,或者说,它是当前版本runC在linux平台上的唯一一种实现。下面是其定义,其中的 initPath 非常关键

</>复制代码

  1. type linuxContainer struct {
  2. id string
  3. config *configs.Config
  4. initPath string
  5. initArgs []string
  6. initProcess parentProcess
  7. .....
  8. }
Factory 接口

runC中,所有的容器都是由容器工厂(Factory)创建的, Factory 也是一个抽象接口,定义如下,它只包含了4个方法

</>复制代码

  1. type Factory interface {
  2. Create(id string, config *configs.Config) (Container, error)
  3. Load(id string) (Container, error)
  4. StartInitialization() error
  5. Type() string
  6. }

linux平台上的对 Factory 接口也有一个标准实现---LinuxFactory,其中的 InitPath 也非常关键,稍后我们会看到

</>复制代码

  1. // LinuxFactory implements the default factory interface for linux based systems.
  2. type LinuxFactory struct {
  3. // InitPath is the path for calling the init responsibilities for spawning
  4. // a container.
  5. InitPath string
  6. ......
  7. // InitArgs are arguments for calling the init responsibilities for spawning
  8. // a container.
  9. InitArgs []string
  10. }

所以,对于linux平台,Factory 创建 Container 实际上就是 LinuxFactory 创建 linuxContainer

回到createContainer(),下面是其实现

</>复制代码

  1. func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) {
  2. /* 1. 将配置存放到config */
  3. rootlessCg, err := shouldUseRootlessCgroupManager(context)
  4. config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{
  5. CgroupName: id,
  6. UseSystemdCgroup: context.GlobalBool("systemd-cgroup"),
  7. NoPivotRoot: context.Bool("no-pivot"),
  8. NoNewKeyring: context.Bool("no-new-keyring"),
  9. Spec: spec,
  10. RootlessEUID: os.Geteuid() != 0,
  11. RootlessCgroups: rootlessCg,
  12. })
  13. /* 2. 加载Factory */
  14. factory, err := loadFactory(context)
  15. if err != nil {
  16. return nil, err
  17. }
  18. /* 3. 调用Factory的Create()方法 */
  19. return factory.Create(id, config)
  20. }

可以看到,上面的代码大体上分为

将配置存放到 config, 数据类型是 Config.config

加载 Factory,实际返回 LinuxFactory

调用 Factory 的Create()方法

</>复制代码

  1. sleep 5 到了变量 config

第1步存放配置没什么好说的,无非是将已有的 spec 和其他一些用户命令行选项配置换成一个数据结构存下来。而第2部加载Factory,在linux上,就是返回一个 LinuxFactory 结构。而这是通过在其内部调用 libcontainer.New()方法实现的

</>复制代码

  1. /* utils/utils_linux.go */
  2. func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
  3. .....
  4. return libcontainer.New(abs, cgroupManager, intelRdtManager,
  5. libcontainer.CriuPath(context.GlobalString("criu")),
  6. libcontainer.NewuidmapPath(newuidmap),
  7. libcontainer.NewgidmapPath(newgidmap))
  8. }

libcontainer.New() 方法在linux平台的实现如下,可以看到,它的确会返回一个LinuxFactory,并且InitPath设置为"/proc/self/exe",InitArgs设置为"init"

</>复制代码

  1. /* libcontainer/factory_linux.go */
  2. func New(root string, options ...func(*LinuxFactory) error) (Factory, error) {
  3. .....
  4. l := &LinuxFactory{
  5. .....
  6. InitPath: "/proc/self/exe",
  7. InitArgs: []string{os.Args[0], "init"},
  8. }
  9. ......
  10. return l, nil
  11. }

得到了具体的 Factory 实现,下一步就是调用其Create()方法,对 linux 平台而言,就是下面这个方法,可以看到,它会将 LinuxFactory 上记录的 InitPathInitArgs 赋给 linuxContainer 并作为结果返回

</>复制代码

  1. func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) {
  2. ....
  3. c := &linuxContainer{
  4. id: id,
  5. config: config,
  6. initPath: l.InitPath,
  7. initArgs: l.InitArgs,
  8. }
  9. .....
  10. return c, nil
  11. }

回到 startContainer() 方法,再得到 linuxContainer 后,将创建一个 runner 结构,并调用其run()方法

</>复制代码

  1. /* utils_linux.go */
  2. func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOpts *libcontainer.CriuOpts) (int, error) {
  3. id := context.Args().First()
  4. container, err := createContainer(context, id, spec)
  5. r := &runner{
  6. container: container,
  7. action: action,
  8. init: true,
  9. ......
  10. }
  11. return r.run(spec.Process)
  12. }

runnerrun() 的入参是 spec.Process 结构,我们并不需要关注它的定义,因为它的内容都来源于 config.json 文件,spec.Process 不过是其中 Process 部分的 Go 语言数据的表示。run() 方法的实现如下:

</>复制代码

  1. func (r *runner) run(config *specs.Process) (int, error) {
  2. ......
  3. process, err := newProcess(*config, r.init) /* 第1部分 */
  4. ......
  5. switch r.action {
  6. case CT_ACT_CREATE:
  7. err = r.container.Start(process) /* runc start */ /* 第2部分 */
  8. case CT_ACT_RESTORE:
  9. err = r.container.Restore(process, r.criuOpts) /* runc restore */
  10. case CT_ACT_RUN:
  11. err = r.container.Run(process) /* runc run */
  12. default:
  13. panic("Unknown action")
  14. }
  15. ......
  16. return status, err
  17. }

上面的 run() 可分为两部分

调用 newProcess() 方法, 用 spec.Process 创建 libcontainer.Process,注意第二个参数是 true ,表示新创建的 process 会作为新创建容器的第一个 process

根据 r.action 的值决定如何操作得到的 libcontainer.Process

</>复制代码

  1. sleep 5 到了变量 process

libcontainer.Process 结构定义在 /libcontainer/process.go, 其中大部分内容都来自 spec.Process

</>复制代码

  1. /* parent process */
  2. // Process specifies the configuration and IO for a process inside
  3. // a container.
  4. type Process struct {
  5. Args []string
  6. Env []string
  7. User string
  8. AdditionalGroups []string
  9. Cwd string
  10. Stdin io.Reader
  11. Stdout io.Writer
  12. Stderr io.Writer
  13. ExtraFiles []*os.File
  14. ConsoleWidth uint16
  15. ConsoleHeight uint16
  16. Capabilities *configs.Capabilities
  17. AppArmorProfile string
  18. Label string
  19. NoNewPrivileges *bool
  20. Rlimits []configs.Rlimit
  21. ConsoleSocket *os.File
  22. Init bool
  23. ops processOperations
  24. }

接下来就是要使用 Start() 方法了

</>复制代码

  1. func (c *linuxContainer) Start(process *Process) error {
  2. if process.Init {
  3. if err := c.createExecFifo(); err != nil { /* 1.创建fifo */
  4. return err
  5. }
  6. }
  7. if err := c.start(process); err != nil { /* 2. 调用start() */
  8. if process.Init {
  9. c.deleteExecFifo()
  10. }
  11. return err
  12. }
  13. return nil
  14. }

Start() 方法主要完成两件事

创建 fifo: 创建一个名为exec.fifo的管道,这个管道后面会用到

调用 start() 方法,如下

</>复制代码

  1. func (c *linuxContainer) start(process *Process) error {
  2. parent, err := c.newParentProcess(process) /* 1. 创建parentProcess */
  3. err := parent.start(); /* 2. 启动这个parentProcess */
  4. ......

start() 也完成两件事:

创建一个 ParentProcess

调用这个 ParentProcessstart() 方法

</>复制代码

  1. sleep 5 到了变量 parent

那么什么是 parentProcess ? 正如其名,parentProcess 类似于 linux 中可以派生出子进程的父进程,在runC中,parentProcess 是一个抽象接口,如下:

</>复制代码

  1. type parentProcess interface {
  2. // pid returns the pid for the running process.
  3. pid() int
  4. // start starts the process execution.
  5. start() error
  6. // send a SIGKILL to the process and wait for the exit.
  7. terminate() error
  8. // wait waits on the process returning the process state.
  9. wait() (*os.ProcessState, error)
  10. // startTime returns the process start time.
  11. startTime() (uint64, error)
  12. signal(os.Signal) error
  13. externalDescriptors() []string
  14. setExternalDescriptors(fds []string)
  15. }

它有两个实现,分别为 initProcesssetnsProcess ,前者用于创建容器内的第一个进程,后者用于在已有容器内创建新的进程。在我们的创建容器例子中,p.Init = true ,所以会创建 initProcess

</>复制代码

  1. func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) {
  2. parentPipe, childPipe, err := utils.NewSockPair("init") /* 1.创建 Socket Pair */
  3. cmd, err := c.commandTemplate(p, childPipe) /* 2. 创建 *exec.Cmd */
  4. if !p.Init {
  5. return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
  6. }
  7. if err := c.includeExecFifo(cmd); err != nil { /* 3.打开之前创建的fifo */
  8. return nil, newSystemErrorWithCause(err, "including execfifo in cmd.Exec setup")
  9. }
  10. return c.newInitProcess(p, cmd, parentPipe, childPipe) /* 4.创建 initProcess */
  11. }

newParentProcess() 方法动作有 4 步,前 3 步都是在为第 4 步做准备,即生成 initProcess

创建一对 SocketPair 没什么好说的,生成的结果会放到 initProcess

创建 *exec.Cmd,代码如下,这里设置了 cmd 要执行的可执行程序和参数来自 c.initPath,即源自 LinuxFactory 的 "/proc/self/exe",和 "init" ,这表示新执行的程序就是runC本身,只是参数变成了 init,之后又将外面创建的 SocketPair 的一端 childPipe放到了cmd.ExtraFiles ,同时将_LIBCONTAINER_INITPIPE=%d加入cmd.Env,其中 %d为文件描述符的数字

</>复制代码

  1. func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) {
  2. cmd := exec.Command(c.initPath, c.initArgs[1:]...)
  3. cmd.Args[0] = c.initArgs[0]
  4. cmd.ExtraFiles = append(cmd.ExtraFiles, p.ExtraFiles...)
  5. cmd.ExtraFiles = append(cmd.ExtraFiles, childPipe)
  6. cmd.Env = append(cmd.Env,
  7. fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1),
  8. )
  9. ......
  10. return cmd, nil
  11. }

includeExecFifo() 方法打开之前创建的 fifo,也将其 fd 放到 cmd.ExtraFiles 中,同时将_LIBCONTAINER_FIFOFD=%d记录到 cmd.Env

最后就是创建 InitProcess 了,这里首先将_LIBCONTAINER_INITTYPE="standard"加入cmd.Env,然后从 configs 读取需要新的容器创建的 Namespace 的类型,并将其打包到变量 data 中备用,最后再创建 InitProcess 自己,可以看到,这里将之前的一些资源和变量都联系了起来

</>复制代码

  1. func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) {
  2. cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
  3. nsMaps := make(map[configs.NamespaceType]string)
  4. for _, ns := range c.config.Namespaces {
  5. if ns.Path != "" {
  6. nsMaps[ns.Type] = ns.Path
  7. }
  8. }
  9. _, sharePidns := nsMaps[configs.NEWPID]
  10. data, err := c.bootstrapData(c.config.Namespaces.CloneFlags(), nsMaps)
  11. if err != nil {
  12. return nil, err
  13. }
  14. return &initProcess{
  15. cmd: cmd,
  16. childPipe: childPipe,
  17. parentPipe: parentPipe,
  18. manager: c.cgroupManager,
  19. intelRdtManager: c.intelRdtManager,
  20. config: c.newInitConfig(p),
  21. container: c,
  22. process: p, /* sleep 5 在这里 */
  23. bootstrapData: data,
  24. sharePidns: sharePidns,
  25. }, nil
  26. }

</>复制代码

  1. sleep 5 在 initProcess.process 中

回到 linuxContainerstart() 方法,创建好了 parent ,下一步就是调用它的 start() 方法了

</>复制代码

  1. func (c *linuxContainer) start(process *Process) error {
  2. parent, err := c.newParentProcess(process) /* 1. 创建parentProcess (已完成) */
  3. err := parent.start(); /* 2. 启动这个parentProcess */
  4. ......

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

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

相关文章

  • 探索 runC ()

    摘要:当前业内比较有名的有,等。至少在笔者的主机上是这样。而第部加载,在上,就是返回一个结构。方法的实现如下第部分第部分上面的可分为两部分调用方法用创建注意第二个参数是,表示新创建的会作为新创建容器的第一个。 前言 容器运行时(Container Runtime)是指管理容器和容器镜像的软件。当前业内比较有名的有docker,rkt等。如果不同的运行时只能支持各自的容器,那么显然不利于整个容...

    yanest 评论0 收藏0
  • 探索runC (下)

    摘要:而不幸的是是多线程的。至此,子进程就从父进程处得到了的配置,继续往下,又创建了两个从注释中了解到,这是为了和它自己的子进程和孙进程进行通信。 回顾 本文接 探索runC(上) 前文讲到,newParentProcess() 根据源自 config.json 的配置,最终生成变量 initProcess ,这个 initProcess 包含的信息主要有 cmd 记录了要执行的可执行...

    gekylin 评论0 收藏0
  • 探索runC (下)

    摘要:而不幸的是是多线程的。至此,子进程就从父进程处得到了的配置,继续往下,又创建了两个从注释中了解到,这是为了和它自己的子进程和孙进程进行通信。 回顾 本文接 探索runC(上) 前文讲到,newParentProcess() 根据源自 config.json 的配置,最终生成变量 initProcess ,这个 initProcess 包含的信息主要有 cmd 记录了要执行的可执行...

    jzman 评论0 收藏0
  • runc容器逃逸漏洞最强后续:应对之策汇总与热点疑问解答

    摘要:年月日,研究人员通过邮件列表披露了容器逃逸漏洞的详情,根据的规定会在天后也就是年月日公开。在号当天已通过公众号文章详细分析了漏洞详情和用户的应对之策。 美国时间2019年2月11日晚,runc通过oss-security邮件列表披露了runc容器逃逸漏洞CVE-2019-5736的详情。runc是Docker、CRI-O、Containerd、Kubernetes等底层的容器运行时,此...

    PingCAP 评论0 收藏0
  • runc 1.0-rc7 发布之际

    摘要:在年月底时,我写了一篇文章发布之际。为何有存在前面已经基本介绍了相关背景,并且也基本明确了就是在正式发布之前的最后一个版本,那为什么会出现呢我们首先要介绍今年的一个提权漏洞。 在 18 年 11 月底时,我写了一篇文章 《runc 1.0-rc6 发布之际》 。如果你还不了解 runc 是什么,以及如何使用它,请参考我那篇文章。本文中,不再对其概念和用法等进行说明。 在 runc 1....

    zhunjiee 评论0 收藏0

发表评论

0条评论

Aomine

|高级讲师

TA的文章

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