runC是什么?
RunC 是一个轻量级的工具,它是用来运行容器的,只用来做这一件事,并且这一件事要做好。我们可以认为它就是个命令行小工具,可以不用通过 docker 引擎,直接运行容器。事实上,runC 是标准化的产物,它根据 OCI 标准来创建和运行容器。而 OCI(Open Container Initiative)组织,旨在围绕容器格式和运行时制定一个开放的工业化标准。
概述
本文对runC的checkpoint功能模块源码进行分析,主要包括:
- 梳理checkpoint功能模块源码的调用流程;
- 梳理checkpoint功能模块源码中用到的struct、method和第三方库;
- 梳理checkpoint功能模块源码中涉及的专业知识(需要学习的部分);
checkpoint功能模块源码的调用流程
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()函数可知,checkpointCommand是checkpoint功能模块的入口,进入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核心内容由Flags和Action组成,Flags中各个flag的作用通过定义可以一目了然,接下来深入分析Action中定义的函数。
Action函数
由checkpointCommand定义可知,Action函数核心内容包括:container, err := getContainer(context) 、options := criuOptions(context)和container.Checkpoint(options)三个函数调用,接下来逐个函数深入分析。
container, err := getContainer(context)
函数getContainer(...)的作用是获取需要checkpoint的容器实例,进入函数getContainer(...)的定义:
runc/utils_linux.go
func getContainer(context *cli.Context) (libcontainer.Container, error) {
...
factory, err := loadFactory(context)
...
return factory.Load(id)
}
由getContainer(...)函数的定义可知,其作用是通过从状态加载指定的容器实例并返回,整个过程是由factory, err := loadFactory(context)和factory.Load(id)联合完成的,接下来对它们的实现进行深入分析。
factory, err := loadFactory(context)
进入loadFactory(context)函数定义:
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(...)函数的定义可知,该函数的主要作用就是根据指定配置创建一个容器实例并返回,该功能主要由libcontainer.New(...)函数完成,进入其定义:
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(...)函数的定义可知,创建factory实例最终是由struct LinuxFactory{...}(runc/libcontainer/factory_linux.go)来实现的,该struct实现了Factory interface {...}(runc/libcontainer/factory_linux.go)。
factory.Load(id)
进入factory.Load(id)函数定义:
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)函数的定义可知,创建容器实例最终是由linuxContainer struct{...}(runc/libcontainer/container_linux.go)来实现的,该struct实现了Container interface {...}(runc/libcontainer/container_linux.go)。
至此,就分析完了getContainer(context)函数的整个实现流程,接下来分析options := criuOptions(context)。
options := criuOptions(context)
函数criuOptions(context)用来获取checkpoint的配置项实例,进入函数 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)函数定义可知,该函数就是用来获取checkpoint的各配置项,并最终创建一个CriuOpts实例
返回。
container.Checkpoint(options)
函数container.Checkpoint(options)负责执行容器的checkpoint操作,进入其定义:
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)函数的定义可知,容器实例根据配置项将运行的容器checkpoint出来。
至此,就梳理完了runC的checkpoint功能模块源码的调用流程。
网友评论