Docker Client作成とコマンド実行


//Dockerのアーキテクチャ分析を追加
Docker Client作成およびコマンド実行
 
Dockerアーキテクチャ:http://www.infoq.com/cn/articles/docker-source-code-analysis-part1
全体的なアーキテクチャ図から、ユーザがDocker Daemon(本体部分)と通信を確立するために使用するクライアントであるDocker clientのアーキテクチャ内の位置がわかる.ユーザーはDockerを使用してファイルを実行してcontainerの管理要求を開始します.
Go言語:GoはGoogleが開発したコンパイル型で、平行化でき、ゴミ回収機能を持つプログラミング言語です.
各Goプログラムはパケットで構成され,プログラム実行のエントリはパケットmainである.DockerはGo言語で開発された.
 
1、Docker Client作成
Docker Client作成は、実際にはdocker実行可能ファイルを介してDockerServerと連絡を作成するクライアントです.
1)flagパラメータ解析
パラメータには、コマンドラインパラメータとリクエストパラメータの2つのクラスがあります.
Docker Clientが作成したソースコードは./docker/docker/docker.go、このファイルにはDocker全体のmain関数が含まれており、Docker全体が実行されるエントリです.
https://github.com/docker/docker/blob/v1.2.0/docker/docker.go
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
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
use '' (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 )
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
specified 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パラメータ解析は2つのタスクを完了します:コマンドラインflagパラメータを解析して相応の値を取得します;非コマンドラインflagパラメータはflagに格納.Args().
 
2)flag情報を処理し、Docker Client構成情報を収集する
flagパラメータの処理:flVersion、flDebug、flDaemon、flTlsVerify、flTls;
Docker Clientの構成情報の収集:protoAddrParts(flHostsパラメータによって取得され、Docker Clientとサーバの通信プロトコルおよび通信アドレスを提供する役割)、tlsConfig(flTls、*flTlsVerifyなどの一連のflagパラメータによって取得され、セキュリティトランスポート層プロトコルの保障を提供する役割を果たす).
flag.Parse()以降のコードは,解析後のflagパラメータを以下のように判断する.
      //  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構成情報により、
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:
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()における要求パラメータ
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関数:
// 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」を例に説明します.
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,"
Please 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