自動化建置在軟體業界中已經被使用非常多年,然而許多工具並不一定容易入門,其中 GitLab CI 就屬於相對容易入門的類型,我們可以使用 YAML 格式去撰寫設定。雖然很好上手,但是在功能跟彈性上就比其他工具相對的缺少一些。
GitLab CI 基礎概念
一般來說,這類工具大多會有 Pipeline(管線)跟 Task(任務)兩個概念存在。我們可以將自動化建置想像為一個虛擬的工廠,因此我們會需要生產線(Pipeline)來產生軟體,而這條生產線中會有不同階段的處理,並且產生生成物(Artifacts)傳遞給下一個階段進行。
在 GitLab CI 的設計中,一個專案就是一條生產線,好處是在設定上的撰寫相對容易,相對的我們就不容易進行一些比較複雜的處理,像是不同平台搭配不同建置處理的情況。
基本結構
要撰寫 GitLab CI 非常容易,只需要在專案中新增 .gitlab-ci.yml
這個檔案,並且加入適當的定義即可。
1# 預設使用的鏡像
2image: ruby:2.7
3
4# 處理階段
5stage:
6 - dependency
7 - precompile
8 - containerize
9 - publish
10
11# 任務
12rubygems:
13 stage: dependency
14 script:
15 - ...
16# ...
基本上除了像是 image
、stage
這類關鍵字之外,剩下的都會被當作任務的定義,每個任務都需要指定階段(Stage)以及腳本(Script)來說明在什麼時候執行哪些指令,最簡單的使用還算容易上手。
自動建置 Rails 鏡像
要讓 GitLab CI 幫我們進行自動化的建置,我們需要將 Assets Precompile(素材預先編譯)的處理事先做完,轉換成生成物傳遞給容器化的任務封裝,最後再上傳到 GitLab Registry 來進行保存。
因為篇幅的關係,我們不會特別討論任務的優化、執行環境的設定等等比較細節的設定,文章中的範例使用的是 GitLab Runner 的 Docker 模式,因此需要 Docker in Docker 的設定支援。
Assets Precompile
要能夠進行 Rails 的 Assets Precompile 動作,我們至少需要將 Ruby 和 Node.js 的套件都安裝到環境中,因此我們的任務會需要安裝 Ruby Gem、Node Package 並且執行 Assets Precompile 任務。
1image: ruby:2.7
2
3stage:
4 - compile
5
6variables:
7 RAILS_MASTER_KEY: # 請在 GitLab CI 設定,避免 Commit 到專案中
8
9assets:precompile:
10 stage: compile
11 variables:
12 RAILS_ENV: production
13 before_script:
14 # Install Ruby Gems
15 - gem install bundler -v 2.3.3
16 - bundle config set path 'vendor'
17 - bundle install
18 # Install Node Packages
19 - curl -SLO https://nodejs.org/dist/v16.13.0/node-v16.13.0-linux-x64.tar.xz
20 - curl -o- -L https://yarnpkg.com/install.sh
21 - export PATH=$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH;
22 - yarn install
23 script:
24 - bundle exec rails assets:precompile
25 artifacts:
26 paths:
27 - public/packs
28 - public/assets
上面這段設定,我們先使用 before_script
來設定「非主要任務」的部分,也就是準備的動作,將 Ruby Gem 跟 Node Package 安裝到環境中,因為一開始已經使用 image: ruby:2.7
指定使用 Ruby 2.7 做為基礎鏡像,就不需要像 Node.js 一樣額外的進行安裝。
除此之外,我們定義了 artfiacts
設定來表示這個任務完成後,會有 public/packs
和 public/assets
兩個目錄的生成物,這樣就能將 Webpacker 以及 Rails 本身的靜態檔案預處理機制都涵蓋進來。
製作容器
接下來我們要使用 GitLab CI 的 Docker in Docker 功能,在運行的鏡像中執行一個獨立的 Docker 環境,並透過這個環境來製作 Rails 的容器鏡像。
1# ...
2stage:
3 - compile
4 - build
5# ...
6
7variables:
8 # ...
9 IMAGE_NAME: "$CI_REGISTRY_IMAGE"
10 IMAGE_TAG: "$CI_COMMIT_SHORT_SHA"
11
12docker:build:
13 image: docker:stable
14 stage: build
15 services:
16 - docker:20.10-dind
17 before_script:
18 # Add Docker Buildx Plugin
19 - mkdir -p $HOME/.docker
20 - mkdir -p /usr/libexec/docker/cli-plugins
21 - wget https://github.com/docker/buildx/releases/download/v2.0.2/buildx-v2.0.2.linux-amd64
22 -O /usr/libexec/docker/cli-plugins/docker-buildx
23 - chmod +x /usr/libexec/docker/cli-plugins/docker-buildx
24 # Login Registry
25 - echo "$CI_JOB_TOKEN" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
26 script:
27 # Setup Tag
28 - export DOCKER_TAG_OPTIONS="--tag ${IMAGE_NAME}:${IMAGE_TAG}"
29 - if [ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]; then export DOCKER_TAG_OPTIONS="--tag ${IMAGE_NAME}:latest ${DOCKER_TAG_OPTIONS}"; fi
30 # Build Docker Image and Push
31 - docker buildx build --cache-from type=registry,ref=$IMAGE_NAME:latest --cache-to type=inline ${DOCKER_TAG_OPTIONS} --push .
32 needs:
33 - job: assets:precompile
34 artifacts: true
雖然看起來很複雜,不過大多是在設置一些用來建置的參數。在這個設定中,我們先改用 Docker 專用的 docker:stable
基礎鏡像,讓我們可以使用 docker
命令。
接下來我們使用 needs
選項,告知需要 assets:precompile
任務完成後並且存在生成物,才能滿足這個任務的執行條件,可以開始執行。
在 before_script
的階段,我額外加入了 Buildx 這個外掛的安裝步驟,使用這個外掛可以用比較簡單的命令完成建置的任務,目前還不確定未來是否會被加入官方的鏡像中,但是在 Docker 官方的GitHub Actions 中已經使用這個方式建置。
外掛安裝完畢後,我們會需要使用 docker login
進行登入,在 GitLab CI 中會自動產生好帳號密碼,我們只需要使用這組臨時的帳號密碼登入即可正常的上傳、下載 GitLab Registry 中對應專案的鏡像。
在建置部分,我們可以永遠的都製作 latest
的標籤推上去,然而這樣在發生問題時想要 Rollback(回滾)的時候並不容易,取而代之的是使用 Git SHA 來當作標籤,這樣就可以指定任意的版本來進行切換。
為了實現這件事情,我們會在這裡使用 Shell Script 來製作 DOCKER_TAG_OPTIONS
的環境變數,並且讓 CI_COMMIT_BRANCH
跟 CI_DEFAULT_BRANCH
相同時,額外加上 latest
標籤,表示這是最新的版本。
最後使用 docker buildx
命令建置,可以看到比原本的 docker build
加上 docker push
的步驟單純很多,可以一次地將所有動作完成。
有了這些處理後,我們就可以在自己的 GitLab 專案自動化的製作 Rails 鏡像用於部署。
如果想在第一時間收到更新,歡迎訂閱弦而時習之在這系列文章更新時收到通知,如果有希望了解的知識,可以利用Rails 部署實踐回饋表單告訴我。
如果對這篇文章有興趣,可以透過以下連結繼續閱讀這系列的其他文章。
- Rails 部署實踐 - 補上 Rails 教學缺少的一塊
- Rails 部署實踐 - 以容器部署 Rails 的方案
- Rails 部署實踐 - 部署前置準備
- Rails 部署實踐 - 容器化 Rails 專案概述
- Rails 部署實踐 - 上傳容器鏡像
- Rails 部署實踐 - 伺服器搭建
- Rails 部署實踐 - 撰寫 Docker Compose
- Rails 部署實踐 - 使用 HTTPS 協定加密連線
- Rails 部署實踐 - 健康檢查
- Rails 部署實踐 - 滾動更新
- Rails 實踐部署 - 使用 Alpine 製作容器鏡像
- Rails 部署實踐 - 容器化的 Bundler 最佳設定
- Rails 部署實踐 - 多階段建置
- Rails 部署實踐 - 素材預先編譯
- Rails 部署實踐 - 容器進入點
- Rails 部署實踐 - 容器相關工具
- Rails 部署實踐 - 持續部署
- Rails 部署實踐 - 使用 GitLab CI 自動化建置
- Rails 部署實踐 - 使用 GitHub Actions 自動化建置
- Rails 部署實踐 - 使用 Watchtower 自動更新
- Rails 部署實踐 - Docker Swarm 與 Docker Compose
- Rails 部署實踐 - Docker Swarm 安裝與設定
- Rails 部署實踐 - 部署到 Docker Swarm
- Rails 部署實踐 - 整合 GitLab CI 自動部署
- Rails 部署實踐 - 使用 GitLab 的 Review Apps 機制
- Rails 部署實踐 - 部署不是終點