CI/CD Pipeline 之 stage: build

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

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

昨天我們已經創建了第一條 CI/CD Pipeline,但實際上在 script: 中我們並沒有撰寫什麼真實可用的動作。接續昨天的進度,今天繼續說明 CI/CD Pipeline 中的那些辛苦麻煩事。

前情提要

在繼續說明之前,先幫大家恢復一下記憶並說明目前 CI/CD 的環境現況。

我們假設的案例情境是,這是一個全新建立的開發團隊,並且即將要開發一項全新的產品。在目前萬事待舉的狀態下,我們的主角 Dev Leader 決定先用熟悉的老方法來建立初期的 CI/CD Pipeline,因此目前 CI/CD 的環境將如下圖所示:

  • GitLab Runner、Dev Server 與 Stg Server 皆是獨立的 Virtual Machine。
  • GitLab Runner 會向 GitLab Server 取得 CI Job 及 Source Code。
  • GitLab Runner 會在自己 Server 的 local 完成 build
  • GitLab Runner 會將 build 完成的程式 deploy 至 Dev 與 Stg Server。
  • GitLab Runner 會連上 Dev 與 Stg Server 執行自動化測試。

前情提要完畢,下面就開始今天的進度。

Build

相信大家都知道,並不是你將任何的 Source Code 放在 Server 上,該程式就直接能夠運行。有些程式語言需要事先 Compile,有一些則是有許多的 Dependency 需要處理。因此不管是 Compiled Language 或 Interpreted Language,通常 CI Pipeline 的第一個 Stage/Job 多半都會是 Build,透過 Build 將你的程式從無法運行的 Source Code 轉變成一包只要 Deploy 至 Server 之後即可運行的程式。

舉例來說,如果是需要 Compile 的程式碼,很可能 Build 的 CI Job 會如下範例:

compile:
  stage: build
  tags:
    - "build"
  script:
    - ./configure
    - make

如果是有相依其他 Package 或 Library 的程式碼,例如目前 PHP 熱門 Framework 之一的 Laravel,則可能會如下範例:

laravel-build:
  stage: build
  tags:
    - "build"
  script:
    - composer install
    - npm install
    - npm run production

(Composer 是 PHP 的 Dependency Manager,composer install 會根據 composer.jsoncomposer.lock 去抓取相依的 PHP Packages。)

不管是上述哪一種,總之在 script: 你需要撰寫的就是這些 Compile 或抓取相依 Packages 的指令,讓 GitLab Runner 幫你自動執行這些指令,將 Source Code 轉變成可以運行的程式。

Artifacts

當 Source Code 經過 build 變成可運行的程式後,這些程式(檔案)即可被稱為是 Artifacts(產出物)。Artifacts 需要被管理、儲存,不然輪到下一個 Stage/Jobdeploy 時,GitLab Runner 會不知道該部署哪些檔案。

因此前面的範例,很可能需要修改成下面的模樣:

compile:
  stage: build
  tags:
    - "build"
  script:
    - ./configure
    - make
    - export CI_COMMIT=$(echo $CI_BUILD_REF | cut -b 1-6)
    - export ARTIFACT_NAME="$CI_PROJECT_NAME"-"$CI_COMMIT"
    - mv bin/compiled-code /artifacts/$ARTIFACT_NAME

我們假設負責 build 的 GitLab Runner 該 Server 同時也兼作存放 Artifacts 的 Server,因此 Source Code 編譯產生的 Artifacts,可以直接用 Command mv 搬移到指定的路徑,並且修改 Artifacts 的檔名以吻合規定的命名規則進行歸檔。等到 Runner 要執行 deploy 的 CI Job 時,我們就可以指定它去該路徑下取得特定的 Artifacts 進行部署的動作。

續上,如果是 Laravel 的範例,則可能會修改為:

laravel-build:
  stage: build
  tags:
    - "build"
  script:
    - composer install
    - npm install
    - npm run production
    - export CI_COMMIT=$(echo $CI_BUILD_REF | cut -b 1-6)
    - export ARTIFACT_NAME="$CI_PROJECT_NAME"-"$CI_COMMIT".tar.gz
    - cd ..
    - tar -zc $CI_PROJECT_NAME -f /artifacts/$ARTIFACT_NAME

因為是 PHP 包含多個檔案,因此將整個資料夾用 tar 打包為 .tar.gz 進行 Artifacts 的歸檔。

GitLab CI - Job Artifacts

在前面的範例中,我們假設 Artifacts 是直接存放在 GitLab Runner 該 Server 的 /artifacts/ 路徑之下,算是必須要自己想辦法管理 Artifacts。但其實不用這麼麻煩,因為 GitLab CI 目前已經有名為 Jobs Artifacts 的功能,能夠幫助我們管理 CI Pipeline 各 Stage 所產生的 Artifacts。

讓我們繼續修改前面的範例,套用 GitLab CI 的 Job Artifacts:

compile:
  stage: build
  tags:
    - "build"
  script:
    - ./configure
    - make
  artifacts:
    paths:
      - bin/compiled-code

# 節省版面,兩個範例放在一起

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

如上範例,原本我們寫在 script: 中的那些動作都可以省去了,只要在 artifacts: 中指定 path: 即可完成歸檔,也不需要自己 tar 將資料夾打包,只要直接指定資料夾的路徑,其中所有的檔案都會被自動歸檔為 Artifacts。除此之外,甚至可以直接在 artifacts: 中設定 expire_in:,藉此控制 Artifacts 要保留多久的時間,免除自行管理 Artifacts 時需要煩惱檔案保存週期的問題。


(如正確設定了 artifacts:,即可在 GitLab CI 的 Job 頁面右欄發現 Job artifacts,User 亦可以手動 Download 或直接在線上查看 Artifacts 的內容。另外,在左欄中也會發現自動多了 Uploading artifacts 的動作。)


(查看後續的 CI Job - deploy,也會發現 GitLab CI 自動多了 Downloading artifacts 的動作。)

小結

今天我們試著認識了 CI Pipeline 的第一個 Stage - Build,透過它將 Source Code 變成真正能夠運行的程式,在這個 Stage 有哪些關鍵問題需要處理呢?

  • 你的 CI Server/Worker 是否具備 build 的能力?舉例來說如果要 Compile C++,那麼 CI Server 上是否已經安裝好 gcc 或其他編譯所需的 library?同理,如果是 PHP,那麼 CI Server 是否已安裝為 php 環境,並且安裝好 Composer 這套 PHP 的 Dependency Manager 呢?
  • 哪些檔案應該被打包為 Artifacts?例如在 build 的過程中是否會產生某些暫存檔案,這些檔案是否也要一併打包為 Artifacts?
  • Artifacts 該如何儲存與管理?Artifacts 該如何歸檔?歸檔的原則為何?檔案的命名規則?Artifacts 要保留多久?Artifacts 是要 local 直接存放在 CI Server 的某個路徑之下?上傳到自己架設某個 Files Server?上傳 AWS S3 歸檔?還是直接存放在 GitLab CI 的 Job artifacts?
  • 續上,如果是存放在 GitLab CI 的 Job artifacts,那麼 GitLab CI 會自己幫你解決後續的 CI Job 如何取得 Artifacts 的問題,同時也讓 User 可以方便地自行下載 Artifacts。但反之,如果你不是使用 GitLab CI 的 Job artifacts,該如何取得已歸檔的 Artifacts,又會是另一項你需要好好思考的問題。

Build 是 CI/CD Pipeline 重要的第一步,而 Artifacts Management(產出物管理)則是隱藏在 CI/CD Pipeline 背後的重要觀念,能否將 Artifacts 管理好,對於 CI/CD 是至關重要的。其實 Artifacts Management 本身就足夠作為鐵人賽 30 天的一個主題,但今天我們就先在此打住,隨著假想情境的推進,後續(應該)還會有機會說明更多相關的內容。

鐵人賽,我們明天見~

參考資料