runC是什么?
RunC 是一個輕量級的工具鸥咖,它是用來運(yùn)行容器的燕鸽,只用來做這一件事,并且這一件事要做好啼辣。我們可以認(rèn)為它就是個命令行小工具啊研,可以不用通過 docker 引擎,直接運(yùn)行容器鸥拧。事實(shí)上党远,runC 是標(biāo)準(zhǔn)化的產(chǎn)物,它根據(jù) OCI 標(biāo)準(zhǔn)來創(chuàng)建和運(yùn)行容器富弦。而 OCI(Open Container Initiative)組織沟娱,旨在圍繞容器格式和運(yùn)行時制定一個開放的工業(yè)化標(biāo)準(zhǔn)。
概述
本文對runC的checkpoint功能模塊源碼進(jìn)行分析腕柜,主要包括:
- 梳理checkpoint功能模塊源碼的調(diào)用流程济似;
- 梳理checkpoint功能模塊源碼中用到的struct矫废、method和第三方庫;
- 梳理checkpoint功能模塊源碼中涉及的專業(yè)知識(需要學(xué)習(xí)的部分)砰蠢;
checkpoint功能模塊源碼的調(diào)用流程
runC源碼入口為:
runc/main.go
func main() {
app := cli.NewApp()
app.Name = "runc"
app.Usage = usage
...
app.Flags = []cli.Flag{
...
}
app.Commands = []cli.Command{
checkpointCommand,
...
}
...
}
由main()函數(shù)可知蓖扑,checkpointCommand是checkpoint功能模塊的入口,進(jìn)入checkpointCommand定義:
runc/checkpoint.go
var checkpointCommand = cli.Command{
...
Flags: []cli.Flag{
...
},
Action: func(context *cli.Context) error {
...
container, err := getContainer(context)
...
options := criuOptions(context)
...
return container.Checkpoint(options)
},
}
由checkpointCommand定義可知台舱,checkpointCommand核心內(nèi)容由Flags和Action組成赵誓,F(xiàn)lags中各個flag的作用通過定義可以一目了然,接下來深入分析Action中定義的函數(shù)柿赊。
Action函數(shù)
由checkpointCommand定義可知俩功,Action函數(shù)核心內(nèi)容包括:container, err := getContainer(context) 、options := criuOptions(context)和container.Checkpoint(options)三個函數(shù)調(diào)用碰声,接下來逐個函數(shù)深入分析诡蜓。
container, err := getContainer(context)
函數(shù)getContainer(...)的作用是獲取需要checkpoint的容器實(shí)例,進(jìn)入函數(shù)getContainer(...)的定義:
runc/utils_linux.go
func getContainer(context *cli.Context) (libcontainer.Container, error) {
...
factory, err := loadFactory(context)
...
return factory.Load(id)
}
由getContainer(...)函數(shù)的定義可知胰挑,其作用是通過從狀態(tài)加載指定的容器實(shí)例并返回蔓罚,整個過程是由factory, err := loadFactory(context)和factory.Load(id)聯(lián)合完成的,接下來對它們的實(shí)現(xiàn)進(jìn)行深入分析瞻颂。
factory, err := loadFactory(context)
進(jìn)入loadFactory(context)函數(shù)定義:
runc/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))
}
由 loadFactory(...)函數(shù)的定義可知豺谈,該函數(shù)的主要作用就是根據(jù)指定配置創(chuàng)建一個容器實(shí)例并返回,該功能主要由libcontainer.New(...)函數(shù)完成贡这,進(jìn)入其定義:
runc/libcontainer/factory_linux.go
func New(root string, options ...func(*LinuxFactory) error) (Factory, error) {
...
l := &LinuxFactory{
Root: root,
InitPath: "/proc/self/exe",
InitArgs: []string{os.Args[0], "init"},
Validator: validate.New(),
CriuPath: "criu",
}
...
return l, nil
}
由New(...)函數(shù)的定義可知茬末,創(chuàng)建factory實(shí)例最終是由struct LinuxFactory{...}(runc/libcontainer/factory_linux.go)來實(shí)現(xiàn)的,該struct實(shí)現(xiàn)了Factory interface {...}(runc/libcontainer/factory_linux.go)盖矫。
factory.Load(id)
進(jìn)入factory.Load(id)函數(shù)定義:
runc/libcontainer/factory_linux.go
func (l *LinuxFactory) Load(id string) (Container, error) {
...
c := &linuxContainer{
...
}
c.state = &loadedState{c: c}
...
return c, nil
}
由factory.Load(id)函數(shù)的定義可知丽惭,創(chuàng)建容器實(shí)例最終是由linuxContainer struct{...}(runc/libcontainer/container_linux.go)來實(shí)現(xiàn)的,該struct實(shí)現(xiàn)了Container interface {...}(runc/libcontainer/container_linux.go)辈双。
至此责掏,就分析完了getContainer(context)函數(shù)的整個實(shí)現(xiàn)流程,接下來分析options := criuOptions(context)湃望。
options := criuOptions(context)
函數(shù)criuOptions(context)用來獲取checkpoint的配置項實(shí)例换衬,進(jìn)入函數(shù) criuOptions(context)的定義:
runc/restore.go
func criuOptions(context *cli.Context) *libcontainer.CriuOpts {
imagePath := getCheckpointImagePath(context)
...
}
return &libcontainer.CriuOpts{
ImagesDirectory: imagePath,
WorkDirectory: context.String("work-path"),
ParentImage: context.String("parent-path"),
LeaveRunning: context.Bool("leave-running"),
TcpEstablished: context.Bool("tcp-established"),
ExternalUnixConnections: context.Bool("ext-unix-sk"),
ShellJob: context.Bool("shell-job"),
FileLocks: context.Bool("file-locks"),
PreDump: context.Bool("pre-dump"),
AutoDedup: context.Bool("auto-dedup"),
LazyPages: context.Bool("lazy-pages"),
StatusFd: context.Int("status-fd"),
}
}
由criuOptions(context)函數(shù)定義可知,該函數(shù)就是用來獲取checkpoint的各配置項证芭,并最終創(chuàng)建一個CriuOpts實(shí)例
返回瞳浦。
container.Checkpoint(options)
函數(shù)container.Checkpoint(options)負(fù)責(zé)執(zhí)行容器的checkpoint操作,進(jìn)入其定義:
runc/libcontainer/container_linux.go
func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
...
if err := os.Mkdir(criuOpts.ImagesDirectory, 0700); err != nil && !os.IsExist(err) {
return err
}
...
rpcOpts := criurpc.CriuOpts{
...
}
c.handleCriuConfigurationFile(&rpcOpts)
...
var t criurpc.CriuReqType
if criuOpts.PreDump {
...
} else {
...
}
if criuOpts.LazyPages {
...
}
req := &criurpc.CriuReq{
Type: &t,
Opts: &rpcOpts,
}
// no need to dump all this in pre-dump
if !criuOpts.PreDump {
...
}
err = c.criuSwrk(nil, req, criuOpts, nil)
...
return nil
}
由container.Checkpoint(options)函數(shù)的定義可知檩帐,容器實(shí)例根據(jù)配置項將運(yùn)行的容器checkpoint出來术幔。
至此,就梳理完了runC的checkpoint功能模塊源碼的調(diào)用流程湃密。