CI/CD Pipeline 之 Container

本系列文是從 iT 邦幫忙鐵人賽系列文章搬至 gitlab-book.tw,鐵人賽撰文時 GitLab 仍為 12.x 版本,因此本系列文內容已有部分過期,本次搬移至此後,會視狀況新增一些補註說明。

本系列文是從 iT 邦幫忙鐵人賽系列文章搬至 gitlab-book.tw,鐵人賽撰文時 GitLab 仍為 12.x 版本,因此本系列文內容已有部分過期,本次搬移至此後,會視狀況新增一些補註說明。 鐵人賽原文網址:https://ithelp.ithome.com.tw/articles/10223232

CI/CD Pipeline 需要持續維護,並非建立一次就天下太平了,今天讓我們繼續跟著假想團隊的進度往下走,看看 CI Pipeline 將會做出哪些改變。

善用 Container

隨著自動化測試案例的增加,CI 在執行自動化測試的時間越來越長,另外因為目前的 CI Pipeline 的規劃是先 deploy 至 dev 或 stg 環境,接著在其上執行自動化測試,因此效率又更差了,並且也比較難擴展為並行同步執行測試。因此團隊決定調整 CI Pipeline,並開始引入 Container 技術。

既然要引入 Container,首先要準備一個能夠操控 Container 的 GitLab Runner。還記得我們在 Day 9 的文章中是如何安裝 GitLab Runner 的嗎?那時是在 Server 上以 apt-get 的方式安裝,這次我們要換一個作法,不僅要讓 Runner 可以控制 Container,連 Runner 本身都是以 Container 架設。

首先第一步,要在 Server 上先安裝好 Docker。Docker 的安裝與使用方式本文就不詳述了,還請自己參閱 Docker 官方文件

接著透過 Docker 來架設 GitLab Runner,並且將 host 上的 docker 分享至 container 內,讓 Runner 能夠控制 Docker,藉此能夠以 container 來執行 CI Job。(這種讓 Container 裡面可以操作 host 的 docker 的做法,又稱為 Docker in Docker。)

docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest

Runner 架設完畢後,記得一樣要註冊,才算是真正的架設完畢。這次註冊 Runner 的時候,要注意 executor 要選擇 Docker,以及要給 Runner 一個合適的 tags 與其他非操控 Container 的 Runner 區隔。


(這次是在 Container 內執行 gitlab-runner register,記得要選擇 docker 作為 executor,並指定預設使用的 docker image。)

接著就來調整 CI Pipeline,首先回憶一下目前的.gitlab-ci.yml

stages:
  - build
  - deploy
  - test

laravel-build:
  stage: build
  tags:
    - "build"
  script:
    - composer install
    - npm install
    - npm run production
  artifacts:
    paths:
      - ./

deploy-for-testing:
  stage: deploy
  tags:
    - "shell"
  script:
    - ansible-playbook deploy-for-testing.yml

testing:
  stage: test
  tags:
    - "shell"
  script:
    - ansible-playbook unit-testing.yml
    - ansible-playbook service-testing.yml
    - ansible-playbook gui-testing.yml

接著我們調整 Unit testing,將其改成 Container 取代,調整後的 .gitlab-ci.yml 會類似下面的模樣。

stages:
  - build
  - unit-test
  - deploy
  - test

laravel-build:
  stage: build
  tags:
    - "build"
  script:
    - composer install
    - npm install
    - npm run production
  artifacts:
    paths:
      - ./

unit-test:
  image: phpunit/phpunit
  stage: unit-test
  tags:
    - "container"
  script:
    - /usr/bin/phpunit -c phpunit.xml --coverage-text --colors-never
  dependencies:
    - laravel-build

deploy-for-testing:
  stage: deploy
  tags:
    - "shell"
  script:
    - ansible-playbook deploy-for-testing.yml

testing:
  stage: test
  tags:
    - "shell"
  script:
    - ansible-playbook service-testing.yml
    - ansible-playbook gui-testing.yml

上面調整的是將原本 testing 中的 Unit testing 的動作 ansible-playbook unit-testing.yml 移除,改為一個獨立的 stage 與 CI Job unit-test。在 CI Job unit-test 中特別之處有三個:

  • image: phpunit/phpunit 是指我們要使用 phpunit/phpunit 這個 docker image 作為執行 Unit testing 的環境。
  • tags: 則指定為 container,即是前面我們架設新的 GitLab Runner 時為 Runner 指定的 tags。
  • dependencies: 則是這項 CI Job 相依於哪一個 CI Job 產生的 Artifacts,在執行 script 之前,Runner 會先把指定的 Artifacts 下載到 Container 內。

當 GitLab Runner 在執行此 CI Job 時,會自動運行一個 Container 並且將 Source code 或 Artifacts 放進 Container 內,因此只要你選用的 Docker image 及 script: 內的指令撰寫都正確,就可以順利完成自動化測試,畢竟你總不可能要執行的是 php 的 unit testing 卻找一個 java 環境的 docker image 來執行 phpunit。

透過這樣的調整,原本我們必須先有一個 deploy-for-testing 的 CI Job,先將程式部署至 dev 或 stg 環境後才能執行下一個 CI Job testing。但現在則直接由一個 unit-test 取代。


(如上圖,在 CI Job 的紀錄中有 Using docker image 的動作,這即是以 Container 運行的 CI 環境。)

小結

今天我們將 Unit testing 獨立改用 Container 環境來執行自動化測試。相較於 Service testing、GUI testing,Unit testing 應該是最容易被獨立轉換成 Container 環境的一種測試,正常來說 unit testing 在開發者的 local 就應該先被執行過一次,都能順利通過之後,程式碼才能被允許送進版控。

當然有些開發者的 local 環境可能欠缺執行測試的環境與相依 Packages,但這項問題在 Container 盛行的現在已不再是難解的問題。(甚至不用到 Container,以 Virtual Machine 就已經足夠方便解決。)

因為 Container 具備的封裝性、可攜性與便利性,讓開發者的 local 環境、CI Service 執行 CI Job 的環境,甚至是 Production 環境都能更便捷的保持一致。過去開發者與維運人員經常容易在環境架設及組態設定上起爭執而搞不定的狀況,現在藉助 Container 也更容易解決了。對於自動化測試及 CI/CD 而言,學習如何善用 Container 幾乎是一項先決條件了。

最後,針對今天的內容提幾個問題:

  • Unit testing 到底應該在哪裡執行?有需要在不同環境重複執行嗎?
  • 其他種類的自動化測試又該如何改用 Container 來執行?
  • 有沒有某種自動化測試不能或不適合以 Container 環境執行?
  • 如何善用 Container 加速自動化測試?
  • 除了自動化測試,其他的 CI Job 如轉換成 Container 來執行是否也能獲得好處?
  • Container 除了用於自動化測試,還可以用在哪些地方?
  • Docker in Docker 這種做法有沒有什麼不妥之處?

參考資料