guanjunjian

「一」DOCKER源码分析1 docker client命令行执行流程

2017-09-26
guanjunjian

开始阅读docker源码,最终目的是了解docker network的实现;

本文从docker run –net bridge ubuntu 追踪docker network初始化的过程,同时了解docker对网络数据包的处理流程;

源码阅读基于docker version1.17.05.x

1. docker client的入口main

1.1 源码

docker client的main函数位于moby/cmd/docker/docker.go,代码的主要内容是:

func main() {
	...
	dockerCli := command.NewDockerCli(stdin, stdout, stderr)
	cmd := newDockerCommand(dockerCli)
	if err := cmd.Execute(); 
	...
}

这部分代码的主要工作是:

  • 生成一个带有输入输出的客户端对象

  • 根据dockerCli客户端对象,解析命令行参数,生成带有命令行参数及客户端配置信息的cmd命令行对象

  • 根据输入参数args完成命令执行

1.2 流程图

我们需要追踪的是docker run的执行流程,从流程图中可以看到,从cmd := newDockerCommand(dockerCli)的一步步进行下去,可以追踪到runContainer(dockerCli,opts,copts,contianerConfig),而这里就是run命令的执行函数。 在具体了解runContainer()的具体执行之前,先了解一下cobra.Command结构体。

1.3 cobra.Command

1.3.1 首先介绍cobra这个库的简单使用:

package main

import (
    "fmt"

    "github.com/spf13/cobra"
)

func main() {
   //1.定义主命令
    var Version bool
    var rootCmd = &cobra.Command{
        Use:   "root [sub]",
        Short: "My root command",
       //命令执行的函数
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("Inside rootCmd Run with args: %v\n", args)
            if Version {
             fmt.Printf("Version:1.0\n")
            }
        },


    }
   //2.定义子命令
    var subCmd = &cobra.Command{
        Use:   "sub [no options!]",
        Short: "My subcommand",
        //命令执行的函数
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Printf("Inside subCmd Run with args: %v\n", args)
        },

    }
    //添加子命令
    rootCmd.AddCommand(subCmd)
   //3.为命令添加选项
    flags := rootCmd.Flags()
    flags.BoolVarP(&Version, "version", "v", false, "Print version information and quit")
      //执行命令
    _ = rootCmd.Execute()
}

基本用法大概就是四步:

  • 定义一个主命令(包含命令执行函数等)
  • 定义若干子命令(包含命令执行函数等,根据需要可以为子命令定义子命令),并添加到主命令
  • 为命令添加选项
  • 执行命令

1.3.2 docker中cobra.Command的使用

docker中Command的使用就体现在commands.AddCommands()。在commands.AddCommands(cmd, dockerCli)中,将run、build等一些命令的执行函数添加到commands中。

2. runContainer()

2.1 源码

runContainer()的代码位于moby/cli/command/container/run.go,代码的主要部分为:

func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
	config := containerConfig.Config
	hostConfig := containerConfig.HostConfig
	...
	createResponse, err := createContainer(ctx, dockerCli, containerConfig, opts.name)  //向daemon发送create
	...
	client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}) ////向daemon发送post

}

2.2 流程图

ContainerCreate()ContainerStart()中分别向daemon发送了create和start命令。下一步,就需要到docker daemon中分析daemon对create和start的处理。

结语

本文分析了docker client对于docker run命令的处理流程,从上文的分析中的最后可以看到,client向daemon分别发送了create和start两个命令,这是容易创建的两个步骤,在下一篇文章中将分析daemon的处理流程。

参考


Content