dockerコマンドのbuild


dockerコマンドのbuild

1 man docker-build


NAME        docker-build - Build an image from a Dockerfile source at PATH SYNOPSIS        docker build [--no-cache[=false]] [-q|--quiet[=false]] [--rm] [-t|--tag=TAG] PATH | URL | - DESCRIPTION        This  will  read  the  Dockerfile  from the directory specified in PATH.  It also sends any other files and directories        found in the current directory to the Docker daemon.  The contents of this directory would  be  used  by  ADD  commands        found within the Dockerfile.        Warning,  this  will  send  a lot of data to the Docker daemon depending on the contents of the current directory.  The        build is run by the Docker daemon, not by the CLI, so the whole context must be transferred to the daemon.  The  Docker        CLI reports "Sending build context to Docker daemon"when the context is sent to the daemon.        When  a  single  Dockerfile  is given as the URL, then no context is set.  When a Git repository is set as the URL, the        repository is used as context. OPTIONS        -q, --quiet=true|false When set to true, suppress verbose build output.  Default is false.        --rm=true|false When true, remove intermediate containers that are created during the build process.   The  default  is        true.        -t, --tag=tag The name to be applied to the resulting image on successful completion of the build.  tag in this context        means the entire image name including the optional TAG after the ':'.        --no-cache=true|false When set to true, do not use a cache when building the image.  The default is false.

2プロセス


実行コマンド:docker build--rm-tag tt.

2.1クライアント側


CmdBuild(api/client/command.go) -----> postBuild(api/server/server.go)   ------> CmdBuild(builder/job.go)
ClientのCmdBuildメソッドは主にbuildパラメータとbuild環境が要求を満たしているかどうかを確認した後、解析したパラメータをPOSTでhttpserverに要求し、この部分は分析しなくなった.
ここでtag=tt,rm:1,その他のパラメータはデフォルトまたは空です

2.2 serverエンド


クライアントからのリクエストを解析し、jobが実行する環境変数を構成します.docker buildコマンドラインに対応するパラメータが指定されていない場合は、システムのデフォルト構成の環境を使用します.デフォルトのパラメータは次のとおりです.
 --force-rm=false     Always remove intermediate containers, even after unsuccessful builds   --no-cache=false     Do not use cache when building the image   -q, --quiet=false    Suppress the verbose output generated by the containers   --rm=true            Remove intermediate containers after a successful build   -t, --tag=""         Repository name (and optionally a tag) to be applied to the resulting image in case of success
var (
		authEncoded       = r.Header.Get("X-Registry-Auth")
		authConfig        = &registry.AuthConfig{}
		configFileEncoded = r.Header.Get("X-Registry-Config")
		configFile        = &registry.ConfigFile{}
		job               = eng.Job("build")
	)
if r.FormValue("forcerm") == "1" && version.GreaterThanOrEqualTo("1.12") {
		job.Setenv("rm", "1")
	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
		job.Setenv("rm", "1")
	} else {
		job.Setenv("rm", r.FormValue("rm"))
	}
	job.Stdin.Add(r.Body)
	job.Setenv("remote", r.FormValue("remote"))
	job.Setenv("t", r.FormValue("t"))
	job.Setenv("q", r.FormValue("q"))
	job.Setenv("nocache", r.FormValue("nocache"))
	job.Setenv("forcerm", r.FormValue("forcerm"))
	job.SetenvJson("authConfig", authConfig)
	job.SetenvJson("configFile", configFile)
運転job
if err := job.Run(); err != nil {
		if !job.Stdout.Used() {
			return err
		}
		sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
		w.Write(sf.FormatError(err))
	}

2.3 job端子


build jobインストール、build動作を処理するjob関数体.
func (b *BuilderJob) Install() {
	b.Engine.Register("build", b.CmdBuild)
}
job環境変数を解析します.ここでrm=1,repoName=tt,tag=latest
var (
		remoteURL      = job.Getenv("remote")
		repoName       = job.Getenv("t")
		suppressOutput = job.GetenvBool("q")
		noCache        = job.GetenvBool("nocache")
		rm             = job.GetenvBool("rm")
		forceRm        = job.GetenvBool("forcerm")
		authConfig     = &registry.AuthConfig{}
		configFile     = &registry.ConfigFile{}
		tag            string
		context        io.ReadCloser
	)
	job.GetenvJson("authConfig", authConfig)
	job.GetenvJson("configFile", configFile)
	repoName, tag = parsers.ParseRepositoryTag(repoName)
本例では、次のコードセグメントを用いてdockerfileを処理し、dockerfileの内容をcontextファイルに読み出す.dockerfileを取得するには、ローカルから取得するモードとgitから取得するモード、URLから取得するモードの3つがあります.
if remoteURL == "" {
	context = ioutil.NopCloser(job.Stdin)
} else if urlutil.IsGitURL(remoteURL) {
	...
}
} else if utils.IsURL(remoteURL) {
	...
}

buildを実行するタスク構造体を新規に作成し、関数の前に収集した情報をこの構造体に埋め込み、今回のbuildの環境とします.
builder := &Builder{
		Daemon: b.Daemon,
		Engine: b.Engine,
		OutStream: &utils.StdoutFormater{
			Writer:          job.Stdout,
			StreamFormatter: sf,
		},
		ErrStream: &utils.StderrFormater{
			Writer:          job.Stdout,
			StreamFormatter: sf,
		},
		Verbose:         !suppressOutput,
		UtilizeCache:    !noCache,
		Remove:          rm,
		ForceRemove:     forceRm,
		OutOld:          job.Stdout,
		StreamFormatter: sf,
		AuthConfig:      authConfig,
		AuthConfigFile:  configFile,
	}
builderタスクの実行
id, err := builder.Run(context)
	if err != nil {
		return job.Error(err)
	}

buildタスクが完了したら、repoName、tag、idなどの情報を保存します.
if repoName != "" {
		b.Daemon.Repositories().Set(repoName, tag, id, false)
	}

builderのRunはbuilder/evaluatorにある.goではbuilderの1つの方法です.
この方法はRunパラメータcontextの内容を読み取ることによってファイルに読み込まれ,このファイルの内容を解析するが,このファイルの内容はDockerfileの内容である.
Dockerfileで使用するコマンドはファイルdispatchers.goのメソッドはevaluatorによってルーティングされる.goファイルが責任を負います.これはDockerfileファイルのコマンドです.
対応する処理方法.
func init() {
	evaluateTable = map[string]func(*Builder, []string, map[string]bool) error{
		"env":            env,
		"maintainer":     maintainer,
		"add":            add,
		"copy":           dispatchCopy, // copy() is a go builtin
		"from":           from,
		"onbuild":        onbuild,
		"workdir":        workdir,
		"docker-version": nullDispatch, // we don't care about docker-version
		"run":            run,
		"cmd":            cmd,
		"entrypoint":     entrypoint,
		"expose":         expose,
		"volume":         volume,
		"user":           user,
		"insert":         insert,
	}
}
runメソッドの分析
コンテンツの読み込み
if err := b.readContext(context); err != nil {
		return "", err
	}

生成されたDockerfileファイルを対応するパスに挿入
filename := path.Join(b.contextPath, "Dockerfile")

ファイルの内容を読み込む
f, err := os.Open(filename)
	if err != nil {
		return "", err
	}

Dockerfileファイルの内容を解析し、これらのDockerfileコマンドを実行する準備をします.
ast, err := parser.Parse(f)
	if err != nil {
		return "", err
	}

	b.dockerfile = ast
Dockerfileコマンドを実行し、b.dispatherによってコマンド配布を行う.コマンドラインに--force-rm=trueパラメータがある場合は、コマンド実行に失敗したContainerを削除します.dockerfileの各行はdockerfileコマンドであり、各コマンドの実行はContainerを新規作成し、すべてのdockerfileコマンドが実行されるまで、コマンドの実行結果をミラーのレイヤに書き込みます.このプロセスで生成されたContainerは、パラメータを実行することによって
--rm=true、build中に生成された一時Containerを削除します.またdispatchにはdockerfileが使用できないコマンドをフィルタリングする機能もあります.
b.Config = &runconfig.Config{Entrypoint: []string{}, Cmd: []string{"/bin/sh", "-c"}}
	b.TmpContainers = map[string]struct{}{}

	for i, n := range b.dockerfile.Children {
		if err := b.dispatch(i, n); err != nil {
			if b.ForceRemove {
				b.clearTmp()
			}
			return "", err
		}
		fmt.Fprintf(b.OutStream, " ---> %s
", utils.TruncateID(b.image)) if b.Remove { b.clearTmp() } }
buildによって生成されたミラーidを返す
fmt.Fprintf(b.OutStream, "Successfully built %s
", utils.TruncateID(b.image)) return b.image, nil

Dockerfileの各コマンドにはエントリがあり、dispatchメソッドで配布され、Dockerfileコマンドに対応するhandlerはdispatchersにある.goでは、
しかし、最後にcommitメソッドに渡され、commitメソッドはinternalsにある.goでは、各コマンドの実行はContainerを新規作成することで完了します.
最後に、各コマンドの実行結果をlayerにコミットします.
if id == "" {
		cmd := b.Config.Cmd
		b.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
		defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)

		hit, err := b.probeCache()
		if err != nil {
			return err
		}
		if hit {
			return nil
		}

		container, err := b.create()
		if err != nil {
			return err
		}
		id = container.ID

		if err := container.Mount(); err != nil {
			return err
		}
		defer container.Unmount()
	}
// Commit the container
	image, err := b.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
	if err != nil {
		return err
	}