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

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

讓 CircleCI 知道專案需要 Docker 支援

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

machine:
    services:
      - docker

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

專案所需的 Dockerfile

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

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 命令來執行 。 為了可以輕易從映像檔中得知當初建構時的程式碼版本 , 山姆鍋使用下列指令 :

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, 使用下列指令 :

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 檔 :

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 環境的主機 , 可以使用下列指令啟動 《 我 . 影化身 》:

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

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

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] 這裏當然是山姆鍋自己的主觀認定 。

知識不會因為傳播而減少,喜歡這篇文章請幫忙分享。


本篇文章由 Sampot (山姆鍋) 發表,下面是有關他的連結:

評論

您的反饋是我寫作的最大動力,歡迎參與討論。P.S. 我會優先回答張貼在這裡的問題。

comments powered by Disqus