摘要:當(dāng)前業(yè)內(nèi)比較有名的有,等。至少在筆者的主機(jī)上是這樣。而第部加載,在上,就是返回一個(gè)結(jié)構(gòu)。方法的實(shí)現(xiàn)如下第部分第部分上面的可分為兩部分調(diào)用方法用創(chuàng)建注意第二個(gè)參數(shù)是,表示新創(chuàng)建的會(huì)作為新創(chuàng)建容器的第一個(gè)。
前言
容器運(yùn)行時(shí)(Container Runtime)是指管理容器和容器鏡像的軟件。當(dāng)前業(yè)內(nèi)比較有名的有docker,rkt等。如果不同的運(yùn)行時(shí)只能支持各自的容器,那么顯然不利于整個(gè)容器技術(shù)的發(fā)展。于是在2015年6月,由Docker以及其他容器領(lǐng)域的領(lǐng)導(dǎo)者共同建立了圍繞容器格式和運(yùn)行時(shí)的開放的工業(yè)化標(biāo)準(zhǔn),即Open Container Initiative(OCI),OCI具體包含兩個(gè)標(biāo)準(zhǔn):運(yùn)行時(shí)標(biāo)準(zhǔn)(runtime-spec)和容器鏡像標(biāo)準(zhǔn)(image-spec)。簡(jiǎn)單來說,容器鏡像標(biāo)準(zhǔn)定義了容器鏡像的打包形式(pack format),而運(yùn)行時(shí)標(biāo)準(zhǔn)定義了如何去運(yùn)行一個(gè)容器。
本文包含以下內(nèi)容:
runC的概念和使用
runC運(yùn)行容器的原理剖析
本文不包含以下內(nèi)容:
docker engine使用runC
runC概念runC是一個(gè)遵循OCI標(biāo)準(zhǔn)的用來運(yùn)行容器的命令行工具(CLI Tool),它也是一個(gè)Runtime的實(shí)現(xiàn)。盡管你可能對(duì)這個(gè)概念很陌生,但實(shí)際上,你的電腦上的docker底層可能正在使用它。至少在筆者的主機(jī)上是這樣。
root@node-1:~# docker info ..... Runtimes: runc Default Runtime: runc .....安裝runC
runC不僅可以被docker engine使用,它也可以多帶帶使用(它本身就是命令行工具),以下使用步驟完全來自runC"s README,如果
依賴項(xiàng)Go version 1.6或更高版本
libseccomp庫
yum install libseccomp-devel for CentOS apt-get install libseccomp-dev for Ubuntu下載編譯
# 在GOPATH/src目錄創(chuàng)建"github.com/opencontainers"目錄 > cd github.com/opencontainers > git clone https://github.com/opencontainers/runc > cd runc > make > sudo make install
或者使用go get安裝
# 在GOPATH/src目錄創(chuàng)建github.com目錄 > go get github.com/opencontainers/runc > cd $GOPATH/src/github.com/opencontainers/runc > make > sudo make install
以上步驟完成后,runC將安裝在/usr/local/sbin/runc目錄
使用runC 創(chuàng)建一個(gè)OCI BundleOCI Bundle是指滿足OCI標(biāo)準(zhǔn)的一系列文件,這些文件包含了運(yùn)行容器所需要的所有數(shù)據(jù),它們存放在一個(gè)共同的目錄,該目錄包含以下兩項(xiàng):
config.json:包含容器運(yùn)行的配置數(shù)據(jù)
container 的 root filesystem
如果主機(jī)上安裝了docker,那么可以使用docker export命令將已有鏡像導(dǎo)出為OCI Bundle的格式
# create the top most bundle directory > mkdir /mycontainer > cd /mycontainer # create the rootfs directory > mkdir rootfs # export busybox via Docker into the rootfs directory > docker export $(docker create busybox) | tar -C rootfs -xvf - > ls rootfs bin dev etc home proc root sys tmp usr var
有了root filesystem,還需要config.json,runc spec可以生成一個(gè)基礎(chǔ)模板,之后我們可以在模板基礎(chǔ)上進(jìn)行修改。
> runc spec > ls config.json rootfs
生成的config.json模板比較長(zhǎng),這里我將它process中的arg 和 terminal進(jìn)行修改
{ "process": { "terminal":false, <-- 這里改為 true "user": { "uid": 0, "gid": 0 }, "args": [ "sh" <-- 這里改為 "sleep","5" ], "env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm" ], "cwd": "/", }, "root": { "path": "rootfs", "readonly": true }, "linux": { "namespaces": [ { "type": "pid" }, { "type": "network" }, { "type": "ipc" }, { "type": "uts" }, { "type": "mount" } ], } }
config.json 文件的內(nèi)容都是 OCI Container Runtime 的訂制,其中每一項(xiàng)值都可以在Runtime Spec找到具體含義,OCI Container Runtime 支持多種平臺(tái),因此其 Spec 也分為通用部分(在config.md中描述)以及平臺(tái)相關(guān)的部分(如linux平臺(tái)上就是config-linux)
process:指定容器啟動(dòng)后運(yùn)行的進(jìn)程運(yùn)行環(huán)境,其中最重要的的子項(xiàng)就是args,它指定要運(yùn)行的可執(zhí)行程序, 在上面的修改后的模板中,我們將其改成了"sleep 5"
root:指定容器的根文件系統(tǒng),其中path子項(xiàng)是指向前面導(dǎo)出的中root filesystem的路徑
linux: 這一項(xiàng)是平臺(tái)相關(guān)的。其中namespaces表示新創(chuàng)建的容器會(huì)額外創(chuàng)建或使用的namespace的類型
運(yùn)行容器現(xiàn)在我們使用create命令創(chuàng)建容器
# run as root > cd /mycontainer > runc create mycontainerid
使用list命令查看容器狀態(tài)為created
# view the container is created and in the "created" state > runc list ID PID STATUS BUNDLE CREATED OWNER mycontainerid 12068 created /mycontainer 2018-12-25T19:45:37.346925609Z root
使用start命令查看容器狀態(tài)
# start the process inside the container > runc start mycontainerid
在5s內(nèi) 使用list命令查看容器狀態(tài)為running
# within 5 seconds view that the container is running runc list ID PID STATUS BUNDLE CREATED OWNER mycontainerid 12068 running /mycontainer 2018-12-25T19:45:37.346925609Z root
在5s后 使用list命令查看容器狀態(tài)為stopped
# after 5 seconds view that the container has exited and is now in the stopped state runc list ID PID STATUS BUNDLE CREATED OWNER mycontainerid 0 stopped /mycontainer 2018-12-25T19:45:37.346925609Z root
使用delete命令可以刪除容器
# now delete the container runc delete mycontaineridrunC 實(shí)現(xiàn)
runC可以啟動(dòng)并管理符合OCI標(biāo)準(zhǔn)的容器。簡(jiǎn)單地說,runC需要利用OCI bundle創(chuàng)建一個(gè)獨(dú)立的運(yùn)行環(huán)境,并執(zhí)行指定的程序。在Linux平臺(tái)上,這個(gè)環(huán)境就是指各種類型的Namespace以及Capability等等配置
代碼結(jié)構(gòu)runC由Go語言實(shí)現(xiàn),當(dāng)前(2018.12)最新版本是v1.0.0-rc6,代碼的結(jié)構(gòu)可分為兩大塊,一是根目錄下的go文件,對(duì)應(yīng)各個(gè)runC命令,二是負(fù)責(zé)創(chuàng)建/啟動(dòng)/管理容器的libcontainer,可以說runC的本質(zhì)都在libcontainer
以上面的例子為例,以"runc create"這條命令來看runC是如何完成從無到有創(chuàng)建容器,并運(yùn)行用戶指定的 "sleep 5" 這個(gè)進(jìn)程的。
創(chuàng)建容器,運(yùn)行 sleep 5 就是我們的目標(biāo),請(qǐng)牢記
本文涉及的調(diào)用關(guān)系如下,可隨時(shí)翻閱
setupSpec(context) startContainer(context,?spec,?CT_ACT_CREATE,?nil) |- createContainer |- specconv.CreateLibcontainerConfig |- loadFactory(context) |- libcontainer.New(......) |- factory.Create(id, config) |- runner.run(spec.Process) |- newProcess(*config, r.init) |- r.container.Start(process) |- c.createExecFifo() |- c.start(process) |- c.newParentProcess(process) |- parent.start()
create命令的響應(yīng)入口在 create.go, 我們直接關(guān)注其注冊(cè)的Action的實(shí)現(xiàn),當(dāng)輸入runc create mycontainerid時(shí)會(huì)執(zhí)行注冊(cè)的Action,并且參數(shù)存放在Context中
/* run.go */ Action:?func(context?*cli.Context)?error?{ ...... ? spec,?err?:=?setupSpec(context) /* (sleep 5 在這里) */ ??status,?err?:=?startContainer(context,?spec,?CT_ACT_CREATE,?nil) ..... }
setupSpec:從命令行輸入中找到-b 指定的 OCI bundle 目錄,若沒有此參數(shù),則默認(rèn)是當(dāng)前目錄。讀取config.json文件,將其中的內(nèi)容轉(zhuǎn)換為Go的數(shù)據(jù)結(jié)構(gòu)specs.Spec,該結(jié)構(gòu)定義在文件 github.com/opencontainers/runtime-spec/specs-go/config.go,里面的內(nèi)容都是OCI標(biāo)準(zhǔn)描述的。
sleep 5 到了變量 spec
startContainer:嘗試創(chuàng)建啟動(dòng)容器,注意這里的第三個(gè)參數(shù)是 CT_ACT_CREATE, 表示僅創(chuàng)建容器。本文使用linux平臺(tái),因此實(shí)際調(diào)用的是 utils_linux.go 中的startContainer()。startContainer()根據(jù)用戶將用戶輸入的 id 和剛才的得到的 spec 作為輸入,調(diào)用 createContainer() 方法創(chuàng)建容器,再通過一個(gè)runner.run()方法啟動(dòng)它
/× utils_linux.go ×/ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOpts *libcontainer.CriuOpts) (int, error) { id := context.Args().First() container, err := createContainer(context, id, spec) r := &runner{ container: container, action: action, init: true, ...... } return r.run(spec.Process) }
這里需要先了解下runC中的幾個(gè)重要數(shù)據(jù)結(jié)構(gòu)的關(guān)系
在runC中,Container用來表示一個(gè)容器對(duì)象,它是一個(gè)抽象接口,它內(nèi)部包含了BaseContainer接口。從其內(nèi)部的方法的名字就可以看出,都是管理容器的基本操作
/* libcontainer/container.go */ type BaseContainer interface { ID() string Status() (Status, error) State() (*State, error) Config() configs.Config Processes() ([]int, error) Stats() (*Stats, error) Set(config configs.Config) error Start(process *Process) (err error) Run(process *Process) (err error) Destroy() error Signal(s os.Signal, all bool) error Exec() error } /* libcontainer/container_linux.go */ type Container interface { BaseContainer Checkpoint(criuOpts *CriuOpts) error Restore(process *Process, criuOpts *CriuOpts) error Pause() error Resume() error NotifyOOM() (<-chan struct{}, error) NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) }
有了抽象接口,那么一定有具體的實(shí)現(xiàn),linuxContainer 就是一個(gè)實(shí)現(xiàn),或者說,它是當(dāng)前版本runC在linux平臺(tái)上的唯一一種實(shí)現(xiàn)。下面是其定義,其中的 initPath 非常關(guān)鍵
type linuxContainer struct { id string config *configs.Config initPath string initArgs []string initProcess parentProcess ..... }
在runC中,所有的容器都是由容器工廠(Factory)創(chuàng)建的, Factory 也是一個(gè)抽象接口,定義如下,它只包含了4個(gè)方法
type Factory interface { Create(id string, config *configs.Config) (Container, error) Load(id string) (Container, error) StartInitialization() error Type() string }
linux平臺(tái)上的對(duì) Factory 接口也有一個(gè)標(biāo)準(zhǔn)實(shí)現(xiàn)---LinuxFactory,其中的 InitPath 也非常關(guān)鍵,稍后我們會(huì)看到
// LinuxFactory implements the default factory interface for linux based systems. type LinuxFactory struct { // InitPath is the path for calling the init responsibilities for spawning // a container. InitPath string ...... // InitArgs are arguments for calling the init responsibilities for spawning // a container. InitArgs []string }
所以,對(duì)于linux平臺(tái),Factory 創(chuàng)建 Container 實(shí)際上就是 LinuxFactory 創(chuàng)建 linuxContainer
回到createContainer(),下面是其實(shí)現(xiàn)
func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) { /* 1. 將配置存放到config */ rootlessCg, err := shouldUseRootlessCgroupManager(context) config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{ CgroupName: id, UseSystemdCgroup: context.GlobalBool("systemd-cgroup"), NoPivotRoot: context.Bool("no-pivot"), NoNewKeyring: context.Bool("no-new-keyring"), Spec: spec, RootlessEUID: os.Geteuid() != 0, RootlessCgroups: rootlessCg, }) /* 2. 加載Factory */ factory, err := loadFactory(context) if err != nil { return nil, err } /* 3. 調(diào)用Factory的Create()方法 */ return factory.Create(id, config) }
可以看到,上面的代碼大體上分為
將配置存放到 config, 數(shù)據(jù)類型是 Config.config
加載 Factory,實(shí)際返回 LinuxFactory
調(diào)用 Factory 的Create()方法
sleep 5 到了變量 config
第1步存放配置沒什么好說的,無非是將已有的 spec 和其他一些用戶命令行選項(xiàng)配置換成一個(gè)數(shù)據(jù)結(jié)構(gòu)存下來。而第2部加載Factory,在linux上,就是返回一個(gè) LinuxFactory 結(jié)構(gòu)。而這是通過在其內(nèi)部調(diào)用 libcontainer.New()方法實(shí)現(xiàn)的
/* utils/utils_linux.go */ func loadFactory(context *cli.Context) (libcontainer.Factory, error) { ..... return libcontainer.New(abs, cgroupManager, intelRdtManager, libcontainer.CriuPath(context.GlobalString("criu")), libcontainer.NewuidmapPath(newuidmap), libcontainer.NewgidmapPath(newgidmap)) }
libcontainer.New() 方法在linux平臺(tái)的實(shí)現(xiàn)如下,可以看到,它的確會(huì)返回一個(gè)LinuxFactory,并且InitPath設(shè)置為"/proc/self/exe",InitArgs設(shè)置為"init"
/* libcontainer/factory_linux.go */ func New(root string, options ...func(*LinuxFactory) error) (Factory, error) { ..... l := &LinuxFactory{ ..... InitPath: "/proc/self/exe", InitArgs: []string{os.Args[0], "init"}, } ...... return l, nil }
得到了具體的 Factory 實(shí)現(xiàn),下一步就是調(diào)用其Create()方法,對(duì) linux 平臺(tái)而言,就是下面這個(gè)方法,可以看到,它會(huì)將 LinuxFactory 上記錄的 InitPath 和 InitArgs 賦給 linuxContainer 并作為結(jié)果返回
func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) { .... c := &linuxContainer{ id: id, config: config, initPath: l.InitPath, initArgs: l.InitArgs, } ..... return c, nil }
回到 startContainer() 方法,再得到 linuxContainer 后,將創(chuàng)建一個(gè) runner 結(jié)構(gòu),并調(diào)用其run()方法
/* utils_linux.go */ func startContainer(context *cli.Context, spec *specs.Spec, action CtAct, criuOpts *libcontainer.CriuOpts) (int, error) { id := context.Args().First() container, err := createContainer(context, id, spec) r := &runner{ container: container, action: action, init: true, ...... } return r.run(spec.Process) }
runner 的 run() 的入?yún)⑹?spec.Process 結(jié)構(gòu),我們并不需要關(guān)注它的定義,因?yàn)樗膬?nèi)容都來源于 config.json 文件,spec.Process 不過是其中 Process 部分的 Go 語言數(shù)據(jù)的表示。run() 方法的實(shí)現(xiàn)如下:
func (r *runner) run(config *specs.Process) (int, error) { ...... process, err := newProcess(*config, r.init) /* 第1部分 */ ...... switch r.action { case CT_ACT_CREATE: err = r.container.Start(process) /* runc start */ /* 第2部分 */ case CT_ACT_RESTORE: err = r.container.Restore(process, r.criuOpts) /* runc restore */ case CT_ACT_RUN: err = r.container.Run(process) /* runc run */ default: panic("Unknown action") } ...... return status, err }
上面的 run() 可分為兩部分
調(diào)用 newProcess() 方法, 用 spec.Process 創(chuàng)建 libcontainer.Process,注意第二個(gè)參數(shù)是 true ,表示新創(chuàng)建的 process 會(huì)作為新創(chuàng)建容器的第一個(gè) process。
根據(jù) r.action 的值決定如何操作得到的 libcontainer.Process
sleep 5 到了變量 process
libcontainer.Process 結(jié)構(gòu)定義在 /libcontainer/process.go, 其中大部分內(nèi)容都來自 spec.Process
/* parent process */ // Process specifies the configuration and IO for a process inside // a container. type Process struct { Args []string Env []string User string AdditionalGroups []string Cwd string Stdin io.Reader Stdout io.Writer Stderr io.Writer ExtraFiles []*os.File ConsoleWidth uint16 ConsoleHeight uint16 Capabilities *configs.Capabilities AppArmorProfile string Label string NoNewPrivileges *bool Rlimits []configs.Rlimit ConsoleSocket *os.File Init bool ops processOperations }
接下來就是要使用 Start() 方法了
func (c *linuxContainer) Start(process *Process) error { if process.Init { if err := c.createExecFifo(); err != nil { /* 1.創(chuàng)建fifo */ return err } } if err := c.start(process); err != nil { /* 2. 調(diào)用start() */ if process.Init { c.deleteExecFifo() } return err } return nil }
Start() 方法主要完成兩件事
創(chuàng)建 fifo: 創(chuàng)建一個(gè)名為exec.fifo的管道,這個(gè)管道后面會(huì)用到
調(diào)用 start() 方法,如下
func (c *linuxContainer) start(process *Process) error { parent, err := c.newParentProcess(process) /* 1. 創(chuàng)建parentProcess */ err := parent.start(); /* 2. 啟動(dòng)這個(gè)parentProcess */ ......
start() 也完成兩件事:
創(chuàng)建一個(gè) ParentProcess
調(diào)用這個(gè) ParentProcess 的 start() 方法
sleep 5 到了變量 parent
那么什么是 parentProcess ? 正如其名,parentProcess 類似于 linux 中可以派生出子進(jìn)程的父進(jìn)程,在runC中,parentProcess 是一個(gè)抽象接口,如下:
type parentProcess interface { // pid returns the pid for the running process. pid() int // start starts the process execution. start() error // send a SIGKILL to the process and wait for the exit. terminate() error // wait waits on the process returning the process state. wait() (*os.ProcessState, error) // startTime returns the process start time. startTime() (uint64, error) signal(os.Signal) error externalDescriptors() []string setExternalDescriptors(fds []string) }
它有兩個(gè)實(shí)現(xiàn),分別為 initProcess 和 setnsProcess ,前者用于創(chuàng)建容器內(nèi)的第一個(gè)進(jìn)程,后者用于在已有容器內(nèi)創(chuàng)建新的進(jìn)程。在我們的創(chuàng)建容器例子中,p.Init = true ,所以會(huì)創(chuàng)建 initProcess
func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) { parentPipe, childPipe, err := utils.NewSockPair("init") /* 1.創(chuàng)建 Socket Pair */ cmd, err := c.commandTemplate(p, childPipe) /* 2. 創(chuàng)建 *exec.Cmd */ if !p.Init { return c.newSetnsProcess(p, cmd, parentPipe, childPipe) } if err := c.includeExecFifo(cmd); err != nil { /* 3.打開之前創(chuàng)建的fifo */ return nil, newSystemErrorWithCause(err, "including execfifo in cmd.Exec setup") } return c.newInitProcess(p, cmd, parentPipe, childPipe) /* 4.創(chuàng)建 initProcess */ }
newParentProcess() 方法動(dòng)作有 4 步,前 3 步都是在為第 4 步做準(zhǔn)備,即生成 initProcess
創(chuàng)建一對(duì) SocketPair 沒什么好說的,生成的結(jié)果會(huì)放到 initProcess
創(chuàng)建 *exec.Cmd,代碼如下,這里設(shè)置了 cmd 要執(zhí)行的可執(zhí)行程序和參數(shù)來自 c.initPath,即源自 LinuxFactory 的 "/proc/self/exe",和 "init" ,這表示新執(zhí)行的程序就是runC本身,只是參數(shù)變成了 init,之后又將外面創(chuàng)建的 SocketPair 的一端 childPipe放到了cmd.ExtraFiles ,同時(shí)將_LIBCONTAINER_INITPIPE=%d加入cmd.Env,其中 %d為文件描述符的數(shù)字
func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.Cmd, error) { cmd := exec.Command(c.initPath, c.initArgs[1:]...) cmd.Args[0] = c.initArgs[0] cmd.ExtraFiles = append(cmd.ExtraFiles, p.ExtraFiles...) cmd.ExtraFiles = append(cmd.ExtraFiles, childPipe) cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1), ) ...... return cmd, nil }
includeExecFifo() 方法打開之前創(chuàng)建的 fifo,也將其 fd 放到 cmd.ExtraFiles 中,同時(shí)將_LIBCONTAINER_FIFOFD=%d記錄到 cmd.Env。
最后就是創(chuàng)建 InitProcess 了,這里首先將_LIBCONTAINER_INITTYPE="standard"加入cmd.Env,然后從 configs 讀取需要新的容器創(chuàng)建的 Namespace 的類型,并將其打包到變量 data 中備用,最后再創(chuàng)建 InitProcess 自己,可以看到,這里將之前的一些資源和變量都聯(lián)系了起來
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) { cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard)) nsMaps := make(map[configs.NamespaceType]string) for _, ns := range c.config.Namespaces { if ns.Path != "" { nsMaps[ns.Type] = ns.Path } } _, sharePidns := nsMaps[configs.NEWPID] data, err := c.bootstrapData(c.config.Namespaces.CloneFlags(), nsMaps) if err != nil { return nil, err } return &initProcess{ cmd: cmd, childPipe: childPipe, parentPipe: parentPipe, manager: c.cgroupManager, intelRdtManager: c.intelRdtManager, config: c.newInitConfig(p), container: c, process: p, /* sleep 5 在這里 */ bootstrapData: data, sharePidns: sharePidns, }, nil }
sleep 5 在 initProcess.process 中
回到 linuxContainer 的 start() 方法,創(chuàng)建好了 parent ,下一步就是調(diào)用它的 start() 方法了
func (c *linuxContainer) start(process *Process) error { parent, err := c.newParentProcess(process) /* 1. 創(chuàng)建parentProcess (已完成) */ err := parent.start(); /* 2. 啟動(dòng)這個(gè)parentProcess */ ......
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/27650.html
摘要:當(dāng)前業(yè)內(nèi)比較有名的有,等。至少在筆者的主機(jī)上是這樣。而第部加載,在上,就是返回一個(gè)結(jié)構(gòu)。方法的實(shí)現(xiàn)如下第部分第部分上面的可分為兩部分調(diào)用方法用創(chuàng)建注意第二個(gè)參數(shù)是,表示新創(chuàng)建的會(huì)作為新創(chuàng)建容器的第一個(gè)。 前言 容器運(yùn)行時(shí)(Container Runtime)是指管理容器和容器鏡像的軟件。當(dāng)前業(yè)內(nèi)比較有名的有docker,rkt等。如果不同的運(yùn)行時(shí)只能支持各自的容器,那么顯然不利于整個(gè)容...
摘要:而不幸的是是多線程的。至此,子進(jìn)程就從父進(jìn)程處得到了的配置,繼續(xù)往下,又創(chuàng)建了兩個(gè)從注釋中了解到,這是為了和它自己的子進(jìn)程和孫進(jìn)程進(jìn)行通信。 回顧 本文接 探索runC(上) 前文講到,newParentProcess() 根據(jù)源自 config.json 的配置,最終生成變量 initProcess ,這個(gè) initProcess 包含的信息主要有 cmd 記錄了要執(zhí)行的可執(zhí)行...
摘要:而不幸的是是多線程的。至此,子進(jìn)程就從父進(jìn)程處得到了的配置,繼續(xù)往下,又創(chuàng)建了兩個(gè)從注釋中了解到,這是為了和它自己的子進(jìn)程和孫進(jìn)程進(jìn)行通信。 回顧 本文接 探索runC(上) 前文講到,newParentProcess() 根據(jù)源自 config.json 的配置,最終生成變量 initProcess ,這個(gè) initProcess 包含的信息主要有 cmd 記錄了要執(zhí)行的可執(zhí)行...
摘要:年月日,研究人員通過郵件列表披露了容器逃逸漏洞的詳情,根據(jù)的規(guī)定會(huì)在天后也就是年月日公開。在號(hào)當(dāng)天已通過公眾號(hào)文章詳細(xì)分析了漏洞詳情和用戶的應(yīng)對(duì)之策。 美國(guó)時(shí)間2019年2月11日晚,runc通過oss-security郵件列表披露了runc容器逃逸漏洞CVE-2019-5736的詳情。runc是Docker、CRI-O、Containerd、Kubernetes等底層的容器運(yùn)行時(shí),此...
摘要:在年月底時(shí),我寫了一篇文章發(fā)布之際。為何有存在前面已經(jīng)基本介紹了相關(guān)背景,并且也基本明確了就是在正式發(fā)布之前的最后一個(gè)版本,那為什么會(huì)出現(xiàn)呢我們首先要介紹今年的一個(gè)提權(quán)漏洞。 在 18 年 11 月底時(shí),我寫了一篇文章 《runc 1.0-rc6 發(fā)布之際》 。如果你還不了解 runc 是什么,以及如何使用它,請(qǐng)參考我那篇文章。本文中,不再對(duì)其概念和用法等進(jìn)行說明。 在 runc 1....
閱讀 3885·2021-11-17 09:33
閱讀 1196·2021-10-09 09:44
閱讀 400·2019-08-30 13:59
閱讀 3478·2019-08-30 11:26
閱讀 2177·2019-08-29 16:56
閱讀 2849·2019-08-29 14:22
閱讀 3151·2019-08-29 12:11
閱讀 1269·2019-08-29 10:58