弦而時習之

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

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 的設定檔即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
name: Containerize # 名稱

on: # 啟動條件
  push:
    branches:
      - main

jobs: # 任務
  assets:
	runs-on: ubuntu-latest # 運行環境
	steps:
	  # ...

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

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

自動建置 Rails 鏡像

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

Assets Precompile

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
name: Containerize

on:
  push:
    branches:
      - main

env: # 共通環境變數
  RUBY_VERSION: '2.7.5'
  NODE_VERSION: '16'
  RAILS_ENV: production
  RAILS_MASTER_KEY: ${{ secrets.RAILS_SECRET_KEY }}

jobs:
  assets:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/[email protected] # 預先做成腳本的動作,不需要自己下命令
    - name: Set up Ruby
      uses: ruby/[email protected]
      with: # 預先動作的額外設定,這邊是設定 Ruby 版本
        ruby-version: ${{ env.RUBY_VERSION }}
        bundler-cache: true
    - name: Set up Node
      uses: actions/[email protected]
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'yarn'
    - run: yarn install # 直接執行命令的情況
    - run: bundle exec rake assets:precompile
    - name: Archive precompiled assets
      uses: actions/[email protected]
      with:
        name: assets
        path: public/
        retention-days: 7

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

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

製作容器

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# ...

env:
  # ...
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # ...
  build-and-push-image:
    runs-on: ubuntu-latest
    needs: assets
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/[email protected]

      - name: Download precompiled assets
        uses: actions/[email protected]
        with:
          name: assets
          path: public/

      - name: Log in to the Container registry
        uses: docker/[email protected]
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/[email protected]
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=ref,event=branch            
      - name: Build and push Docker image
        uses: docker/[email protected]
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
          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 部署實踐回饋表單告訴我。

電子報

留言