蒼時弦也
蒼時弦也
資深軟體工程師
發表於

Rails 部署實踐 - 使用 GitHub Actions 自動化建置

這篇文章是 Rails 部署實踐 系列的一部分。

GitHub Actions 跟 GitLab CI 有著不少差異,雖然在這類工具中不外乎就是生產線(Pipeline)和任務(Task)的搭配使用,然而每套系統都還是有著不同的設計可以使用。

因為我比較常使用 GitLab CI 因此有著完整的樣板專案可以使用,目前還在建置 GitHub Actions 的樣板,這篇文章主要是我在 GitHub 上面的專案所彙整出來的使用技巧。

GitHub Actions 基礎概念

跟 GitLab CI 不同的地方在於,整個 GitHub Actions 允許有多個工作流程(Workflows)的存在,我們可以理解為允許多個生產線(也就是 .gitlab-ci.yml 設定)的狀態,基於這樣的前提我們就能把不同的任務拆分出來處理。

GitLab CI 其實也能夠利用 Child Pipeline 的方式來實現這件事情。

基本結構

我們只需要在 .github/workflows/ 目錄下增加像是 containerize.yml 的設定檔即可。

 1name: Containerize # 名稱
 2
 3on: # 啟動條件
 4  push:
 5    branches:
 6      - main
 7
 8jobs: # 任務
 9  assets:
10	runs-on: ubuntu-latest # 運行環境
11	steps:
12	  # ...

基本結構大致上是類似的,然而我們沒有了階段(Stage)的設定,每個工作流程基本上是會同步的進行,但是可以利用 on 的觸發來設定要在什麼情況下才運行。

至於 Job(任務)的使用基本上是類似的,只不過在 GitHub Actions 中不是使用容器而是直接使用 Runner 來運行。

自動建置 Rails 鏡像

想在 GitHub Actions 自動建置鏡像某方面來說比在 GitLab CI 來說是更容易的,因為有許多現成的 Action(動作)模組可以使用,然而每個模組的作者在文件上的撰寫品質不一,因此也不一定容易使用,這邊我們使用的基本上都是官方的套件,因此還不算難使用。

Assets Precompile

同樣的,我們希望在容器化之前就先把素材預先編譯完畢,因此第一個加入的就是 Assets Precompile 的任務。

 1name: Containerize
 2
 3on:
 4  push:
 5    branches:
 6      - main
 7
 8env: # 共通環境變數
 9  RUBY_VERSION: '2.7.5'
10  NODE_VERSION: '16'
11  RAILS_ENV: production
12  RAILS_MASTER_KEY: ${{ secrets.RAILS_SECRET_KEY }}
13
14jobs:
15  assets:
16    runs-on: ubuntu-latest
17    steps:
18    - uses: actions/checkout@v2 # 預先做成腳本的動作,不需要自己下命令
19    - name: Set up Ruby
20      uses: ruby/setup-ruby@v1
21      with: # 預先動作的額外設定,這邊是設定 Ruby 版本
22        ruby-version: ${{ env.RUBY_VERSION }}
23        bundler-cache: true
24    - name: Set up Node
25      uses: actions/setup-node@v2
26      with:
27        node-version: ${{ env.NODE_VERSION }}
28        cache: 'yarn'
29    - run: yarn install # 直接執行命令的情況
30    - run: bundle exec rake assets:precompile
31    - name: Archive precompiled assets
32      uses: actions/upload-artifact@v3
33      with:
34        name: assets
35        path: public/
36        retention-days: 7

在上面這段大多是使用 GitHub、Ruby、Node.js 官方預先準備好的動作來處理,假設是一些主流的語言、工具大多都已經有先寫好的動作,可以直接當作是模組載入使用。

在這個階段我們基本上就是把 Ruby、Node.js 都安裝一遍,然後執行 rake assets:precompile 預先編譯素材,最後使用 Artifacts(生成物)功能將編譯好的檔案上傳。

製作容器

接下來我們要進行容器化的處理,基本上大部分的命令都已經有預先製作好的版本,我們只需要套用正確的設定即可。雖然 Docker 可以使用 Buildx(Buildkit 的封裝)然而卻無法使用 Registry 版本的封裝,這會讓容器化的速度稍微變慢,不過還在可以接受的範圍之內。

 1# ...
 2
 3env:
 4  # ...
 5  REGISTRY: ghcr.io
 6  IMAGE_NAME: ${{ github.repository }}
 7
 8jobs:
 9  # ...
10  build-and-push-image:
11    runs-on: ubuntu-latest
12    needs: assets
13    permissions:
14      contents: read
15      packages: write
16
17    steps:
18      - name: Checkout repository
19        uses: actions/checkout@v2
20
21      - name: Download precompiled assets
22        uses: actions/download-artifact@v3
23        with:
24          name: assets
25          path: public/
26
27      - name: Log in to the Container registry
28        uses: docker/login-action@v1
29        with:
30          registry: ${{ env.REGISTRY }}
31          username: ${{ github.actor }}
32          password: ${{ secrets.GITHUB_TOKEN }}
33
34      - name: Extract metadata (tags, labels) for Docker
35        id: meta
36        uses: docker/metadata-action@v3
37        with:
38          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
39          tags: |
40            type=sha
41            type=semver,pattern={{version}}
42            type=semver,pattern={{major}}.{{minor}}
43            type=ref,event=branch            
44      - name: Build and push Docker image
45        uses: docker/build-push-action@v2
46        with:
47          context: .
48          push: true
49          tags: ${{ steps.meta.outputs.tags }}
50          labels: ${{ steps.meta.outputs.labels }}
51          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
52          cache-to: type=inline

因為 GitHub 有也提供 Registry 的伺服器,因此我們可以直接使用自動設定好的帳號密碼登入來上傳,跟 GitLab CI 版本不太一樣的地方是我們的鏡像標籤。

在 GitLab CI 的版本中,我們會產生類似 registry.gitlab.com/example/example:c5e6528 的鏡像標籤,然而在 GitHub Actions 預先製作好的命令中,產生的是 sha-c5e6528 這樣的標籤,然而這不影響我們使用以及控制版本來進行部署。

在這裡我們對 tags 做了三個選想,分別是 sha 為基礎的標籤、1.0.01.0 以版本為主的標籤,以及以 Branch 名稱的標籤,這個規則基本上是對應大部分 Docker 鏡像的規則,可以依照自己的需求調整。

完成這些設定之後,我們就可以在 GitHub 上面自動的產生 Rails 的容器鏡像,如果是公開的專案就能夠直接透過 GitHub 提供的 Registry 來直接使用。


如果想在第一時間收到更新,歡迎訂閱弦而時習之在這系列文章更新時收到通知,如果有希望了解的知識,可以利用Rails 部署實踐回饋表單告訴我。