2.1 引言
本章基于Docker 1.2.0的源码,分析Docker Client的内容。主要包括两个部分,分别是Docker Client的创建与Docker Client对命令的执行。两部分分析的具体内容如下。
第一部分分析Docker Client的创建。这部分的分析可分为以下三个步骤:
- 分析如何通过docker命令,解析出命令行flag参数,以及docker命令中的请求参数。
- 分析如何处理具体的flag参数信息,并收集Docker Client所需的配置信息。
- 分析如何创建一个Docker Client。
第二部分在已有Docker Client的基础上,分析如何执行docker命令。这部分的分析又可分为以下两个步骤:
- 分析如何解析docker命令中的请求参数,获取相应请求的类型。
- 分析Docker Client如何执行具体的请求命令,最终将请求发送至Docker Server。
2.2 创建Docker Client
2.2.1 Docker命令的flag参数解析
对于Docker请求中的参数,我们可以将其分为两类:第一类为命令行参数,即docker程序运行时所需提供的参数,如:-D、--daemon=true、--daemon=false等;第二类为docker发送给Docker Server的实际请求参数,如:ps、pull NAME等。
对于第一类,我们习惯将其称为flag参数,在Go语言的标准库中,专门为该类参数提供了一个flag包,方便进行命令行参数的解析。
部分main函数代码如下:
func main() {
if reexec.Init() {
return
}
flag.Parse()
// FIXME: validate daemon flags here
}
以上源码实现中,首先判断reexec.Init()方法的返回值,若为真,则直接退出运行,否则将继续执行。reexec.Init()函数的定义位于./docker/reexec/reexec.go,可以发现由于在docker运行之前没有任何Initializer注册,故该代码段执行的返回值为假。reexec存在的作用是:协调execdriver与容器创建时dockerinit这两者的关系。
判断reexec.Init()之后,Docker的main函数通过调用flag.Parse()函数,解析命令行中的flag参数。Docker在./docker/docker/flag.go中定义了多个flag参数,并通过init函数进行部分flag参数初始化。代码如下:
var (
flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
flSocketGroup = flag.String([]string{"G", "-group"}, "docker", "Group to assign the unix socket specifby -H when running in daemon modeuse '' (the empty string) to disable setting of a group")
flEnableCors = flag.Bool([]string{"tls"}, false, "Use TLS; implied by tls-verify flags")
flTls = flag.Bool([]string{"-tlsverify"}, false, "Use TLS and verify the remote (daemon: verify client, client: verify daemon")
// these are initialized in init() below since their default valus depend on dockerCertPath which isn't fully initialized until init() runs
flCa string
flCert string
flKey *string
flHosts []string
)func init() {
flCa = flag.String([]string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust only remotes providing a certficate signed by the CA given here")
flCert = flag.String([]string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
flKey = flag.String([]string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file)
opts.HostListVar(&flHosts, []string{"H", "-host"}, "The socket(s) to bind to in daemon mode\nspecified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd:socketfd.")
}
关于Golang中的init函数,深入分析可以得出以下特性:
- init函数用于程序执行前包的初始化工作,比如初始化变量等;
- 每个包可以有多个init函数;
- 包的每一个源文件也可以有多个init函数;
- 同一个包内的init函数的执行顺序没有明确的定义;
- 不同包的init函数按照包导入的依赖关系决定初始化的顺序;
- init函数不能被调用,而是在main函数调用前自动被调用。
2.2.2 如何创建Docker Client
Docker Client的创建其实就是在已有配置参数信息的情况下,通过Client包中的NewDockerCli方法创建一个Docker Client实例cli。
2.3 Docker命令执行
main函数执行到这个阶段,有以下内容需要为Docker命令的执行服务:创建完毕的Docker Client,docker命令中的请求(经flag解析后存放于flag.Arg())。也就是说,程序需要使用Docker Client来分析Docker命令中的请求参数,得出请求的类型,转义为Docker Server可以识别的请求之后,最终发送给Docker Server。
Docker Client主要完成两方面的工作:解析请求命令,得出请求类型;执行具体类型的请求。
2.3.1 Docker Client解析请求命令
Docker Client解析请求命令的工作,在Docker命令执行部分第一个完成。
2.3.2 Docker Client执行请求命令
不同的Docker尽管请求内容不同,但是请求执行流程大致相同,故本节依旧以一个例子来阐述其中的流程,例子为docker pull Image_Name。该命令的作用为:Docker Client发起下载镜像的请求,最终由Docker Server接收请求,由Docker Daemon完成镜像的下载与存储。
Docker Client在执行docker pull Image_Name请求命令时,执行CmdPull函数,传入参数为args[1:]...,即Image_Name。
若用户没有制定Docker Registry的地址,则Docker默认地址为Docker Hub地址https://index.docker.io/v1/。
有疑问加站长微信联系(非本文作者)