Docker源码学习之docker-run命令
docker源码学习之 docker run 子命令
说明: 使用的代码是 https://github.com/golang108/docker-cli.git (tag: v24.0.6)
1. 添加容器相关的几个子命令
其中就有我们这里说到 run 子命令。
容器相关的 子命令 在这里 被加入到 corba 中去
cli/command/commands/commands.go 文件
1 |
|
整个命令的一个调用执行 流程如下:
1 |
|
2. NewRunCommand() 函数
1 | func NewRunCommand(dockerCli command.Cli) *cobra.Command { |
1️⃣这个函数 主要作用 就是 初始化 cobra.Command{} 结构体 指针。然后返回这个指针。
2️⃣然后 设置 flags的解析。
2.1 cmd := &cobra.Command{} 结构体的初始化
1 | func NewRunCommand(dockerCli command.Cli) *cobra.Command { |
所以 看 docker run 命令的 主要 功能呢 就是看 这个 runRun(dockerCli, cmd.Flags(), &options, copts)
函数
2.2 添加 flags
1️⃣ 单独在 NewRunCommand() 函数内添加的。对应到 ropts runOptions。
经过 flags.BoolVarP,flags.StringVar 等函数的执行,结构体里面的属性字段, 并且都用指针 和 flags 绑定 上了。
2️⃣ 另外一类是在 copts = addFlags(flags) 函数内添加的。对应到 copts *containerOptions。
经过 addFlags(flags) 函数,这个 copts 结构体实例算是创建好了,里面的属性都是零值了,并且都用指针 和 flags 绑定上了。
等 cobra 解析 flags 的时候 就会把 命令上的选项,参数 赋值 到 copts 实例里面的字段上了。
3️⃣ 其他 flags
1 | command.AddPlatformFlag(flags, &options.platform) |
4️⃣注册 RegisterFlagCompletionFunc 函数
1 | cmd.RegisterFlagCompletionFunc( |
3. runRun(dockerCli, flags, ropts, copts)函数
执行到这个 runRun() 函数的时候 copts 里面的属性值有些都确定了。
3.1 ropts *runOptions 参数介绍
1 | type createOptions struct { |
ropts 这个参数 是 `var options runOptions` , 在 func NewRunCommand() 函数开头
定义的一个, 是一个结构体类型。 里面字段的属性值 也是和 flags绑定的。
这个和flags的绑定没有单独弄个函数出来,是直接在 NewRunCommand() 函数 里面写了。
例如: -d, --name,--quiet,--pull 等选项。
这些选项没有存放在 Config/HostConfig 结构体里面(containerCfg结构体里面包含着2个属性字段)。
这个称之为 run options。
3.2 copts *containerOptions 参数介绍
这个结构体里面 的属性很多,下面简单介绍几个,
1 | // containerOptions is a data object with all the options for creating a container |
其实这结构体就是 run 子命令 后面跟的那些参数,选项等。
copts 这个参数 是 `var copts *containerOptions`, 同上一样的。也是一个结构体。
里面字段的属性值 也是和 flags绑定的。经过 addFlags(flags) 函数进行绑定了。
这个称之为 container Options。
3.3 containerCfg, err := parse(flags, copts, serverOS) 函数
这个函数 用来 生成 containerCfg 结构体实例。
1️⃣ serverOS 参数就是 “linux” 或 “windows”
2️⃣ containerCfg 是返回值,类型是 containerConfig指针。
1 | type containerConfig struct { |
4. runContainer(dockerCli, ropts, copts, containerCfg)
ropts, copts 2个参数的介绍 参考 3.1和3.2
containerCfg 这个参数是通过 containerCfg, err := parse(flags, copts, OSType)
函数得到的。
5. createContainer(ctx, dockerCli, containerCfg, &opts.createOptions)
1 | config := containerCfg.Config |
这里面会 定义了一个 pullAndTagImage 变量。这个变量是个函数,里面 有个 pullImage(ctx, dockerCli, config.Image, opts) 的调用。
流程是 先执行 dockerCli.Client().ContainerCreate() 尝试创建容器,如果没有创建成功,才会 执行那个拉取镜像的动作。
如果本地镜像已经存在,就会尝试创建容器的。
如果本地镜像不存在,走 pullImage() -> dockerCli.Client().ImageCreate() 拉取镜像这个流程
1 | 第一次尝试, 如果没有成功,这里的 response 会是个空字符串,如果成功了,会是一个容器的ID。 |
dockerCli.Client().ImageCreate() 里面是 调用 “/images/create” 接口发送post请求。
6. dockerCli.Client().ContainerCreate()
到这一步 就会 发送 http 相关请求 到 dockerd 服务上了。
1 | containerCfg{ |
dockerCli.Client().ContainerCreate() 里面 调用 “/containers/create” 接口发送post请求。带的body就是 containerCfg 中的3个属性字段。
1 | body := configWrapper{ |
1 | 服务器端注册路由: |
body := configWrapper{} -> 会转为json字符串发送请求带上。 然后在 服务端接收的时候 会再次 把 json字符串转换为 对应的结构体的。
1 | config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body) |
一次执行的body的内容:
1 | { |
容器名称是 带到 query 参数中了, 这里我们传入的是 –name my