DnD-mag / magazine

magazine
3 stars 0 forks source link

Rails app 自動化部屬 - hubot 與 heaven #25

Closed kjj6198 closed 5 years ago

kjj6198 commented 5 years ago

前言

目前所在的公司裡頭是直接在本地端的 terminal 跑 cap staging deploy 指令。 capistrano 作為自動部署化的工具非常方便,但難免會遇到幾個問題:

對一家新創來說,越穩定的開發效率和流程,就越能夠專注在產品當中。所以我們希望做到幾件事:

逐漸厭倦了在 terminal 打指令,ssh key 手動加的日子。於是打算自己研究有沒有更流暢的部署流程。

之前在 Sudo 裡頭,幸好有 @ocowchun@henry 兩位懶工,devops 做得非常完整,才能夠專注在開發功能,而不是一堆繁複的設定當中。(雖然才剛開發完就關閉服務了…)

目前覺得最合適的解決方案是搭配 hubot-deploy 以及 heaven 來幫助部署。

但 heaven 的文件實在寫的有夠爛。

看了老半天,甚至看了一下 source code 才知道到底該怎麼設定。於是決定將整個設定流程分享給大家,希望能夠減少其他 devops 們走歪路的時間。

主要流程

hubot 接收到部署指令後,會發送 github deployment,同時會觸發 deployment 這個事件,這時 github 就會發送 POST 給在 webhook 設置的 url(這邊接收者為 heaven),heaven 接收到請求之後,就會開始部署,再一一回傳我們想要知道的部數狀況。

hubot-deploy

hubot-deploy 能夠用 slack 對 slack-bot 下指令的方式建立 github 的 deployment event。

heaven

是一個 Rails 的 application。主要有一個 /events 負責接收從 github deployment 傳來的 deployment 與 payload。

設定步驟

heaven 的文件寫得不明所以hubot-deploy 也是草草帶過。幾乎只能靠著他們提供的流程圖,不斷的試錯與通靈。

設定 hubot-deploy

  1. 利用 yeoman 產生 hubot,並且選擇 adapterslack

  2. package.json 中加入 hubot-deploy,或者 run npm install hubot-deploy --save-dev

  3. external-scripts.json 裡頭加入 hubot-deploy

  4. apps.json 中設定想要部署的 repos 有哪些:

    {
     "repo_name": {
       "provider": "capistrano",
       "auto_merge": false,
       "repository": "kjj6198/deploy101",
       "environments": ["production", "staging"]
     }
    }

    這些資料在 hubot 送出 deployment 時會一併塞入 payload 當中。像是這樣:

    payload: {
     "name": "repo_name",
     "robotName": "yourrobot",
     "hosts": "",
     "notify": {
       "adapter": "slack",
       "room": "123456789",
       "user": "123456789",
       "user_name": "kjj6198"
     },
     "config": {
       "provider": "capistrano",
       "auto_merge": false,
       "repository": "kjj6198/deploy101",
       "environments": [
         "production",
         "staging"
       ]
     }
    }

特別要注意的是,provider 的欄位之後會送給 heaven,所以 provider 的值必須是 heaven 有的(之後會提到),或是自己實作 Provider。

這樣子我們的 hubot 就算設定完成了。先部署到 heroku 上測試看看,部署到 heroku 很簡單:

heroku login
git init
git add .
git commit "init"
heroku create
git push heroku master

部署成功後,比較重要的變數有幾個:

全域變數可以到 heroku 的 dashboard 或是直接用 command line 設定:

heroku config:set HUBOT_GITHUB_TOKEN=abcccc
heroku config:set HUBOT_SLACK_TOKEN=abcccc

測試一下是否成功。在你設定的頻道中輸入 hubot deploy:version

其中的 hubot 要跟你的機器人名稱相同,例如機器人的名稱為 tripmomo,那麼我就要輸入 tripmomo deploy:version

成功的話 hubot 會回應你目前的版本訊息。

  1. 確認 hubot 有送出 deployment 事件。輸入 hubot deploy app to statging

  2. 輸入curl -H "Authorization: token YOUR_GITHUB_TOKEN" https://api.github.com/repos/my-github/my-repo/deployments看看 deployment 是否建立成功。如果成功會回傳:

    {
       "url": "https://api.github.com/repos/my-github/my-repo/deployments/28301325",
       "id": 123456,
       "sha": "2e3xxxxxxxaaaaaaabbbbbbb",
       "ref": "develop",
       "task": "deploy",
       "payload": { // from apps.json
         "name": "my-app",
         "robotName": "tripmomo",
         "hosts": "",
         "notify": {
           "adapter": "slack",
           "room": "aabbccdd",
           "user": "aabbccdd",
           "user_name": "kalan.chen"
         },
         "config": { 
           "provider": "capistrano",
           "auto_merge": false,
           "repository": "my-github/my-repo",
           "environments": [
             "production",
             "staging"
           ]
         }
       },
       "environment": "staging",
       "description": "deploy on staging from hubot-deploy-v0.13.27",
       "creator": {
         "login": "kjj6198",
         "id": 123456,
         "avatar_url": "https://avatars2.githubusercontent.com/u/123456?v=3",
         "gravatar_id": "",
         "url": "https://api.github.com/users/kjj6198",
         "html_url": "https://github.com/kjj6198",
         "followers_url": "https://api.github.com/users/kjj6198/followers",
         "following_url": "https://api.github.com/users/kjj6198/following{/other_user}",
         "gists_url": "https://api.github.com/users/kjj6198/gists{/gist_id}",
         "starred_url": "https://api.github.com/users/kjj6198/starred{/owner}{/repo}",
         "subscriptions_url": "https://api.github.com/users/kjj6198/subscriptions",
         "organizations_url": "https://api.github.com/users/kjj6198/orgs",
         "repos_url": "https://api.github.com/users/kalanchen/repos",
         "events_url": "https://api.github.com/users/kjj6198/events{/privacy}",
         "received_events_url":"https://api.github.com/users/kjj6198/received_events",
         "type": "User",
         "site_admin": false
       },
       "created_at": "2017-03-01T12:24:20Z",
       "updated_at": "2017-03-01T12:24:20Z",
       "statuses_url": "https://api.github.com/repos/my-github/my-repo/deployments/12345667/statuses",
       "repository_url": "https://api.github.com/repos/my-github/my-repo"
     }

    更多 deployment API 可以到 github deployment API 看看。

設定 heaven

補充說明 DEPLOYMENT_PRIVATE_KEY:原始檔案長這樣

-----BEGIN RSA PRIVATE KEY-----
MJVGa/WNT9aFs63ykxLCdGzav8CfQ5vKXrLrllHXUYFaB2yaN72L+fSsXAy9zMs2
vy6wV2fB6j3YrVNCnBwUUNGTX9Ka6eeK98dCvHVyyE9Iz3CJAWZxaI03Px/xX9ps
M4kDWe7IA6+mnuCVSzwQVWMdOoAXbQbhGdfeixbqljNhJrKW/jA9w4BNarwGYv4E
0MwdU9x7zpk826ytza87yXHSdNuTKcsGQk4XHMYxJECj4EM8vTlVlEyEXZtCeh2z
P4bjYkTcBom4nC/q7Ea7Pmy1iDJqs0qc1L/xtNMypMhx4iIaeDVawkvBaL6t9IPT
KVuC9Y1uw5nJP1gwxXa5qoazhcikzqRYmaeWIzsZrcVShZBrJO9/a/APxXY7qJpJ
0r1YYTykw7THYj2QYiv8cfF64/vh9cB0NELEp5hIuS82Mf6CjqRR7QYR+By3uIdD
hQ77NMpQlmIC+TCJsLoADqwmEEZCiQSejtkXXtN/mNl581jP8+ViNkWZfPYWe7g6
yUeXVN1cBPo6AIu+lStE+SlR8lbu7sdpn6lid1pJf50zeythabze81y/nrAdx+Jn
scACBJBrERkhm2wdULkqwMV2g0U53YpYVAs2fFU1hGzRcE5zF1sdy9RLLX45Mzrm
lRErTbSUcnoQJhhCso5uNY6MMnr/rQF920KA0Ufr40IBcQ8bOSX7lJucST5bZLDg
H7g16rimHgK4I9rrvKy4plvbolfpuKGMYJDS3Q7IW5cL5lWLU3HaVSn+VyZe8p3A
prVx0XmSCwpmUzbDI6FoqniVPVdgis2tV1uKdnJPVn0DoK0ersosGXmALytbYLeE
arH/cIlGGCoGbIX+Iv3u8aICBEG2eR8eXmQSlGI5rp9hGK/JrlkL3PywVmPw4Efi
atiS6Y12Tuu8bdpPxBTzXK3PoZ23Pc+1l7NXXIzBeGnj56bALOIbAY5kg+lIRdtP
NSTAW8IVgFJUl4uzy/NXn/ewiE093ZVs59I2x4OoS14S20mkM/ldWbvlVm4Z3JxC
xIWsIV8aLznttic5MJUGjGoqH1Brg0o1HyWdkoEcC1N0G57oO4pN4UTD5co5xY9j
Ai2NIcFCYzqrdTfSlPWJBZLhjZ5hOXIwuTeJfRxDAVphaUqfpXb3o3URGRWiGENA
kIYKiq4XeNguwrFBzg5CB7NEKvjbjJ31GI26yAPa7yrKpuNFAjPpO6JKdL8slvx8
GXCOSbhGPFxzmtYzEeMxmnHqOa0Z953XeheKfJoipqRAyENxPBvclDonqVfxuTvw
cZzqFD+XjDJCJ5INwuwk2WupVzQjzV6TagcIX63Kq1Z9HSoFIBiCrdLzTMDG4Ro3
2wpN1tFQFz6alvwKtifGwhvG3qqmsfcQqw56gGY0DWIqG5x/thdG7UzZT7iMVDJV
LAO5wNnBK6L+feov9LqP7ONAonBVawmTv0ArjVhhkYZEi6d+ymvPpL1ORFAymLne
dpk4VmmmQvkUu0KudRqulavTIrnXFkuv2va+5X9mHGoNNMo1TXk2XX1eM4Rc7nAY
6IwPyAuFEtT5ocWBklB/qUZtdu4fG876o0X87GklR9ZfPG+tWpH2F+1j1mMHKuiP
-----END RSA PRIVATE KEY-----

要修改成:

-----BEGIN RSA PRIVATE KEY-----\nMJVGa/WNT9aFs63ykxLCdGzav8CfQ5vKXrLrllHXUYFaB2yaN72L+fSsXAy9zMs2\nvy6wV2fB6j3YrVNCnBwUUNGTX9Ka6eeK98dCvHVyyE9Iz3CJAWZxaI03Px/xX9ps\nM4kDWe7IA6+mnuCVSzwQVWMdOoAXbQbhGdfeixbqljNhJrKW/jA9w4BNarwGYv4E\n0MwdU9x7zpk826ytza87yXHSdNuTKcsGQk4XHMYxJECj4EM8vTlVlEyEXZtCeh2z\nP4bjYkTcBom4nC/q7Ea7Pmy1iDJqs0qc1L/xtNMypMhx4iIaeDVawkvBaL6t9IPT\nKVuC9Y1uw5nJP1gwxXa5qoazhcikzqRYmaeWIzsZrcVShZBrJO9/a/APxXY7qJpJ\n0r1YYTykw7THYj2QYiv8cfF64/vh9cB0NELEp5hIuS82Mf6CjqRR7QYR+By3uIdD\nhQ77NMpQlmIC+TCJsLohtJEmEEZCiQSejtkXXtN/mNl581jP8+ViNkWZfPYWe7g6\nyUeXVN1cBPo6AIu+lStE+SlR8lbu7sdpn6lid1pJf50zeythabze81y/nrAdx+Jn\nscACBJBrERkhm2wdULkqwMV2g0U53YpYVAs2fFU1hGzRcE5zF1sdy9RLLX45Mzrm\nlRErTbSUcnoQJhhCso5uNY6MMnr/rQF920KA0Ufr40IBcQ8bOSX7lJucST5bZLDg\nH7g16rimHgK4I9rrvKy4plvbolfpuKGMYJDS3Q7IW5cL5lWLU3HaVSn+VyZe8p3A\nprVx0XmSCwpmUzbDI6FoqniVPVdgis2tV1uKdnJPVn0DoK0ersosGXmALytbYLeE\narH/cIlGGCoGbIX+Iv3u8aICBEG2eR8eXmQSlGI5rp9hGK/JrlkL3PywVmPw4Efi\natiS6Y12Tuu8bdpPxBTzXK3PoZ23Pc+1l7NXXIzBeGnj56bALOIbAY5kg+lIRdtP\nNSTAW8IVgFJUl4uzy/NXn/ewiE093ZVs59I2x4OoS14S20mkM/ldWbvlVm4Z3JxC\nxIWsIV8aLznttic5MJUGjGoqH1Brg0o1HyWdkoEcC1N0G57oO4pN4UTD5co5xY9j\nAi2NIcFCYzqrdTfSlPWJBZLhjZ5hOXIwuTeJfRxDAVphaUqfpXb3o3URGRWiGENA\nkIYKiq4XeNguwrFBzg5CB7NEKvjbjJ31GI26yAPa7yrKpuNFAjPpO6JKdL8slvx8\nGXCOSbhGPFxzmtYzEeMxmnHqOa0Z953XeheKfJoipqRAyENxPBvclDonqVfxuTvw\ncZzqFD+XjDJCJ5INwuwk2WupVzQjzV6TagcIX63Kq1Z9HSoFIBiCrdLzTMDG4Ro3\n2wpN1tFQFz6alvwKtifGwhvG3qqmsfcQqw56gGY0DWIqG5x/thdG7UzZT7iMVDJV\nLAO5wNnBK6L+feov9LqP7ONAonBVawmTv0ArjVhhkYZEi6d+ymvPpL1ORFAymLne\ndpk4VmmmQvkUu0KudRqulavTIrnXFkuv2va+5X9mHGoNNMo1TXk2XX1eM4Rc7nAY\n6IwPyAuFEtT5ocWBklB/qUZtdu4fG876o0X87GklR9ZfPG+tWpH2F+1j1mMHKuiP\n-----END RSA PRIVATE KEY-----

既然公開,這組 private key 當然報廢了

設定 Gemfile

因為 heaven 的動作會是拉下最新的 repo 後,執行 cap ... deploy 的指令,所以capistrano 的版本必須跟要部署的那個版本相同。同時,也要注意任何 asset 相關的 gem 也要一併放入 heaven。舉例來說,如果我的 Capfile 有用到

gem 'capistrano', '3.4.0'
gem 'capistrano3-unicorn'
gem 'capistrano-rails'
gem 'sitemap_generator'
gem 'capistrano-rvm'

那麼就要將這些 gem 加入 heaven 的 Gemfile 當中。因為 heaven 會將要部署的 repo 抓下來之後,進去資料夾輸入 cap staging ... deploy 的指令,所以如果沒有安裝相對應的 gem,heaven 就沒辦法部署了。

串接 github deployment

部署

如果是部署到 heroku 的話,因為 heaven 要開 redis 跟 resque。記得加入相對應的 add-on 以及 REDIS_URL

同時別忘記了要建立資料庫 heroku run rake db:migrate

hubot-deploy 常用指令

筆記

後記

通常在公司裡頭,開發團隊人數不多的話,devops 都是由後端兼任的,前端比較少接觸。不過用「我是前端,我不需要管 devops」這種藉口搪塞自己不去學習好像也說不太過去,畢竟開發一個健全的系統絕對不可能只有前端而已。

這篇文章試著將文件中沒有提到或是省略的步驟整合起來,heaven 跟 hubot-deploy 的文件中有太多沒有提到的細節,導致整合起來時需要花不少時間試錯。希望能夠節省大家踩雷跟翻原始碼的時間。

這篇文章還有許多 devops 的細節沒有詳述,畢竟建立一套完整的 devops pipeline 需要時間,自己對於 CI/CD 的設定也還不夠熟悉。

參考資源: