弦而時習之

Rails 部署實踐 - 使用 GitLab 的 Review Apps 機制

Review Apps 是 GitLab 所提供的一個機制,可以用於針對某個 Merge Request(合併請求)來自動部署給用來進行 QA(Quality Assurance)驗證或者專案經理檢查功能的機制。因為我們已經可以進行自動化的部署,也因此可以用來產生 Review Apps 進行驗證。

Heroku 也有提供 Review Apps 的方案可以跟 GitHub 搭配,可以根據需求調整。

Review Apps 的運作方式

GitLab 的 Review Apps 簡單來說,就是當我們發出一個 Merge Request 的時候,如果有偵測到部署的行為,就會自動在 Merge Request 的畫面上多出一個 View app 按鈕,可以用來打開被部署的環境。

要實現一個完整的 Review Apps 的流程,我們需要以下幾個設定:

  • 在 Merge Request 產生時執行部署
  • 在 Merge Request 合併時停止部署(移除暫時的環境)
  • 在 Merge Request 的部署中載入初始化資料

啟動跟停止的部分可以利用 GitLab CI 來實現,至於初始化的測試資料則需要我們運用一些小技巧來處理。

這類初始化資料可以使用 Rails 的 Seeds 功能來實現

加入停止機制

我們在整合 GitLab CI 自動部署這篇文章中,已經可以自動的部署到正式環境,基本上只需要使用相同的方式,加入下面這段調整就可以支援 Review Apps 的啟動,然而我們還需要停止的處理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
deploy:review:
  image: docker:stable
  stage: deploy
  environment:
	name: review/$CI_COMMIT_REF_NAME
    url: $DEPLOY_DOMAIN
    on_stop: review:stop
  before_script:
    - echo "$CI_JOB_TOKEN" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
    - apk -Uuv add curl
    - curl -LO https://github.com/sudo-bmitch/docker-stack-wait/raw/main/docker-stack-wait.sh
    - chmod +x docker-stack-wait.sh
  script:
    - docker stack deploy -c $DEPLOY_STACK_FILE --with-registry-auth --prune $DEPLOY_NAME
    - sleep $DEPLOY_WAIT_TIME
    - ./docker-stack-wait.sh -r $DEPLOY_NAME
  only:
    - merge_requests

DEPLOY_DOMAIN 是由 elct9620/ruby-gitlab-ci 是先定義好的,如果要直接使用請自行調整

我們只需要將 name 命名為 review/$CI_COMMIT_REF_NAME 就會自動的被 GitLab 判定為不同的部署,同時還可以利用 review/ 的結構讓 GitLab 已資料夾的形式呈現,把 Review Apps 合成一個群組避免畫面呈現太過複雜。

跟之前不同的地方在於,我們對 environment 設定加入了 on_stop 的設定,讓我們可以在 GitLab 認為需要停止的情況(手動點選、合併後)自動的執行對應的任務。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
review:stop:
  image: docker:stable
  stage: deploy
  environment:
    name: review/$CI_COMMIT_REF_NAME
    action: stop
  script:
    - docker stack rm $DEPLOY_NAME
  only:
    - merge_requests
  when: manual

對應的 review:stop 任務我們使用 when: manual 限定只在「手動操作」的狀況下才會執行,避免部署後馬上就被停止。

environment 的設定中,我們也另外指定了 action: stop 表示這是一個「停止」的動作,最後只需要用同樣的方式利用 GitLab Runner 操作 Docker Swarm 執行 docker stack rm 命令,用相同的命令移除掉像是 91-review-new-function 這樣登記在 Docker Swarm 的服務即可。

在這裡我們應用的關鍵技巧,主要在於像是 DEPLOY_NAME 這類環境變數,需要挑選適合的 GitLab 環境變數去組合,來讓 Docker Swarm 和 GitLab 都能順利辨識出來。

用 GitLab CI 來預載資料

基於我們的 GitLab CI 是可以直接操作 Docker Swarm 的關係,我們其實是能夠直接在 Docker Swarm 執行任意的容器,利用這樣的特性,我們就可以「刻意的」將在 Docker Swarm 執行一些一次性的命令來達到這個效果。

首先,我們原本的 Docker Swarm 是使用自動產生的 Overlay 網路,我們是無法讓其他容器連線到上面,因此需要修改 Docker Compose 的設定,手動的定義網路。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
services:
  # ...
  postgres:
    # ...
    networks: # 資料庫只限內部存取
      - net

networks:
  net:
    driver: overlay
    attachable: true # 可以被附加到其他容器上
  traefik-public:
    external: true

如此一來,我們就可以使用 docker run --network 91-production_net 這樣的選項來將容器網路暫時性的串聯起來,因此我們就可以製作類似這樣的 GitLab CI 任務。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# ...
seed:
  image: docker:stable
  before_script:
    - mkdir -p $HOME/.docker
    - echo "$CI_JOB_TOKEN" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
  script:
   - docker run --rm -t --network ${DEPLOY_NAME}_net
      -e DATABASE_URL=postgres://$POSTGRES_USER:[email protected]/$POSTGRES_DB
      registry.example.com/myapp:${VERSION:-latest}
      rake db:seed
  needs:
    - review:deploy

如此一來,就可以產生一個暫時型的容器使用我們剛剛部署的 Review Apps 並且連接到對應的資料庫,執行我們想要的 rake db:seed 命令來預先載入一些測試用的資料。

然而,這個方法還是有一定程度的限制,像是沒辦法跟 Docker Compose 一樣預先設定好各種環境變數,而這個可能會影響執行命令的結果,因此並不是一個萬能的解決方案。

Docker Swarm 沒有類似 Kubernetes 的 init container 機制可以來處理這些事情,也因此我們可以評估改為使用像是 Sidekiq 之類的方式在背景進行處理。


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

電子報

留言