[Docker] 用 Docker 建立專案的 build 機器

[Docker] 用 Docker 建立專案的 build 機器

平常在公司的專案裡,都會有個叫 build machine 的東東,

上面通常要裝一堆函式庫,然後再把原始碼放上去,

在那個機器上編譯出可執行碼~

 

後來這個 build machine 從實體機器演進到虛擬機器 (VM) 了,

我們可以每個 RD 都自己依需求架好幾台 VM,然後在上面編譯~

 

最近聽到公司另一個部門同事的分享,他們用 Docker 技術來建立 build machine,

因為他們每次只要 Linux kernel 有更新,就得立刻跟進,

要架設 VM 版的 build machine 還是頗花時間…

聽了他們的分享之後,想說也來研究一下我們自己的專案是不是也能這麼做呢?

 

1. 初步規劃

以我們的 build machine 來說,我們只有一套原始碼,

但是需要在 CentOS 5.4, 6.2, 和 7.0 三種平台上編譯~

需要安裝的 3rd-party 函式庫都差不多,只有很小部分的差異…

因此我決定

  – 寫一個 shell script,負責從一個 Dockerfile 的範本中,根據平台產生出對應的 Dockerfile

  – 再用 Dockerfile 建出 base image,

  – 最後就可以在這 base image 跑 container 來執行編譯工作了

 

2. 準備 Dockerfile 範本

這邊先講我寫的 Dockerfile 範本~

首先 FROM 指令這邊,因為每個平台要有不同的 base image,

因此我定義了一個 __FROM_CENTOS_TAG__,

待會再用 shell script 根據平台來把它替換成正確的 base image 名稱:

FROM __FROM_CENTOS_TAG__

 

接著有一大段使用 RUN 指令安裝 3rd-party 函式庫以及工具的部分,

這邊因為對每個平台來說都一樣,所以不需要修改:

# Install libraries
RUN yum -y install glibc-devel glibc-devel.i686
RUN yum -y install libstdc++ libstdc++.i686
RUN yum -y install postgresql-devel
RUN yum -y install pcre-devel
RUN yum -y install libpng-devel
RUN yum -y install libpcap-devel
RUN yum -y install openssl-devel openssl-devel.i686
RUN yum -y install python-devel
# Install tools
RUN yum -y install make gcc-c++
RUN yum -y install mkisofs
RUN yum -y install tar bzip2 zip unzip
RUN yum -y install patch
RUN yum -y install bison flex
RUN yum -y install gettext
RUN yum -y install wget
RUN yum -y install curl
RUN yum -y install which
RUN yum -y install openssh-clients

 

接著使用 WORKDIR 指令,將工作目錄切換成 /tmp,

因為待會會開始用 wget 抓一些工具下來編譯,想說就抓到 /tmp 目錄下:

WORKDIR /tmp

 

接下來的幾個動作稍微麻煩一點…

我需要在 CentOS 5.4 的平台上,安裝 python 2.6 (原本預設值是 2.4),

同時得把 curl 升級到 7.19.7,

還得在各個平台上安裝 pylint 好讓我們可以用 pylint 靜態檢查 python 程式有無問題,

以及安裝 cppcheck 靜態檢查 C/C++ 程式的問題~

這邊因為跟平台有關,因此我也定義了幾個會被取代的變數 (像 __INSTALL_PYTHON26__),

讓 shell script 來決定正確的變數值,這樣 Dockerfile 範本可以保持簡潔:

# Install python 2.6
RUN [ "__INSTALL_PYTHON26__" = "0" ] || (wget http://www.python.org/ftp/python/2.6.8/Python-2.6.8.tgz && tar zxvf Python-2.6.8.tgz && cd Python-2.6.8 && ./configure --with-threads && make && make install)
# Install curl 7.19.7
RUN [ "__INSTALL_CURL7_19_7__" = "0" ] || (wget https://github.com/csexton/curl-7.19.7/archive/master.zip -O master.zip && unzip master.zip && cd curl-7.19.7* && ./configure --disable-ldap --disable-ldaps --prefix /usr --libdir /usr/lib64 && make && make install)
# Install pylint
RUN wget https://pypi.python.org/packages/source/l/logilab-common/logilab-common-0.58.3.tar.gz && tar zxvf logilab-common-0.58.3.tar.gz && cd logilab-common-* && python setup.py build && python setup.py install
RUN wget https://pypi.python.org/packages/source/l/logilab-astng/logilab-astng-0.24.1.tar.gz && tar zxvf logilab-astng-0.24.1.tar.gz && cd logilab-astng-* && python setup.py build && python setup.py install
RUN wget https://pypi.python.org/packages/source/p/pylint/pylint-0.26.0.tar.gz && tar zxvf pylint-0.26.0.tar.gz && cd pylint-* && python setup.py build && python setup.py install
# Install cppcheck
RUN [ "__INSTALL_CPPCHECK__" = "0" ] || (wget http://downloads.sourceforge.net/project/cppcheck/cppcheck/1.65/cppcheck-1.65.tar.gz && tar zxvf cppcheck*.gz && cd cppcheck* && make SRCDIR=build CFGDIR=cfg HAVE_RULES=yes && make install)

 

在上面的指令裡面,要注意我都是用 && 來連結各個指令,

這邊是不能用 ; 的,否則如果前面指令錯誤的話,分號 ; 後面的指令依然會被執行,

這樣建立出來的 base image 可能是有部分東西沒有裝成功的喔~

 

最後我還準備了一份預先改好的 .bashrc,使用 COPY 指令複製進 base image,

這樣之後進去編譯環境時,可以有自己習慣使用的 alias 或設定~

以及建立 ssh key pair,

方便把 ssh public key 加到常用的 server 上面,避免 ssh 時要一直打密碼

# Prepare bashrc
COPY .bashrc ~/
# Generate ssh key
RUN ssh-keygen -f ~/.ssh/id_rsa -N ""

 

這是完整的 Dockerfile 範本:

FROM __FROM_CENTOS_TAG__
# Install libraries
RUN yum -y install glibc-devel glibc-devel.i686
RUN yum -y install libstdc++ libstdc++.i686
RUN yum -y install postgresql-devel
RUN yum -y install pcre-devel
RUN yum -y install libpng-devel
RUN yum -y install libpcap-devel
RUN yum -y install openssl-devel openssl-devel.i686
RUN yum -y install python-devel
# Install tools
RUN yum -y install make gcc-c++
RUN yum -y install mkisofs
RUN yum -y install tar bzip2 zip unzip
RUN yum -y install patch
RUN yum -y install bison flex
RUN yum -y install gettext
RUN yum -y install wget
RUN yum -y install curl
RUN yum -y install which
RUN yum -y install openssh-clients
WORKDIR /tmp
# Install python 2.6
RUN [ "__INSTALL_PYTHON26__" = "0" ] || (wget http://www.python.org/ftp/python/2.6.8/Python-2.6.8.tgz && tar zxvf Python-2.6.8.tgz && cd Python-2.6.8 && ./configure --with-threads && make && make install)
# Install curl 7.19.7
RUN [ "__INSTALL_CURL7_19_7__" = "0" ] || (wget https://github.com/csexton/curl-7.19.7/archive/master.zip -O master.zip && unzip master.zip && cd curl-7.19.7* && ./configure --disable-ldap --disable-ldaps --prefix /usr --libdir /usr/lib64 && make && make install)
# Install pylint
RUN wget https://pypi.python.org/packages/source/l/logilab-common/logilab-common-0.58.3.tar.gz && tar zxvf logilab-common-0.58.3.tar.gz && cd logilab-common-* && python setup.py build && python setup.py install
RUN wget https://pypi.python.org/packages/source/l/logilab-astng/logilab-astng-0.24.1.tar.gz && tar zxvf logilab-astng-0.24.1.tar.gz && cd logilab-astng-* && python setup.py build && python setup.py install
RUN wget https://pypi.python.org/packages/source/p/pylint/pylint-0.26.0.tar.gz && tar zxvf pylint-0.26.0.tar.gz && cd pylint-* && python setup.py build && python setup.py install
# Install cppcheck
RUN [ "__INSTALL_CPPCHECK__" = "0" ] || (wget http://downloads.sourceforge.net/project/cppcheck/cppcheck/1.65/cppcheck-1.65.tar.gz && tar zxvf cppcheck*.gz && cd cppcheck* && make SRCDIR=build CFGDIR=cfg HAVE_RULES=yes && make install)
# Prepare bashrc
COPY .bashrc /root/
# Generate ssh key
RUN ssh-keygen -f ~/.ssh/id_rsa -N ""

 

3. 撰寫 shell script 來產生 Dockerfile

shell script 本身並不複雜,只需要根據 command line 給的平台參數,

來決定 base image 的名稱,以及 cppcheck 或是 python 2.6 等等是否需要安裝,

決定好之後,就可以從 Dockerfile 範本中 (本例中的 Dockerfile.template) 複製一份,

把變數值用 sed 修改好就行了~

最後再執行 docker build 將 base image 建立好:

#!/bin/sh
. ./common.sh
# Get build platform from command line parameter
BUILD_PLATFORM="$1"
INSTALL_CPPCHECK=1
INSTALL_PYTHON26=0
INSTALL_CURL7_19_7=0
case "${BUILD_PLATFORM}" in
centos5_4)
FROM_CENTOS_TAG="gpmidi/centos-5.4"
INSTALL_CPPCHECK=0
INSTALL_PYTHON26=1
INSTALL_CURL7_19_7=1
;;
centos6_2)
FROM_CENTOS_TAG="centos:6"
;;
centos7)
FROM_CENTOS_TAG="centos:7.0.1406"
;;
*)
echo "Syntax: $0 <centos5_4|centos6_2|centos7>"
exit
;;
esac
# Replace tokens in Dockerfile
cp -f Dockerfile.template Dockerfile
for VAR in FROM_CENTOS_TAG      \
INSTALL_PYTHON26     \
INSTALL_CURL7_19_7   \
INSTALL_CPPCHECK
do
sed -i "s|__${VAR}__|$(eval echo \$${VAR})|g" Dockerfile
done
docker build -t "$(get_image_name)" .

 

4. 建立 base image

上述的 shell script 程式寫好後,就可以很方便地建立出某個平台上的 base image,例如:

./build_base_image.sh centos7

 

5. 從 base image 上執行 container,來執行專案的編譯

這邊我一樣寫了一個 run_container.sh 的 shell script 程式方便使用,

只要執行 ./run_container.sh centos7,

就能從剛建立好的 CentOS 7 的 base image 跑出一個 container 來~

程式裡面基本上就是執行 docker run,

加上 -v 參數好讓原始碼的目錄可以被掛載 (mount) 到 container 裡面

另外用 –env 設定一個 NO_PYLINT 的環境變數,

這樣的環境變數可以被 container 裡面的 Makefile 吃到來做控制的~

#!/bin/sh
. ./common.sh
# Get build platform from command line parameter
BUILD_PLATFORM="$1"
if [ -z "${BUILD_PLATFORM}" ]; then
echo "Syntax: $0 <centos5_4|centos6_2|centos7>"
exit
fi
docker run -it --rm -v ~/p4:/p4:Z -w /p4 --env NO_PYLINT=1 "$(get_image_name)" /bin/bash

 

進到 container 裡面之後,就可以執行 make 指令來編譯專案的原始碼了:

testuser@localhost ~ $ ./run_container.sh centos7
Usage of loopback devices is strongly discouraged for production use. Either use `--storage-opt dm.thinpooldev` or use `--storage-opt dm.no_warn_on_loop_devices=true` to suppress this warning.
[root@060facde7f9c src]# make

 

用上面的這些步驟,就可以很方便的建立出給某個平台用的 base image,

然後可以用這 base image 跑出來的 container 來執行專案的編譯,

不需要再另外架設 build machine (VM) 了~

 

另一個很大的好處是可以將這幾個(很小的)程式傳給其他同事,

讓他們也能很快速的建立出一樣的編譯環境,

不用再擔心每個人自己架 build machine 可能架的都不太一樣~

Docker 這個應用真的很不錯喔~^^

 

(本頁面已被瀏覽過 280 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料