本文根据docker给出的docker代码编译环境搭建手册进行更深入的分析。 官方指导比较简单,但是因为国外网络问题,经常编译失败。 了解了编译步骤后怎么编译docker源码,还可以结合遇到的网络问题进行“规避”。
docker编译环境实际上创建了一个docker容器,并编译容器中的代码。 如果你想快速查看编译环境搭建指南而不关注环境搭建的机制和细节,可以直接跳到最后一章“总结”。
前提
机器上已经安装好了Docker,因为编译环境是docker容器,所以提前要有一个docker(守护进程),后面会创建一个编译环境容器,在容器上编译代码。 本文使用的是物理机,docker(守护进程)运行在物理机上。
本机(物理机)上安装了git。后续使用git下载docker源码
Make安装在机器(物理机)上。
下载ubuntu 14.04的docker镜像
编译前的配置文件
官方的编译方式是make build和make binary。 我们先来分析一下Makefile。 了解了Makefile之后,编译环境的设计流程就会更加清晰。
生成文件
在下载的docker源码中可以看到它的Makefile,以及Makefile中的几个关键参数:
DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)") DOCKER_MOUNT 表示创建容器时挂载范围。 由于编译环境是一个容器,因此后续步骤启动容器时使用DOCKER_MOUNT参数将物理机上的目录挂载到容器容器中。 容器中的这个目录就是编译生成docker二进制文件的目录。
DOCKER_FLAGS := docker run --rm -i --privileged $(DOCKER_ENVS) $(DOCKER_MOUNT) 这是旁边创建 docker 容器时命令行的一部分,其中包括上面的 DOCKER_MOUNT 参数。
DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH)) 这是docker镜像参数,镜像名称为docker-dev,使用当前git中的docker版本作为标签姓名。 该图像是在 make build 步骤中制作的。
DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)" 创建docker容器的命令行,结合上面的DOCKER_FLAGS和DOCKER_IMAGE。 从命令行可以看出,启动容器使用的参数是--rm -i --privileged,使用了一些环境变量,-v参数用于将化机上的目录挂载到容器中,并且二进制文件是在容器中编译出来的,将文件放在这个目录下之后,docker二进制文件就只能在化工机上获取了。 启动的docker容器镜像名称为docker-dev。 下面将介绍docker-dev镜像是如何来的。
由于“构建编译环境”的官方方式是执行 make build,因此在下面的 Makefile 中看到的 build 分支如下所示:
当 make build 时会调用 docker build -t "$(DOCKER_IMAGE)" 。 制作一个名为 DOCKER_IMAGE 的镜像。
源代码编译的形式是执行make binary来编译代码。 Makefile中make binary的分支如下:
make binary不仅执行make build怎么编译docker源码,还执行$(DOCKER_RUN_DOCKER),也就是上面提到的docker run命令行。 由于执行了构建,会构建docker-dev镜像,所以docker run时直接使用上面构建的镜像。 docker run时的命令行参数是hack/make.sh二进制文件。 make binary的过程其实就是创建一个容器,并在容器中执行hack/make.sh二进制脚本。 接下来,我们将详细介绍make build和make binary的作用。
进行构建
根据官方指导,首先执行make build来搭建编译环境。 上面分析了,make build实际上是创建了一个镜像,其中包含了编译代码所需的环境。 下面我们就来介绍一下这个镜像。
Dockerfile
在Makefile的同一目录下(源码根目录),有一个Dockerfile。 执行make build相当于调用docker build,使用的是Dockerfile。 Dockerfile中的几个主要步骤(这里跳过了一些步骤):
FROM ubuntu:14.04 使用 ubuntu 14.04 作为基础镜像; 在主机上,您必须提前下载ubuntu 14.04镜像。
安装一些编译所需的软件;
用git下载lvm2源码,编译安装;
下载并安装GO 1.5.1;
安装GO相关工具进行代码检测,如代码覆盖率测试、go lint。
安装注册中心和公证服务器;
安装 docker-py 之前运行集成测试
将物理机的 contrib/download-frozen-image.sh 脚本复制到镜像 /go/src/github.com/docker/docker/contrib/
运行 contrib/download-frozen-image.sh 创建镜像。 实际上,这一步只是下载了三个镜像的tar文件。 注:docker build相当于创建一个临时容器(执行临时容器中Dockerfile中的每一步,最后保存为镜像),“运行contrib/download-frozen-image.sh制作镜像”的动作” 出现在 Dockerfile 中,相当于下载了 docker build 创建的临时容器中的 docker 镜像。 这里有docker-in-docker容器嵌套的概念。 下一节将详细分析 download-frozen-image.sh 脚本。
由 ENTRYPOINT ["hack/dind"] 创建的映像可用于使用其启动的容器在源目录中手动运行 hack/dind 脚本。 dind 这个脚本是一个包装脚本,它允许 docker 在 docker 容器内运行。 以下部分将对 hack/dind 脚本进行详细分析。
复制 。 /go/src/github.com/docker/docker 将化工机上的docker源代码文件Hack到镜像download-frozen-image.sh脚本中
上一节提到,在Dockerfile中,有一步会调用contrib/download-frozen-image.sh,其主要功能是下载三个镜像的tar包,以供后续docker加载。 Dockerfile中的调用方法如下:
如何编译Docker源代码