本文与前面文章相同,但多了一些分析的步骤。
一、环境搭建
docker的编译,需要在宿主机预先安装docker软件。因为编译docker的源码时,会构建一个docker镜像并运行,在这个容器里面进行build操作。由于这个容器已经包含了go语言环境,故宿主机无须额外安装golang。
宿主机系统:ubuntu 16.04 64bit
宿主机docker版本:
docker -v
Docker version 17.10.0-ce, build f4ffd25
复制代码
二、下载源码
docker的github官方网站为:github.com/docker/dock…。
docker以每月发布一个版本的节奏进行开发。命名规则为:年份-月份-ce
,其中ce表示社区版本。截至本文撰写时,最新版本为v18.02.0-ce
,但下一版本v18.03.0
的rc版本已经释放出来了,主分支的版本号为v18.03.0-ce-dev
(带dev表示开发阶段),本文编译得到的版本即为v18.03.0-ce-dev
。
发行版本下载地址:
github.com/docker/dock…
本文编译的源码,无实际路径无关。
下载源码:
git clone https://github.com/docker/docker-ce
复制代码
进入docker-ce目录:
cd docker-ce
复制代码
三、编译过程
本节编译docker-ce主分支代码,经过分析后,对其编译脚本进行了修改、调整,以加速编译时间。
Makefile分析
这里先跟踪顶层Makefile到具体平台的编译Makefile文件。
首先是工程目录docker-ce的Makefile,由于是ubuntu系统,因此编译的是deb包,相关命令:
.PHONY: deb
deb: ## build deb packages
$(MAKE) VERSION=$(VERSION) CLI_DIR=$(CLI_DIR) ENGINE_DIR=$(ENGINE_DIR) -C $(PACKAGING_DIR) deb
复制代码
从命令看到,调用的是components\packaging\deb目录的Makefile(默认情况下,执行的是Makefile文件), 该Makefile文件关于deb的编译命令如下:
deb: ubuntu debian raspbian
复制代码
而ubuntu平台又涉及2个发行版本:
ubuntu: ubuntu-xenial ubuntu-trusty
复制代码
关于ubuntu-xenial,编译命令:
ubuntu-xenial: ## build ubuntu xenial deb packages
docker build -t debbuild-$@/$(ARCH) -f $(CURDIR)/$@/Dockerfile.$(ARCH) .
docker run --rm -i \
-e DEB_VERSION=$(DEB_VERSION) \
-e VERSION=$(VERSION) \
-e DOCKER_GITCOMMIT=$(GITCOMMIT) \
-v $(CURDIR)/debbuild/$@:/build \
-v $(ENGINE_DIR):/engine \
-v $(CLI_DIR):/cli \
-v $(CURDIR)/systemd:/root/build-deb/systemd \
debbuild-$@/$(ARCH)
$(CHOWN) -R $(shell id -u):$(shell id -g) debbuild/$@
复制代码
大意是先用docker build
构建一个镜像(涉及到Dockerfile,后文再提及),然后运行这个镜像,运行命令需要设置环境变量(VERSION等),还有挂载目录($(ENGINE_DIR):/engine等),执行的命令是docker镜像默认的命令。构建docker镜像命令如下:
docker build -t debbuild-$@/$(ARCH) -f $(CURDIR)/$@/Dockerfile.$(ARCH) .
复制代码
针对ubuntu16.04(代号为ubuntu-xenial),其中编译生成的镜像名称为debbuild-(ARCH),展开宏定义,则变成debbuild-ubuntu-xenial/x86_64
,而由-f指定Dockerfile,则为ubuntu-xenial/ Dockerfile.x86_64
。
ockerfile.x86_64
需要访问golang.org下载go语言安装包,该网站国内一般无法访问,因此需要想其它方法。
编译流程
从Dockerfile.x86_64
看出,默认执行脚本为build-deb,该文件位于Makefile同一目录,大致内容为进行4个组件的编译,再编译docker源码,然后拷贝生成的文件到指定目录。
具体细节本文不展开。
修改编译流程
在编译docker源码过程中,每次都会构建docker镜像,而在docker里面,每次都需要从github.com上克隆4个必要的组件源码(并进行编译),这个过程在起初时是必要的,但如果在实际开发中只需要修改个别源码进行编译的话,跑完整个流程就显示比较繁琐了。修改思路有:
预先制作好包含go语言环境的docker(已经制作好singula/docker-dev),基于这个docker镜像再次制作编译所需镜像。
将编译的组件源码目录挂载到主机目录,这样不需要每次都从网络上下载了。
修改工程目录Makefile,参考原来的deb,新加mydeb编译:
# build mydeb for ubuntu-xenial(16.04).PHONY: mydebmydeb: ## build deb packages $(MAKE) VERSION=$(VERSION) CLI_DIR=$(CLI_DIR) ENGINE_DIR=$(ENGINE_DIR) -C $(PACKAGING_DIR)/deb -f myMakefile ubuntu-xenial
复制代码
在components\packaging\deb
目录,参考Makefile,新建myMakefile,关键内容:
.PHONY: xenial_dockerxenial_docker: ## build the docker docker build -t debbuild-ubuntu-xenial/$(ARCH) -f $(CURDIR)/ubuntu-xenial/myDockerfile.$(ARCH) ..PHONY: ubuntu-xenialubuntu-xenial: ## build ubuntu xenial deb packages mkdir -p $(CURDIR)/src/tini $(CURDIR)/src/libnetwork \ $(CURDIR)/src/runc $(CURDIR)/src/containerd docker run --rm -i \ -e DEB_VERSION=$(DEB_VERSION) \ -e VERSION=$(VERSION) \ -e DOCKER_GITCOMMIT=$(GITCOMMIT) \ -v $(CURDIR)/debbuild/$@:/build \ -v $(ENGINE_DIR):/engine \ -v $(CLI_DIR):/cli \ -v $(CURDIR)/systemd:/root/build-deb/systemd \ -v $(CURDIR)/src/tini:/go/tini \ -v $(CURDIR)/src/libnetwork:/go/src/github.com/docker/libnetwork \ -v $(CURDIR)/src/runc:/go/src/github.com/opencontainers/runc \ -v $(CURDIR)/src/containerd:/go/src/github.com/containerd \ debbuild-$@/$(ARCH) /root/build-deb/mybuild-deb $(CHOWN) -R $(shell id -u):$(shell id -g) debbuild/$@ cp $(CURDIR)/debbuild/$@/*.deb $(TOP_DIR)
复制代码
该文件将构建docker和编译拆分出来,编译部分,将编译所需的组件目录挂载位于deb同级目录的src目录。最后将生成的deb包拷贝到工程目录。
参考编译脚本build-deb新建mybuild-deb文件。将TMP_GOPATH="/go" hack/dockerfile/install/install.sh $component
修改为TMP_GOPATH="/go" hack/dockerfile/myinstall/install.sh $component
。
在components\engine\hack\dockerfile
目录下,拷贝install为myinstall,修改其中的文件:
containerd.installer、proxy.installer、runc.installer、tini.installer。修改git clone的处理逻辑。
以tini.installer为例,原来内容为:
TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574install_tini() { echo "Install tini version $TINI_COMMIT" git clone https://github.com/krallin/tini.git "$GOPATH/tini" cd "$GOPATH/tini" git checkout -q "$TINI_COMMIT" cmake . make tini-static mkdir -p ${PREFIX} cp tini-static ${PREFIX}/docker-init}
复制代码
修改后为
TINI_COMMIT=949e6facb77383876aeff8a6944dde66b3089574install_tini() { echo "Install tini version $TINI_COMMIT" if [ ! -d $GOPATH/tini/.git ]; then echo "will clone tini..." git clone https://github.com/krallin/tini.git "$GOPATH/tini" else echo "tini exist..." fi cd "$GOPATH/tini" git checkout -q "$TINI_COMMIT" cmake . make tini-static mkdir -p ${PREFIX} cp tini-static ${PREFIX}/docker-init}
复制代码
即,判断tini是否被下载,如果是,则不再下载。如否,则下载之。
其它文件同理。
编译及生成文件
在docker-ce目录输入make mydeb即可进行编译。最后生成的安装包位于同一目录下。
如果从未编译过docker-ce,编译耗时约在20~30分钟(大约数,根据网络和机器性能而定),如果已编译过docker-ce,修改源码后,再次编译,则只需要几分钟到十几分钟即可。大减少编译时间。
安装
将得到的deb包存放到本机或其它ubuntu系统上,执行以下命令进行安装:
# dpkg -i docker-ce_18.04.0~ce~dev~git20180312.035344.0.37dff31-0~ubuntu_amd64.deb
复制代码
验证其版本号:
# docker -vDocker version 18.04.0-ce-dev, build 2b42807
复制代码
到此,docker的编译结束。
备注
本文所用方法,考虑了与官方docker源码不发生冲突,但在合并时,还是要注意本文提到的修改的文件。至于是否有其它更好的办法,则待后面发现时再尝试。