---
title: "Rails 部署實踐 - 使用 GitLab 的 Review Apps 機制"
date: 2022-07-22T00:00:00+08:00
publishDate: 2022-07-22T00:00:00Z
lastmod: 2023-09-03T17:41:34+08:00
tags: ["Rails","教學","部署","實作","Rails 部署實踐","Docker Swarm","GitLab CI","Review Apps"]
series: "rails-deployment-in-practice"
toc: true
permalink: "https://blog.aotoki.me/posts/2022/07/22/rails-deployment-in-practice-use-gitlab-review-apps/"
language: "zh-tw"
---


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

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

<!--more-->

## Review Apps 的運作方式{#how-review-apps-work}

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

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

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

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

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

## 加入停止機制{#add-stop-job}

我們在[整合 GitLab CI 自動部署](/posts/2022/07/15/rails-deployment-in-practice-use-gitlab-ci-with-docker-swarm/)這篇文章中，已經可以自動的部署到正式環境，基本上只需要使用相同的方式，加入下面這段調整就可以支援 Review Apps 的啟動，然而我們還需要停止的處理。

```yaml
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](https://github.com/elct9620/ruby-gitlab-ci) 是先定義好的，如果要直接使用請自行調整

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

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

```yaml
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 來預載資料{#seed-with-gitlab-ci}

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

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

```yaml
services:
  # ...
  postgres:
    # ...
    networks: # 資料庫只限內部存取
      - net

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

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

```yaml
# ...
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:$POSTGRES_PASSWORD@postgres/$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 之類的方式在背景進行處理。

---

如果想在第一時間收到更新，歡迎[訂閱弦而時習之](https://mailchi.mp/aotoki/rails-deployment-in-practice)在這系列文章更新時收到通知，如果有希望了解的知識，可以利用[Rails 部署實踐回饋表單](https://us4.list-manage.com/survey?u=dd3d68032c0510041f1302539&id=f25e0dc43e&attribution=false)告訴我。

