Rails + Drone CI with ssh CD

👀 3 min read 👀

大家好,我是 Cindy,最近開始在用 Drone CI - 一款用 Go 寫的 CI platform,紀錄一下,我練習用自己的電腦搭配 ngrok 架一個暫時的 CI server,配上 Github 跑測試和部署的實作過程。((因為練習才這樣做唷,正常會架在 GCP 之類的地方,或是可以直接用 cloud drone 不需要自架 server 的方案。))

用 docker + ngrok 在 local 起 drone server

  1. 安裝 ngrok
    brew install --cask ngrok

  2. 註冊 ngrok

  3. 依照官網說明連結帳戶 ngrok authtoken xxxxx

  4. 將 ngrok run 在 port 80
    ngrok http 80

  5. 在 github 新增 OAuth Apps

  6. 新增一組密碼
    openssl rand -hex 16

  7. 新增 docker-compose.yml

    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
    version: "3.9"

    services:
    drone-server:
    image: drone/drone:2
    container_name: drone-server
    ports:
    - 443:443
    - 80:80
    volumes:
    - /var/lib/drone:/var/lib/drone/
    restart: always
    environment:
    - DRONE_GITHUB_CLIENT_ID=xxx
    - DRONE_GITHUB_CLIENT_SECRET=xxx
    - DRONE_RPC_SECRET=ooo
    - DRONE_SERVER_HOST=xxxxxx.ngrok.io
    - DRONE_SERVER_PROTO=https
    - DRONE_DATABASE_DATASOURCE=/var/lib/drone/drone.sqlite
    - DRONE_DATABASE_DRIVER=sqlite3
    - DRONE_DEBUG=true
    - DRONE_LOGS_DEBUG=true
    - DRONE_LOGS_TRACE=true

    drone-runner:
    image: drone/drone-runner-docker:1
    container_name: drone-runner
    ports:
    - 3000:3000
    restart: always
    depends_on:
    - drone-server
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    environment:
    - DRONE_RPC_PROTO=http
    - DRONE_RPC_HOST=xxxxxx.ngrok.io
    - DRONE_RPC_SECRET=ooo
    - DRONE_RUNNER_CAPACITY=2
    - DRONE_DEBUG=true
    - DRONE_LOGS_DEBUG=true
    - DRONE_LOGS_TRACE=true

    執行 docker-compose up -d,用 docker ps 就可以檢查 docker container 是不是有跑起來囉。

    註:如果不想將私密資訊直接寫在 docker-compose.yml,可以寫在 .env 的檔案,設定 docker 的環境變數。

  8. 輸入 ngrok 的網址確認 server 成功起來可以使用

  9. 確認 runner log
    docker logs drone-runner

  10. 在自架的 drone server 網站選擇要跑 CI 的專案,啟用後這邊會自動在 Github 建立 Webhook,之後如果想改 Webhook 的規則也可以直接在 Github 修改

自動測試

在專案新增 .drone.yml

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
kind: pipeline
type: docker
name: default

platform:
os: linux
arch: arm64

steps:
- name: test
image: ruby:3.0.0
environment:
RAILS_ENV: test
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/test_drone_test"
RAILS_MASTER_KEY:
from_secret: rails_master_key
commands:
- bundle install
- bundle exec rake db:create
- bundle exec rake db:migrate
- bundle exec rspec

services:
- name: postgres
image: postgres:13-alpine
environment:
POSTGRES_HOST_AUTH_METHOD: trust

這樣就可以跑測試囉!

ssh 自動部署

用 ssh 的方式進行部署,設定檔的寫法類似下面這樣:

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
53
54
55
56
57
58
59
60
61
62
63
kind: pipeline
type: docker
name: default

...

definitions:
env-settings: &env-settings
host:
from_secret: host
username:
from_secret: username
key:
from_secret: ssh_key
port:
from_secret: ssh_port
# script_stop: 只要其中一個 script 失敗,後面的 script 就不會執行
script_stop: true
# 由於 appleboy/drone-ssh 的 issue,需要重新讀取 rvm 的設定,才不會出現 bundle: command not found 的 error
# issue: https://github.com/appleboy/ssh-action/issues/21
# rvm info: https://rvm.io/rvm/install#3-reload-shell-configuration-amp-test
scripts:
- script: &export-rvm export PATH="$PATH:$HOME/.rvm/bin"
- script: &reload-rvm-setting source ~/.rvm/scripts/rvm

steps:
...
- name: deploy
# image ref: http://plugins.drone.io/appleboy/drone-ssh
image: appleboy/drone-ssh
settings:
<<: *env-settings
script:
- cd RepoName
# ref: https://docs.drone.io/pipeline/environment/reference/drone-tag/
- git fetch origin ${DRONE_TAG}:${DRONE_TAG}
- git checkout ${DRONE_TAG}
- *export-rvm
- *reload-rvm-setting
- bundle install
- bundle exec rails db:migrate
- sudo service sidekiq restart
- passenger-config restart-app /home/deployer/RepoName
when:
ref:
- refs/tags/v*

- name: notification
image: plugins/slack
settings:
webhook:
from_secret: slack_webhook
channel: devops
when:
status:
- success
- failure

trigger:
ref:
- refs/heads/develop
- refs/pull/**
- refs/tags/v*

其他相關參考資料