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 client端

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获取。
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的一个方法。

这个方法通过读取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方法

读取context内容

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\n", utils.TruncateID(b.image))
		if b.Remove {
			b.clearTmp()
		}
	}
返回build产生的镜像id
fmt.Fprintf(b.OutStream, "Successfully built %s\n", 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
	}













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值