Docker source analysis (two): Client Docker creation and command execution

Label Dockercloud computing
3316 people read comment(2) Collection Report
Classification:

[Abstract]


In this paper the docker source code analysis of the second series, in docker architecture based, continue to starting from the source point of view, analyzing how users create docker client, and how the docker client specific user request. Can be said to play the most attractive Docker, starting from the use of Docker, the use of Docker, from the master Client Docker start.

1 Preface

Today, Docker as the industry's leading lightweight virtual container management engine, to the global developer provides a novel and convenient software integration test and deployment of the road. In team development software, Docker can provide a reusable operating environment, flexible resource configuration, convenient integration test methods and a key way of deployment. It can be said that the advantages of Docker in the simplification of continuous integration, operation and maintenance of the deployment of the most vividly, it allows developers to liberate from the former, the focus on the development of the real.

However, the function of the Docker to play to the extreme, is not an easy task. In the case of a deep understanding of the Docker architecture, the use of Client Docker is also very necessary. The former can refer to"Docker source analysis" series of Docker architecture, and this paper mainly aimed at the latter, from the perspective of source code analysis Docker Client, and strive to help developers more profound understanding of the specific implementation of Client Docker, and ultimately better grasp the use of Client Docker. That this article for the "Docker source code analysis" series of second - Client Docker article.

2 Client Docker source code analysis section arrangement

This article from the source point of view, the main analysis of the two aspects of Client Docker: creation and command execution. The first four chapters are arranged as follows:

The first chapter is the introduction, introducing the role of Docker and the necessity of research on Client Docker.

The second chapter introduces some chapter arrangement.

In the third chapter, from the creation of Client Docker, source code analysis, mainly divided into three sections.

In the 3.1 section, the analysis of how to use the docker command, the analysis of the command line flag parameters, as well as the docker command in the request parameters.

In the 3.2 section, the analysis of how to deal with the specific information of the flag parameters, and to collect the configuration information required by Client Docker.

In Section 3.3, the analysis of how to create a Client Docker.

In the fourth chapter, on the basis of the existing Client Docker, it is analyzed how to execute the docker command, which is divided into two sections.

In the 4.1 section, the analysis of how to resolve the request parameters in the docker command, to obtain the type of request.

In the 4.2 section, the analysis of how Client Docker will perform a specific request command, will eventually be sent to the Server Docker.

3 Client Docker creation

The creation of Client Docker, in essence, is the Docker user through the executable file docker, and Server Docker to establish a connection with the client. The following points are divided into three sections of the Client Docker to create the process.

The following flow chart for the entire docker source code:

Docker-2-1

Figure through the flow chart of the way, so that readers more clearly understand the process of creating and executing the request Client Docker. Which involves a lot of the source code in the specific terms in the following will be explained in one one and analysis.

Analysis of flag parameters of Docker 3.1. command

As we all know, in the concrete implementation of Docker, Server Docker and Client docker are completed by the executable file Docker to create and start. So, it is very important to understand the way in which docker can be executed.

For both, the first example illustrates the difference. Server Docker start command for -d docker or --daemon=true docker; and Client Docker is reflected in the --daemon=false PS docker, pull NAME docker, etc..

Above docker request parameters are divided into two classes: first class as an argument on the command line, the docker program to run the need to provide the parameters, such as: - D, --daemon=true, --daemon=false etc.; the second category is docker sent to docker server actual request parameters, such as: PS, pull name etc..

For the first class, we used to be called the flag parameter, in the standard library go language, while also providing aFlag packageAnalysis of the command line parameters.

Explain the above background, then enter the Client Docker to achieve the creation of the source, located in./docker/docker/docker.goIn the go file, the main function that contains the entire Docker, which is the entire Docker (regardless of Docker Daemon or Client Docker) the operation of the portal. Part of the main function code is as follows:

Main func () {
Reexec.Init if () {
Return
}
Flag.Parse ()
Validate daemon flags here / / FIXME:
......
}

In the above code, the first to determine the return value of the reexec.Init method, if it is true, then quit running, otherwise continue to execute. View in./docker/reexec/reexec.goReexec.Init ()The return value of the code segment can be found to be false because there is no Initializer registration before the docker runs.

And then, the main function passes the flag parameter in the command line by calling the flag.Parse (). View source can be found in the Docker./docker/docker/flag.goDefines a number of flag parameters, and through the init function to initialize. Code as follows:

(VaR
FlVersion = flag.Bool ([]string{"V", "-version"}, false, "version information and quit Print")
FlDaemon = flag.Bool ([]string{"d", "-daemon"}, false, "daemon mode Enable")
FlDebug = flag.Bool ([]string{"D", "-debug"}, false, "debug mode Enable")
FlSocketGroup = flag.String ([]string{"G", "-group"}, "docker", "to assign the UNIX socket specified by -H when running in daemon mode use the empty string to setting Group of group" (disable a)
FlEnableCors = flag.Bool ([]string{"#api-enable-cors", "-api-enable-cors"}, false, "CORS headers in the remote API Enable")
FlTls = flag.Bool ([]string{"-tls"}, false, "TLS Use; by tls-verify flags implied")
FlTlsVerify = flag.Bool ([]string{"-tlsverify"}, false, "TLS and verify the remote (verify client client:, verify daemon daemon:)"), (Use)
    
/ / these are initialized in the init () below since their default values depend on dockerCertPath which isn't fully initialized until init () runs
*string flCa
*string flCert
*string flKey
[]string flHosts
)
    
Init func () {
FlCa = flag.String ([]string{"-tlscacert"}, filepath.Join (dockerCertPath, defaultCaFile), "only remotes providing a certificate signed by the Trust CA given here")
FlCert = flag.String ([]string{"-tlscert"}, filepath.Join (dockerCertPath, defaultCertFile), "to TLS certificate file Path")
FlKey = flag.String ([]string{"-tlskey"}, filepath.Join (dockerCertPath, defaultKeyFile), "to TLS key file Path")
Opts.HostListVar (&flHosts), "H", "[]string{", "-host", "socket The (s) bind to to Daemon in using mode\nspecified one or more tcp://host:port unix:///path/to/socket,, fd://* or fd://socketfd.")
}

 

Here comes a characteristic of the Golang, that is, the implementation of the init function. The characteristics of the init function in Golang are as follows:

* init functions for the initialization of the package before the program execution, such as initialization variables, etc.;
* each package can have multiple init functions;
* each source file of the package can also have multiple init functions;
* there is no explicit definition of the execution order of the init function within the same package;
* the init function of different packages determines the order in which the dependencies are determined by the package import;
* init function can not be called, but before the main function call is automatically called.

Therefore, before the execution of the main function, Docker has defined a number of flag parameters, and a lot of flag parameters are initialized. Defined command line flag parameters are: flDaemon, flVersion, flDebug, flSocketGroup, flEnableCors, flTls, flTlsVerify, flCa, flCert, flKey,, flHosts, etc..

The following concrete analysis flDaemon:

* definition: flDaemon = flag.Bool ([]string{"d", "-daemon"}, false, "daemon mode Enable")
* flDaemon type for Bool type
* flDaemon name is "d" or "-daemon", which appears in the docker command.
* the default value for false is flDaemon
* flDaemon help message for 'daemon mode Enable"
* access the value of the flDaemon, using the pointer * flDaemon to access the reference

When parsing the command line flag argument, the following languages are valid:

* -d, --daemon
* -d=true, --daemon=true
* true "--daemon=", true "-d=""
* true '-d=', --daemon= 'true'

When parsing to the first non - defined flag parameter, the command line flag parameter resolution work ends. For example, when the implementation of the docker docker command --daemon=false --version=false PS flag, parameters are mainly completed two work:

* complete the command line flag parameters of the analysis, named -daemon and flag -version parameters flDaemon and flVersion respectively, the corresponding values were false;
* after the first non flag parameter PS, the PS and all the parameters are stored in the flag.Args (), so that after the implementation of the Client Docker specific request.

For in-depth study of flag parsing, you can see the sourceAnalysis of command line parameter flag.

3.2. process flag information and collect the configuration information of Client Docker

With the above flag parameters analysis of the relevant knowledge, analysis of main Docker function becomes simple and easy to understand a lot of. Through the summary, the first to list the source code in the processing of flag information and the collection of Client Docker configuration information, and then one one of this analysis:

* processing of the flag parameters are: flDebug, flVersion, flDaemon, flTlsVerify and flTls;
* collected docker client configuration information: protoAddrParts (obtained by the parameters of the flHosts, docker client and server communication protocol and communication address), tlsConfig (through a series of parameters of the flag, such as *flTls *flTlsVerify, role for providing secure transmission protocol layer security).

Then analyze and deal with the flag parameter information, as well as configuration information.

After flag.Parse () the following code is as follows:

*flVersion if {
ShowVersion ()
Return
}

 

It is not difficult to understand that, when after the analysis of flag parameters, if the flVersion parameter is true, call showVersion () to display version information, and exit from the main function; otherwise, continue to be executed.

*flDebug if {
Os.Setenv ("DEBUG", "1")
}

 

If the flDebug parameter is true, create a system environment variable named DEBUG by the Setenv function in the OS package and set it to "1"". Continue to perform.

If len (flHosts) = = 0 {
DefaultHost: = os.Getenv ("DOCKER_HOST")
If defaultHost = = "|| {*flDaemon
We do not have a / If host, default to UNIX socket
DefaultHost = fmt.Sprintf ("unix://%s", api.DEFAULTUNIXSOCKET)
}
If: _, err = api.ValidateHost (defaultHost); err = nil {!
Log.Fatal (ERR)
}
FlHosts = append (flHosts, defaultHost)
}

 

More than the source of the main analysis of the internal variables flHosts. The role of flHosts is to provide the Client host to connect to the Docker object, but also for the Server Docker to provide the object to be monitored.

Analysis of the process, the first judge whether the length of the flHosts variable is 0, if the words, through the OS package was named DOCKER_HOST environment variable value, the value will be assigned to defaultHost. If defaultHost is empty or flDaemon is true, then there is no definition of a host object, then the default is set to socket UNIX, the value of api.DEFAULTUNIXSOCKET, which is located in the./docker/api/common.go, the value is "/var/run/docker.sock", so unix:///var/run/docker.sock as "defaultHost"". Verify the legitimacy of the defaultHost, the value of defaultHost will be appended to the end of the flHost. Continue to perform.

*flDaemon if {
MainDaemon ()
Return
}

 

If the flDaemon parameter is true, then the implementation of the mainDaemon function, the realization of Daemon Docker start, if the mainDaemon function is completed, then exit the main function, the general mainDaemon function will not take the initiative to end. Since this section describes the start of Client Docker, it is assumed that the flDaemon parameter is false, do not execute the above code block. Continue to perform.

Len if (flHosts) > 1 {
Log.Fatal ("specify only one -H Please")
ProtoAddrParts: = strings.SplitN (flHosts[0]: / /, 2)

 

Above, if the length of flHosts is greater than 1, then throw the error log. Then the flHosts the string array in the first element, segmentation, the / / "segmentation, the segmentation of the two parts into the variable protoAddrParts array in the. The function of protoAddrParts is to resolve the protocol and address of establishing communication with Server Docker, which is one of the necessary configuration information in the process of Client Docker.

(VaR
*client.DockerCli cli
Tls.Config tlsConfig
)
TlsConfig.InsecureSkipVerify = true

 

Since it has been assumed that the flDaemon is false, you can identify the operation of the main function is to create and execute Client Docker. Here to create two variables: one for the type is the client.DockerCli pointer to the object cli, the other is the type of tls.Config object tlsConfig. And set the InsecureSkipVerify tlsConfig property to true. TlsConfig object is created to protect the CLI in the transmission of data, follow the secure transport layer protocol (TLS). Secure transport layer protocol (TLS) For the confidentiality and data integrity of the two communication applications, the protocol has two layers: the TLS protocol and the TLS handshake protocol. TlsConfig is an optional configuration information for the Client Docker creation process.

If we should verify the / server we need to load a trusted CA
*flTlsVerify if {
*flTls = true
CertPool: = x509.NewCertPool ()
Err, file: = ioutil.ReadFile (*flCa)
Err if! = nil {
Log.Fatalf ("read CA cert%s:%s *flCa", Couldn't, ERR)
}
CertPool.AppendCertsFromPEM (file)
TlsConfig.RootCAs = certPool
TlsConfig.InsecureSkipVerify = false
}

 

If the flag parameter for this flTlsVerify is true, then it is required to verify the security of the server side, tlsConfig objects need to load a trusted Ca file. The path of the CA file for the value of the *flCA parameter, the final completion of the RootCAs object in the tlsConfig attribute values, and the InsecureSkipVerify property is set to false.

TLS is / / If enabled try to load and send client certificates
If *flTls *flTlsVerify {||
_, errCert = os.Stat (*flCert)
_, errKey = os.Stat (*flKey)
If errCert = nil and errKey = = nil {
*flTls = true
Err, cert: = tls.LoadX509KeyPair (*flCert, *flKey)
Err if! = nil {
Log.Fatalf ("load X509 key pair:%s. Key encrypted Couldn't?", ERR) ()
}
TlsConfig.Certificates = []tls.Certificate{cert}
}
}

 

If there is one of the two flag parameters for the flTlsVerify and flTls for true, it indicates the need to load and send a certificate of the client. Eventually the certificate content to the Certificates attribute of tlsConfig.

At this point, the flag parameter has been fully processed, and the configuration information required to complete the Client Docker has been collected. After the content for the Client Docker how to achieve the creation and implementation.

Docker Client 3.3. start

Client Docker is created in fact, in the case of the existing configuration parameter information, through the NewDockerCli package in the Client method to create an instance of CLI, the source code is as follows:

If *flTls *flTlsVerify {||
CLI = client.NewDockerCli (os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig,)
{else} {
CLI = client.NewDockerCli (os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil,)
}

 

If the flags parameter flTls is true or flTlsVerify really, is that need to use TLS protocol to ensure the security of transmission, so create a docker client, brought the parameters of TlsConfig; otherwise, also create docker client, just TlsConfig is nil.

On the Client package in the implementation of the NewDockerCli function, you can see./docker/api/client/cli.go.

NewDockerCli func (io.ReadCloser in, out, io.Writer, err, proto, string addr, tlsConfig, *tls.Config *DockerCli) {
(VaR
IsTerminal = false
Uintptr terminalFd
Scheme = "http"
)
    
TlsConfig if! = nil {
Scheme = "https"
}
    
In if! = nil {
File OK, if: = out. (*os.File); OK {
TerminalFd = file.Fd ()
IsTerminal = term.IsTerminal (terminalFd)
}
}
    
If err = {nil
Err = out
}
返回与dockercli {
原:原,
地址:地址:
在中,:
结账:结账,
错误:错误的,
isterminal,isterminal:
terminalfd,terminalfd:
tlsconfig,tlsconfig:
方案:方案
}
}

 

总体而言,创建dockercli对象较为简单,较为重要的dockercli的属性有proto传输协议地址:;::host的目标地址,tlsconfig安全传输层协议的配置.若tlsconfig为不为空,则说明需要使用安全传输层协议,dockercli对象的scheme设置为“https”另外还有关于输入输出以及错误显示的配置最终返回该对象,,,。。。。。。。

通过调用newdockercli函数,并返回main函数继续执行程序最终完成了创建docker客户端。

4。docker命令执行

main函数执行到目前为止,有以下内容需要为docker命令的执行服务:创建完毕的docker客户端,docker命令中的请求参数(经flag解析后存放于flag.arg .也就是说(),需要使用docker client来分析docker命令中的请求参数,并最终发送相应请求给docker服务器。

4.1。client解析请求命令码头

client解析请求命令的工作码头,在docker命令执行部分第一个完成,直接进入main函数之后的源码部分

如果cli.cmd ERR =(flag.args();……)啊!0 = {
错误:如果干,好为好。(×utils.statuserror);{
如果干的状态。。。。。。。!!!!!!!“=”
log.println(sterr.status)
}
os.exit(sterr.statuscode)
}
log.fatal(ERR)
}

 

查阅以上源码,可以发现,正如之前所说,首先解析存放于中的具体请求参数flag.args(),执行的函数为cli对象的cmd函数.进入客户端API。/ / / / cli.go的cmd函数码头

在指定的executes / cmd命令
功能* dockercli CMD(CLI)的一个错误...string){
如果len(args)>0,{
方法:(一)存在的cli.getmethod [ 0 ])
如果!为{
fmt.println(“错误:没有发现,一个命令:“[ 0 ])
返回cli.cmdhelp(args〔1〕……)
}
收益法(args〔1〕……)
}
返回cli.cmdhelp(args……)
}

 

由代码注释可知cmd函数执行具体的指令.源码实现中首先判断请求参数列表的长度是否大于0若不是的话,,,,说明没有请求信息,返回docker命令的help信息;若长度大于1的话,说明有请求信息,则首先通过请求参数列表中的第一个元素args [ 0 ]来获取具体的method的方法.如果上述method方法不存在则返回docker命令的help信息若存在的话调用具体的method方法,,,,参数为args及其之后所有的请求参数[ 1 ] [ 10 ]。

还是以一个具体的docker命令为例码头–daemon =假,假拉–version name.通过以上的分析,可以总结出以下操作流程:

(1)解析flag参数之后,将docker请求参数“拉”和“name”存放于flag.args();

(2)创建好的docker client为cli cli执行cli.cmd(flag.args),(……);

在cmd函数中,通过args [ 0 ]也就是“拉”,执行cli.getmethod(获取method的名称args【0】);

(3)在getmothod方法中”,通过处理最终返回method的值为cmdpull”;

(4)最终执行method(args〔1〕:…………………(1)也就是cmdpull args〔:〕……)。

4.2。client执行请求命令码头

上一节通过一系列的命令解析,最终找到了具体的命令的执行方法,本节内容主要介绍docker client如何通过该执行方法处理并发送请求。

由于不同的请求内容不同,执行流程大致相同,本节依旧以一个例子来阐述其中的流程,例子为名称:码头拉。

client在执行以上请求命令的时候码头,会执行cmdpull函数[ 1 ],传入参数为args:....源码具体为客户端API。/ / / / command.go中的cmdpull函数码头。。。。。。。

以下逐一分析cmdpull的源码实现。

(1)

cli.subcmd(CMD)的“拉”,“名称”],[标签:“拉一从图像或注册库”)

 

CLI package through the Subcmd method to define a type of object CMD Flagset;

(2)

Tag: = cmd.String ([]string{"#t", "#-tag"}, "", "tagged image in a repository Download")

 

CMD object to define a type of flag, called "#t" or "#-tag", the initial value is null.

(3)

Err if: = cmd.Parse (args); err! = nil {
Nil return
}

 

The args parameter analysis, process analysis, to extract the whether the flag tag parameters are in accordance with the, if the assignment to tag parameters and the rest of the parameters were stored in the cmd.NArg (); if no words, all the parameters stored in the cmd.NArg (in).

(4)

Cmd.NArg if () = 1 {
Cmd.Usage ()
Nil return
}

 

After the flag analysis to determine the parameters of the list, if the parameter list of the number of parameters is not 1, then the need to pull more than image, the pull command is not supported, then call the wrong processing method cmd.Usage (), and return nil.

(5)

(VaR
V = url.Values{}
Remote = cmd.Arg (0)
)
V.Set ("fromImage", remote)
If {*tag = = ""
V.Set ("tag", *tag)
}

 

URL parameter required to create a map type variable V, the variables used to store pull mirror. Then the parameter list of the first value is assigned to a remote variables and remote as the key to fromImage add value to V; finally if tag information, the tag information as the key to "tag" add value to V.

(6)

Remote, _ = parsers.ParseRepositoryTag (remote)
Resolve the Repository name from FQN / to hostname + name
Hostname, _, err = registry.ResolveRepositoryName (remote)
Err if! = nil {
Err return
}

 

Through the remote variables to resolve the mirror where the host address, and the name of the mirror.

(7)

Cli.LoadConfigFile ()
Resolve the Auth config relevant for / this server
AuthConfig: = cli.configFile.ResolveAuthConfig (hostname)

 

Obtain the authentication configuration information needed by cli object and Server Docker communication.

(8)

Pull: = func (registry.AuthConfig authConfig) error {
Err, buf: = json.Marshal (authConfig)
Err if! = nil {
Err return
}
RegistryAuthHeader: = []string{
Base64.URLEncoding.EncodeToString (buf),
}
Cli.stream return ("POST", "/images/create?" +v.Encode (), nil, cli.out, map[string][]string{,
"X-Registry-Auth": registryAuthHeader,.
})
}

 

Defines a function named pull, the incoming parameter type is registry.AuthConfig, the return type is error. The main content of the function is: cli.stream...... Part). This part specifically launched a Server POST Docker request, the request of the URL for "/images/create?" +v.Encode (), the request of the authentication information for: X-Registry-Auth "map[string][]string{": RegistryAuthHeader,}.

(9)

Err if: = pull (authConfig); err! = nil {
Strings.Contains if (err.Error (), "Status 401") {
Fmt.Fprintln (cli.out, "login prior to pull: \nPlease")
Err if: = cli.CmdLogin (hostname); err! = nil {
Err return
}
AuthConfig: = cli.configFile.ResolveAuthConfig (hostname)
Pull return (authConfig)
}
Err return
}

 

Since the last step is just the definition of pull function, this step specific call to perform the pull function, if the success of the final return, if the return error, then do the appropriate error handling. If the return error is 401, you need to log in, go to the login link, after completion, continue to perform the pull function, if completed, the final return.

The above is the whole implementation process pull requests, the other is similar in the execution of the request process. In short, the request execution process, most of them are will command line about the request parameters are preliminary treatment, and add the corresponding auxiliary information, through the end of the specified protocol to docker server sends a docker client and docker server agreed good API request.

5 Summary

This article from the perspective of the source code from the docker executable file to start, to create Client Docker, and ultimately sent to the Server Docker request complete process.

The author believes that by learning and understanding the docker client source can not only allow users to master the docker command, also can make the user in special circumstances have the ability to modify the docker client source code, to meet some special requirements of the system itself, to achieve customized docker client to maximize the docker thought of opening up to the value.

6 introduction of the author

Sun Hongliang,DaoCloudNew team members, software engineers, Zhejiang University computer science graduates.

Graduate school during active in PAAS and docker open source community, in-depth study and enrich the practice of cloud foundry, good at analysis of the underlying platform code, on the platform of the distributed architecture has some experience, has written a lot of depth technology blog.

At the end of 2014 to join the DaoCloud partner to join the team, committed to the spread of Docker based container technology, to promote the pace of the application of the Internet in the container.

Welcome to exchange, mail:Allen.sun@daocloud.io

7 references

[in Docker (two): Docker command line quest "
Go Programming Language] [The
[GO standard library - command line parameters analysis flag package]
Reference-Command Line] [Docker


Welcome to pay attention to the Docker source code analysis of the public number

top
Zero
step on
One
Guess you're looking for
View comments
* the above user comments only represent their personal views, does not represent the views or position of the CSDN website
    personal data
    • Visit83579 times
    • Integral:One thousand three hundred and forty-seven
    • Grade
    • Rank:18459th name
    • Original47
    • Reproduced:0
    • Translation:1
    • Comments:50
    Blog column
    Contact information
    Latest comments