質問やフィードバックがありましたら、フォームからお願いします
本文は台湾華語で、ChatGPT で翻訳している記事なので、不確かな部分や間違いがあるかもしれません。ご了承ください
前言
現在の会社では、ローカルのターミナルで cap staging deploy
コマンドを実行しています。capistrano は自動デプロイのツールとして非常に便利ですが、いくつかの問題に直面することがあります:
- チームメンバー全員が同じ環境を持っているわけではない
- みんながデプロイしているため、staging 上でどのブランチが使われているのか全く分からない。
- デプロイがローカルに依存している。
新興企業にとって、開発の効率とプロセスが安定しているほど、製品に集中できるようになります。そこで、私たちは以下の目標を達成したいと考えています:
- 開発チームが簡単にデプロイできること
- ローカルでコマンドを実行してデプロイする必要がなく、sshの設定も不要
- コンピュータを開いていなくても簡単にデプロイできること
- デプロイの状況を記録すること
- 問題が発生した場合、迅速に前のバージョンにロールバックできること
ターミナルでコマンドを打つのに徐々に飽きてきて、sshキーを手動で追加する日々にうんざりしました。そこで、よりスムーズなデプロイプロセスを模索することにしました。
以前、Sudoで@ocowchun と @henry という二人の怠け者がいて、devopsが非常にしっかりしていたおかげで、機能開発に専念できましたが、複雑な設定に悩まされることはありませんでした。(開発が終わった後、サービスが閉じられてしまったのは残念ですが…)
今のところ、最適な解決策は hubot-deploy
と heaven
を組み合わせてデプロイを支援することだと考えています。
ただし、heavenのドキュメントは本当にひどく書かれています。
しばらくの間、ソースコードを見ながら設定方法を理解する必要がありました。そこで、設定プロセス全体を共有し、他のdevopsが不必要に時間を浪費することを減らしたいと思います。
主要プロセス
{% asset_img "process.png" "Github deployment process" %}
hubotがデプロイコマンドを受け取ると、github deployment を送信し、同時に deployment
イベントをトリガーします。この時、GitHubはwebhookで設定したURL(ここでは受信者は heaven
)にPOSTリクエストを送信します。heavenがリクエストを受け取ると、デプロイを開始し、私たちが知りたいデプロイ状況を一つ一つ返します。
hubot-deploy
hubot-deployは、slackを通じてslack-botに指示を出すことで、GitHubのdeploymentイベントを作成できます。
heaven
heavenはRailsアプリケーションです。主に /events
エンドポイントが、GitHubから送信されたdeploymentとpayloadを受け取る役割を担っています。
設定ステップ
heavenのドキュメントは非常に不明瞭で、hubot-deployも簡単に触れられているだけです。ほぼ彼らが提供したフローチャートに頼りながら、試行錯誤を繰り返さなければなりませんでした。
hubot-deployの設定
-
yeomanを使ってhubotを生成し、
adapter
としてslack
を選択します。 -
package.json
にhubot-deploy
を追加するか、npm install hubot-deploy --save-dev
を実行します。 -
external-scripts.json
にhubot-deploy
を追加します。 -
apps.json
でデプロイしたいリポジトリを設定します:{ "repo_name": { "provider": "capistrano", "auto_merge": false, "repository": "kjj6198/deploy101", "environments": ["production", "staging"] } }
これらのデータは、hubotがデプロイメントを送信する際に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
デプロイが成功した後、重要な変数はいくつかあります:
変数名 | 用途 |
---|---|
HUBOT_GITHUB_TOKEN | GITHUB_TOKENは、個人アカウント > settings > personal access tokens で設定します。権限を設定して、hubotがリポジトリのデプロイメントを作成するためにrepoにチェックを入れます。 |
HUBOT_SLACK_TOKEN | あなたのslack-botトークン。こちらから設定できます:リンク |
グローバル変数は、herokuのダッシュボードまたはコマンドラインから直接設定できます:
heroku config:set HUBOT_GITHUB_TOKEN=abcccc
heroku config:set HUBOT_SLACK_TOKEN=abcccc
設定が成功したか確認します。設定したチャンネルに hubot deploy:version
と入力します。
{% asset_img "success.png" "success" %}
ここでの hubot
はあなたのロボットの名前と一致する必要があります。例えば、ロボットの名前がtripmomoであれば、 tripmomo deploy:version
と入力します。
成功すると、hubotは現在のバージョン情報を返してくれます。
-
hubotがデプロイメントイベントを送信したか確認します。入力は
hubot deploy app to staging
です。 -
curl -H "Authorization: token YOUR_GITHUB_TOKEN" https://api.github.com/repos/my-github/my-repo/deployments
を入力して、デプロイメントが成功したか確認します。成功した場合、以下のようなレスポンスが返ります:{ "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" }
さらに多くのデプロイメントAPIについては、github deployment APIを参照してください。
heavenの設定
- heaven にアクセスして、リポジトリをクローンします。
- グローバル変数を設定します
変数名 | 用途 |
---|---|
DEPLOYMENT_PRIVATE_KEY | heavenはsshでログインするため、プライベートキーが必要です。サーバーがec2上にある場合、pem形式でも設定できます。 |
GITHUB_CLIENT_ID | 個人設定ページ > OAuth application で生成します。 |
GITHUB_CLIENT_SECRET | 個人設定ページ > OAuth application で生成します。 |
DATABASE_URL | heavenはデプロイメントの記録をデータベースに作成します。 |
GITHUB_TOKEN | heavenはgistをstdoutとstderrとして使用します。トークンを設定する際には、gist にチェックを入れてください。 |
他の変数については、こちらで確認できます。
補足説明として、DEPLOYMENT_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
--
これを次のように変更します:
--\nMJVGa/WNT9aFs63ykxLCdGzav8CfQ5vKXrLrllHXUYFaB2yaN72L+fSsXAy9zMs2\nvy6wV2fB6j3YrVNCnBwUUNGTX9Ka6eeK98dCvHVyyE9Iz3CJAWZxaI03Px/xX9ps\nM4kDWe7IA6+mnuCVSzwQVWMdOoAXbQbhGdfeixbqljNhJrKW/jA9w4BNarwGYv4E\n0MwdU9x7zpk826ytza87yXHSdNuTKcsGQk4XHMYxJECj4EM8vTlVlEyEXZtCeh2z\nP4bjYkTcBom4nC/q7Ea7Pmy1iDJqs0qc1L/xtNMypMhx4iIaeDVawkvBaL6t9IPT\nKVuC9Y1uw5nJP1gwxXa5qoazhcikzqRYmaeWIzsZrcVShZBrJO9/a/APxXY7qJpJ\n0r1YYTykw7THYj2QYiv8cfF64/vh9cB0NELEp5hIuS82Mf6CjqRR7QYR+By3uIdD\nhQ77NMpQlmIC+TCJsLoADqwmEEZCiQSejtkXXtN/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-----
公開されたので、このプライベートキーは廃棄されます
Gemfileの設定
heavenの動作は、最新のリポジトリを取得した後、 cap ... deploy
コマンドを実行するため、capistranoのバージョンはデプロイするバージョンと同じでなければなりません。また、アセットに関連するgemもheavenに含める必要があります。例えば、私のCapfileで次のように記述している場合:
gem 'capistrano', '3.4.0'
gem 'capistrano3-unicorn'
gem 'capistrano-rails'
gem 'sitemap_generator'
gem 'capistrano-rvm'
これらのgemをheavenのGemfileに追加する必要があります。heavenはデプロイするリポジトリを取得した後、フォルダに入って cap staging ... deploy
コマンドを実行するため、対応するgemがインストールされていないと、heavenはデプロイできなくなります。
GitHubデプロイメントの接続
- まず、リポジトリのsettings > deploy keyにssh-keyを追加します。
- リポジトリのsettings > webhooks > add webhookに移動します。
- Payload URLにあなたのheavenデプロイホストのURLを入力します。例:https://yourapp.com.tw/events。変更したい場合は、heavenリポジトリの
routes.rb
で修正できます。 - Content Typeは
application/json
を選択します。 - Secretは必要に応じて記入します。
- このwebhookがどのイベントを監視するか尋ねられますが、私たちはデプロイメントを使用しているため、デプロイメントおよびデプロイメントステータスを選択します。
デプロイ
herokuにデプロイする場合、heavenはredisとresqueを起動する必要があります。対応するアドオンと REDIS_URL
を追加することを忘れないでください。
また、データベースを作成することも忘れずに heroku run rake db:migrate
を実行します。
hubot-deployのよく使うコマンド
hubot deploy:version
現在のバージョンhubot deploy repo
:apps.json
に基づいて指定のリポジトリ名をデプロイします。hubot deploy repo/branch
:指定したリポジトリの特定のブランチをデフォルトの環境にデプロイします。HUBOT_DEPLOY_DEFAULT_ENVIRONMENT
を設定することで決定できます。hubot deploy repo/branch to staging
:指定したリポジトリのブランチをstaging
にデプロイします。
ノート
-
heavenのドキュメントは不明瞭ですが、コードとテストはしっかりと書かれているため、Rubyに慣れている開発者は、heavenを構築し、コードを少し変更してルートを追加し、UIを作成してワンクリックデプロイを実現できます。
-
OptionParser::AmbiguousOption: ambiguous option: -s
:Capistranoの更新後にコマンドが変更されたのか不明です。解決策としては、lib/heaven/provider/capistrano.rb
のdeploy_command
を修正します。module Heaven # プロバイダー用のトップレベルモジュール。 module Provider # capistranoプロバイダー。 class Capistrano < DefaultProvider ..... def execute return execute_and_log(["/usr/bin/true"]) if Rails.env.test? unless File.exist?(checkout_directory) log "Cloning #{repository_url} into #{checkout_directory}" execute_and_log(["git", "clone", clone_url, checkout_directory]) end Dir.chdir(checkout_directory) do log "Fetching the latest code" execute_and_log(%w{git fetch}) execute_and_log(["git", "reset", "--hard", sha]) deploy_command = [cap_path, environment, "デプロイするcapコマンド"] log "Executing capistrano: #{deploy_command.join(" ")}" execute_and_log(deploy_command) end end end end end
-
heavenはデプロイ時にgistをstdoutとstderrとして使用するため、GITHUB_TOKENを設定する際には必ずgistのスコープにチェックを入れることを忘れないでください。
-
Net::SSH::AuthenticationFailed: Authentication failed for user apps@staging.tripmoment.com
:SSHプライベートキーの設定に誤りがあります。このSSHキーがGitHubに追加されていることを確認し、次にpassphrase
を削除し、SSHプライベートキーを一行にして\n
を加えます。 -
ArgumentError: Could not parse PKey: no start line
プライベートSSHキーのpassphraseを削除しなかったためです。
後記
通常、会社で開発チームの人数が少ない場合、devopsはバックエンドが兼任していることが多く、フロントエンドはあまり関与しないことが一般的です。しかし、「私はフロントエンドだからdevopsに関わる必要がない」という言い訳で自分を納得させるのは少し無理があるかもしれません。健全なシステムを構築するには、絶対にフロントエンドだけでは不可能です。
この記事では、ドキュメントに記載されていない、または省略された手順をまとめました。heavenとhubot-deployのドキュメントには多くの詳細が記載されておらず、統合する際にかなりの時間を試行錯誤に費やす必要がありました。皆さんが無駄な時間を過ごしたり、ソースコードを翻訳する時間を節約できることを願っています。
この記事にはまだ多くのdevopsの詳細が記載されていません。完全なdevopsパイプラインを構築するには時間がかかり、私自身もCI/CDの設定についてはまだ不十分です。
参考リソース:
この記事が役に立ったと思ったら、下のリンクからコーヒーを奢ってくれると嬉しいです ☕ 私の普通の一日が輝かしいものになります ✨
☕Buy me a coffee