我是山姆鍋

上篇文章說明 使用 CircleCI 作為《我。影化身》雲端持續整合方案, 其中提到 CircleCI 對於 Docker 的支援是很完整的,本文進一步來看看如何在 CircleCI 上使用 Docker 在每次代碼提交時自動建構應用程式的映像。

為了讓產生的映像夠小 [1]《我。影化身》 專案的 Docker 映像是以 eavatar/basebox 為基礎, 關於如何建構 eavatar/basebox 這個映像,請參考:建構一個與 Ubuntu 相容的小型 Docker 映像

讓 CircleCI 知道專案需要 Docker 支援

要讓 CircleCI 知道專案建置需要使用它提供的 Docker 服務,需要在
circle.yml 描述檔中加入:

1
2
3
machine:
services:
- docker

也只需要這樣,CircleCI 就知道在啟動專案建置工作時要同時啟用 Docker 支援,有了這樣的設定, 在我們的建構腳本中就可以使用 Docker 相關的命令。

專案所需的 Dockerfile

建構 Docker 映像需要一個 Dockerfile 來讓 Docker 工具知道如何建構專案所需的映像, 底下是本專案經過刪減後的 Dockerfile:

1
2
3
4
5
6
7
8
9
FROM eavatar/basebox    #1
MAINTAINER sampot <[email protected]>

ADD dist/avame /avame #2
EXPOSE 5080 5443 #3

ENV AVA_POD /home/ava/.config/avame #4
USER ava #5
CMD ["/avame/avame"] #6

其中,

  1. 指定 eavatar/basebox 作為基礎映像

    這是山姆鍋自己使用的基礎映像,在這個映象中已經有預建一個特別用戶:

    ava, 用來作為執行應用時的身份。
    
  2. 將打包好的應用檔案加入映像中的 /avame 路徑

    執行應用所需的程式碼與檔案都放在 dist/avame 這個相對路徑中。

  3. 設定應用使用的網路埠號 (port numbers)

    《我。影化身》有提供 Web 使用者介面以及 API,5080 給 HTTP, 5443

    則是 HTTPS 協定使用。
    
  4. 設定 AVA_POD 環境變數

    這個環境變數指定程式資料要放置的路徑。照道理應該要根據當下登入的使用者身份自動判別,

    但在精簡環境中,會誤判成 \'/.config/avame\'
    這個路徑,由於需要以一般使用者身份執行,
    所以強制指定到可以讀寫的路徑去。
    
  5. 更換執行應用的使用者身份:基於安全理由,即使在 Docker 容器中也不建議使用 root
    身份執行應用程式。

  6. 指定映像啟動時預設執行的命令

    /avame/avame 是應用的執行檔。

建構 Docker 映像

在 CircleCI 中使用 Docker 跟在本地開發相同,使用 docker 命令來執行。 為了可以輕易從映像檔中得知當初建構時的程式碼版本,山姆鍋使用下列指令:

1
2
docker build -t eavatar/avame:$CIRCLE_BUILD_NUM-$CIRCLE_SHA1 .
docker tag eavatar/avame:$CIRCLE_BUILD_NUM-$CIRCLE_SHA1 eavatar/avame:latest

這樣產生的映像檔名就會包含提交 (commit) 時的版本號碼,以及建構的序號。有了這個資訊, 之後不幸要重製問題時,才方便使用找出相同版本的程式碼。

上傳映像到 Docker Hub

為了之後的部署工作,需要將產生的映像上載到某個儲存所。幸好《我。影化身》是開源專案,可以使用 Docker Hub 這個免費的儲存所。要上載映像到 Docker Hub, 使用下列指令:

1
2
docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
docker push eavatar/avame

其中,$DOCKER_EMAIL, $DOCKER_USER, $DOCKER_PASS 會以對應的環境變數取代, 代表登入 Docker Hub 所需的資料,這些環境變數需要在 CircleCI 的後台設定。

完整的 circle.yml 檔案

為了方便參考,下面是寫本文時專案完整的 circle.yml 檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
machine:
services:
- docker

general:
artifacts:
- "dist/avame-0.1.0+${CIRCLE_BUILD_NUM}.tgz"

dependencies:
override:
- sudo mkdir -p /usr/local/lib
- sudo pip install -U pip
- sudo easy_install -U setuptools
- pip install -r requirements.txt
- pip install selenium

test:
override:
- sudo cp plat.libs/libsodium.so.13.1.0 /usr/local/lib/
- sudo ln -s /usr/local/lib/libsodium.so.13.1.0 /usr/local/lib/libsodium.so.13
- sudo ln -s /usr/local/lib/libsodium.so.13.1.0 /usr/local/lib/libsodium.so
- PYTHONPATH=".:./src" py.test -vvv --cov=src/ava tests/unit/
- PYTHONPATH=".:./src" py.test -vvv tests/integration/
- PYTHONPATH=".:./src" py.test -vvv tests/functional/
- PYTHONPATH=".:./src" pyinstaller pack/avame-tui.spec --clean -y
post:
- cd dist && tar zcvf avame-0.1.0+${CIRCLE_BUILD_NUM}.tgz avame/

deployment:
hub:
branch: master
commands:
- docker build -t eavatar/avame:$CIRCLE_BUILD_NUM-$CIRCLE_SHA1 .
- docker tag eavatar/avame:$CIRCLE_BUILD_NUM-$CIRCLE_SHA1 eavatar/avame:latest
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
- docker push eavatar/avame

執行產生的應用映像檔

在一個已經安裝好 Docker 環境的主機,可以使用下列指令啟動《我。影化身》:

1
docker run -it --rm -p 5080:5080 eavatar/avame

在輸出的訊息中,找到 [INFO - Access Token: 9dfDU4GqZ1vgHGnL5wsXsC, 其中
9dfDU4GqZ1vgHGnL5wsXsC 是登入使用者介面需要的密碼,每次都會更動。 也可以在啟動時,透過環境變數 AVA_ACCESS_TOKEN 來指定一個固定值。

1
docker run -it --rm -p 5080:5080 -e AVA_ACCESS_TOKEN=1234 eavatar/avame

假設 Docker 容器的 IP 位址是 192.168.99.101, 則可以使用瀏覽器開啟網址 http://192.168.99.101:5080 來登入,畫面應該跟本文開頭的圖檔相似。

《我。影化身》還在開發階段,歡迎任何改進建議!

結語

既然做到隨著每次代碼的提交 (commit),只要通過測試,一個新的 Docker
映像就會被發佈到 Docker Hub 上。
下一步,自然是利用產生的映像來自動部署到測試或生產環境。對了,《我。影化身》使用
JQuery Mobile + Backbone 這樣的框架組合做 Web UI,有機會再來分享。



  1. 這裏當然是山姆鍋自己的主觀認定。 ↩︎