本文與前面文章相同,但多了一些分析的步驟。
一、環境搭建
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源碼不發生衝突,但在合併時,還是要注意本文提到的修改的文件。至於是否有其它更好的辦法,則待後面發現時再嘗試。