设为首页 收藏本站
查看: 1032|回复: 0

[经验分享] Docker Client创建和命令执行

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-4-9 08:46:45 | 显示全部楼层 |阅读模式
001.jpg
从整体的架构图中,可以看出Docker client在架构中的位置,Docker client是用户用来和Docker Daemon(主体部分)建立通信的客户端。用户使用Docker可执行文件发起管理container的请求。
Go语言:Go是Google开发的一种编译型,可平行化,并具有垃圾回收功能的编程语言。
每个Go程序都是由包组成的,程序运行的入口是包main。Docker是用Go语言开发的。

1、  Docker Client创建
Docker Client创建其实是用户通过docker可执行文件与DockerServer创建联系的客户端。
1)  flag参数解析
参数包含两类:一类是命令行参数,另一类是请求参数。
Docker Client创建的源码位于./docker/docker/docker.go,该文件包含整个Docker的main函数,是整个Docker运行的入口。
https://github.com/docker/docker/blob/v1.2.0/docker/docker.go
1
2
3
4
5
6
7
func main() {
      if reexec.Init() {
             return
      }
      flag.Parse()
      ……
}



reexec.Init()判断是否已经初始化注册,flag.Parse()解析命令行参数。

在./docker/docker/flag.go中定义了多个flag参数,并通过init函数进行初始化。
https://github.com/docker/docker/blob/v1.2.0/docker/flags.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var (
      flVersion     = flag.Bool([]string{"v","-version"}, false, "Print version information and quit")
      flDaemon      = flag.Bool([]string{"d","-daemon"}, false, "Enable daemon mode")
      flDebug       = flag.Bool([]string{"D","-debug"}, false, "Enable debug mode")
      flSocketGroup =flag.String([]string{"G", "-group"}, "docker","Group to assign the unix socket specified by -H when running in daemonmode\nuse '' (the empty string) to disable setting of a group")
      flEnableCors  =flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"},false, "Enable CORS headers in the remote API")
      flTls         =flag.Bool([]string{"-tls"}, false, "Use TLS; implied bytls-verify flags")
      flTlsVerify   =flag.Bool([]string{"-tlsverify"}, false, "Use TLS and verify theremote (daemon: verify client, client: verify daemon)")
  
      // these are initialized in init() belowsince their default values depend on dockerCertPath which isn't fullyinitialized until init() runs
      flCa   *string
      flCert *string
      flKey  *string
      flHosts []string
)



1
2
3
4
5
6
func init() {
      flCa =flag.String([]string{"-tlscacert"}, filepath.Join(dockerCertPath,defaultCaFile), "Trust only remotes providing a certificate signed by theCA 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 keyfile")
      opts.HostListVar(&flHosts,[]string{"H", "-host"}, "The socket(s) to bind to indaemon mode\nspecified using one or more tcp://host:port,unix:///path/to/socket, fd://* or fd://socketfd.")
}



看出Docker定义了参数:flVersion、flDaemon、flDebug、flSocketGroup、flEnableCors、flTls、flTlsVerify、flCa、flCert、flKey等。并对大部分参数初始化。

flag参数解析完成两个任务:解析命令行flag参数获得相应的值;非命令行flag参数存入flag.Args()。

2)  处理flag信息并收集Docker Client配置信息
处理flag参数:flVersion、flDebug、flDaemon、flTlsVerify以及flTls;

收集Docker Client配置信息:protoAddrParts(通过flHosts参数获得,作用为提供Docker Client与Server的通信协议以及通信地址)、tlsConfig(通过一系列flag参数获得,如*flTls、*flTlsVerify,作用为提供安全传输层协议的保障)。


flag.Parse()之后代码如下,对解析后的flag参数判断:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
      //判断flVersion,显示版本信息  
      if *flVersion {
             showVersion()
             return
      }
      //判断flDebug,创建DEBUG系统环境变量并初始化为“1”
      if *flDebug {
             os.Setenv("DEBUG","1")
      }
      
      //分析flHosts,flHosts是为Docker Client提供连接的host对象,为Docker
      //Server提供需监听的对象。
      if len(flHosts) == 0 {
             defaultHost :=os.Getenv("DOCKER_HOST")
             if defaultHost == "" ||*flDaemon {
                    // If we do not have a 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)
      }
      //判断flDaemon,启动Docker Daemon
      if *flDaemon {
             mainDaemon()
             return
      }
//检验flHosts,通过分割解析出与Docker Server通信的协议与地址//(protoAddParts),为Docker Client创建的配置信息。
      if len(flHosts) > 1 {
             log.Fatal("Please specify onlyone -H")
      }
      protoAddrParts :=strings.SplitN(flHosts[0], "://", 2)
//创建两个变量:一个为类型是client.DockerCli指针的对象cli,另一个为类型//是tls.Config的对象tlsConfig。tlsConfig对象的创建是为了保障cli在传输数//据的时候,遵循安全传输层协议(TLS)。tlsConfig是可选的配置信息。
      var (
             cli       *client.DockerCli
             tlsConfig tls.Config
      )
      tlsConfig.InsecureSkipVerify = true
      //判断flTlsVerify,确保server段的安全性。
      // If we should verify the server, we needto load a trusted ca
      if *flTlsVerify {
             *flTls = true
             certPool := x509.NewCertPool()
             file, err := ioutil.ReadFile(*flCa)
             if err != nil {
                    log.Fatalf("Couldn'tread ca cert %s: %s", *flCa, err)
             }
             certPool.AppendCertsFromPEM(file)
             tlsConfig.RootCAs = certPool
             tlsConfig.InsecureSkipVerify =false
      }
      //判断flTls和flTlsVerify,加载及发送client端的证书。
      // If tls is enabled, try to load and sendclient certificates
      if *flTls || *flTlsVerify {
             _, errCert := os.Stat(*flCert)
             _, errKey := os.Stat(*flKey)
             if errCert == nil && errKey== nil {
                    *flTls = true
                    cert, err :=tls.LoadX509KeyPair(*flCert, *flKey)
                    if err != nil {
                           log.Fatalf("Couldn'tload X509 key pair: %s. Key encrypted?", err)
                    }
                    tlsConfig.Certificates =[]tls.Certificate{cert}
             }
      }



main函数执行到这里,flag命令行参数处理完毕并收集了DockerClient的配置信息。


3)  Docker Client的创建
通过上述获得的Docker Client配置信息,
1
2
3
4
5
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)
}



判断flag参数flTls和flTlsVerify,确定是否使用TLS协议保障传输安全性。
创建函数是Client包中的NewDockerCli函数,具体见./docker/api/client/cli.go:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
funcNewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string,tlsConfig *tls.Config) *DockerCli {
      var (
             isTerminal = false
             terminalFd uintptr
             scheme     = "http"
      )
  
      if tlsConfig != nil {
             scheme = "https"
      }
  
      if in != nil {
             if file, ok := out.(*os.File); ok {
                    terminalFd = file.Fd()
                    isTerminal =term.IsTerminal(terminalFd)
             }
      }
  
      if err == nil {
             err = out
      }
      return &DockerCli{
             proto:      proto,      //传输协议
             addr:       addr,       //host的目标地址
             in:         in,
             out:        out,
             err:        err,
             isTerminal: isTerminal,
             terminalFd: terminalFd,
             tlsConfig:  tlsConfig,   //安全传输层协议的配置
             scheme:     scheme,
      }
}



通过调用NewDockerCli函数,程序最终完成了创建Docker Client,并返回main函数继续执行。

2、  命令执行
Docker Client创建完毕,然后分析解析放入flag.Args()的请求参数,最后发送给Docker Server。
1)  Docker Client解析请求命令
解析并处理完flag信息之后,main函数将解析存入flag.Args()中的请求参数
1
2
3
4
5
6
7
8
9
if err :=cli.Cmd(flag.Args()...); err != nil {
             if sterr, ok :=err.(*utils.StatusError); ok {
                    if sterr.Status !="" {
                           log.Println(sterr.Status)
                    }
                    os.Exit(sterr.StatusCode)
             }
             log.Fatal(err)
      }



解析请求参数的函数是cli对象的Cmd函数。./docker/api/client/cli.go的Cmd函数:
1
2
3
4
5
6
7
8
9
10
11
12
// Cmdexecutes the specified command
func (cli*DockerCli) Cmd(args ...string) error {
      if len(args) > 0 {
             method, exists :=cli.getMethod(args[0])
             if !exists {
                    fmt.Println("Error:Command not found:", args[0])
                    returncli.CmdHelp(args[1:]...)
             }
             return method(args[1:]...)
      }
      return cli.CmdHelp(args...)
}



首先判断请求参数长度,然后通过args[0]获取具体method,如果存在则调用该方法处理后面的请求参数。

2)  Docker Client执行请求命令
不同的请求参数的执行方法不同,但流程大致相同,以”docker pull ubuntu”为例讲解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
func (cli*DockerCli) CmdPull(args ...string) error {
      cmd := cli.Subcmd("pull","NAME[:TAG]", "Pull an image or a repository from theregistry")
      tag := cmd.String([]string{"#t","#-tag"}, "", "Download tagged image in arepository")
      if err := cmd.Parse(args); err != nil {
             return nil
      }
  
      if cmd.NArg() != 1 {
             cmd.Usage()
             return nil
      }
      var (
             v      = url.Values{}
             remote = cmd.Arg(0)
      )
  
      v.Set("fromImage", remote)
  
      if *tag == "" {
             v.Set("tag", *tag)
      }
  
      remote, _ =parsers.ParseRepositoryTag(remote)
      // Resolve the Repository name from fqn tohostname + name
      hostname, _, err :=registry.ResolveRepositoryName(remote)
      if err != nil {
             return err
      }
  
      cli.LoadConfigFile()
  
      // Resolve the Auth config relevant forthis server
      authConfig := cli.configFile.ResolveAuthConfig(hostname)
  
      pull := func(authConfigregistry.AuthConfig) error {
             buf, err :=json.Marshal(authConfig)
             if err != nil {
                    return err
             }
             registryAuthHeader := []string{
                    base64.URLEncoding.EncodeToString(buf),
             }
  
             return cli.stream("POST","/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
                    "X-Registry-Auth":registryAuthHeader,
             })
      }
  
      if err := pull(authConfig); err != nil {
             if strings.Contains(err.Error(),"Status 401") {
                    fmt.Fprintln(cli.out,"\nPlease login prior to pull:")
                    if err :=cli.CmdLogin(hostname); err != nil {
                           return err
                    }
                    authConfig :=cli.configFile.ResolveAuthConfig(hostname)
                    return pull(authConfig)
             }
             return err
      }
  
      return nil
}




整个Docker源代码运行流程图:
wKioL1Uk7Luyle_kAAGYxU496jc768.jpg


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-55229-1-1.html 上篇帖子: docker 管理 下篇帖子: Docker Daemon启动
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表