我的docker随笔13:docker源码编译进阶篇

本文与前面文章相同,但多了一些分析的步骤。

一、环境搭建

docker的编译,需要在宿主机预先安装docker软件。因为编译docker的源码时,会构建一个docker镜像并运行,在这个容器里面进行build操作。由于这个容器已经包含了go语言环境,故宿主机无须额外安装golang。
宿主机系统:ubuntu 16.04 64bit
宿主机docker版本:

docker -v
Docker version 17.10.0-ce, build f4ffd25

二、下载源码

docker的github官方网站为:https://github.com/docker/docker-ce/
docker以每月发布一个版本的节奏进行开发。命名规则为:年份-月份-ce,其中ce表示社区版本。截至本文撰写时,最新版本为v18.02.0-ce,但下一版本v18.03.0的rc版本已经释放出来了,主分支的版本号为v18.03.0-ce-dev(带dev表示开发阶段),本文编译得到的版本即为v18.03.0-ce-dev
发行版本下载地址:
https://github.com/docker/docker-ce/releases
本文编译的源码,无实际路径无关。
下载源码:

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需要访问https://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: mydeb
mydeb: ## 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_docker
xenial_docker: ## build the docker
	docker build -t debbuild-ubuntu-xenial/$(ARCH) -f $(CURDIR)/ubuntu-xenial/myDockerfile.$(ARCH) .

.PHONY: ubuntu-xenial
ubuntu-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=949e6facb77383876aeff8a6944dde66b3089574

install_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=949e6facb77383876aeff8a6944dde66b3089574

install_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 -v
Docker version 18.04.0-ce-dev, build 2b42807

到此,docker的编译结束。

备注

本文所用方法,考虑了与官方docker源码不发生冲突,但在合并时,还是要注意本文提到的修改的文件。至于是否有其它更好的办法,则待后面发现时再尝试。

发布了483 篇原创文章 · 获赞 252 · 访问量 111万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览