こんにちは、駆け出しエンジニアのよしこ @k2_yoshikoukiです
CI/CDってカッコいいですよね。なんかこう、カッコいいんですよね(語彙力)
しかし「どうやって実装したらいいのかワケワカメ\(^o^)/」というエンジニアの方は多いと思います。でも実際はとても簡単なんだよということを知ってほしかったので、10分で読める記事で CI/CD のうちのCD(自動デプロイ)を実装していきたいと思います。
実作業時間は詰まらなかったら30分かからないくらいです。
ゴール
CI/CD をやってみたいがやったことがないし何から手を付けたら良いか分からないエンジニア向けに、Rails アプリ (6系) で自動デプロイを最速で実装する。
結果、GitHub Actions を使ったCI/CDの大枠を把握できて自力で実装できるようになる
実装フローを見ていく
- Rails アプリを作る
- Heroku に手動デプロイして動作確認
- GitHub Actions の初期設定
-
./.github/workflows/{好きな名前}.yml
の作成 - 必要な鍵などの取得・GitHubへ設定
-
すでにアプリケーションがある方は 3 からです。たった2ステップで自動デプロイが実装できます。早速やっていきましょう
手順
Railsアプリの準備
-
rails new sample-rails-with-gha
~/tmp/sample-rails-with-gha ❯ rails -v Rails 6.0.3.4 ~/tmp/sample-rails-with-gha ❯ ruby -v ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19] # 後から2.7.2にアップデートします(デバックのため)
bundle install rails server
リポジトリの準備
-
GitHub上にリポジトリを作る
https://github.com/new -
ローカルにリポジトリを登録
git remote add origin https://github.com/yoshikouki/sample-rails-with-gha.git

- アップロード
```bash
git add -A
git commit -m "rails new"
git push -u origin master
```
### デプロイの初期設定と初回デプロイ
- 今回はデプロイ先に Heroku を使う(簡単なので)
```bash
~/tmp/sample-rails-with-gha master
❯ heroku --version
zsh: command not found: heroku
```
- Heroku 入ってなかった・・・
```bash
brew tap heroku/brew && brew install heroku
# https://devcenter.heroku.com/articles/heroku-cli
```
- Heroku アカウントを作っておく
[https://signup.heroku.com/](https://signup.heroku.com/)
- Heroku にログイン(ターミナル上)
```bash
heroku login --interactive
```
- Heroku 用のアプリケーションを作る(アップロードする場所を作る)
```bash
heroku create
# URL を控えておく
# https://sleepy-beyond-32826.herokuapp.com/
# `$ heroku open` でも開ける
```

- Heroku は sqlite3 に対応していないため、`./Gemfile` を操作する

- Heroku へデプロイする
```bash
git push heroku master
```
- あらら

- エラーログ
```bash
$ heroku logs
2020-11-22T14:39:05.610528+00:00 app[web.1]: I, [2020-11-22T14:39:05.610419 #4] INFO -- : [a6b71e5b-f13a-4224-a857-9b791da3ecc4] Started GET "/" for 133.32.232.41 at 2020-11-22 14:39:05 +0000
2020-11-22T14:39:05.611222+00:00 app[web.1]: F, [2020-11-22T14:39:05.611152 #4] FATAL -- : [a6b71e5b-f13a-4224-a857-9b791da3ecc4]
2020-11-22T14:39:05.611223+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] ActionController::RoutingError (No route matches [GET] "/"):
2020-11-22T14:39:05.611224+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4]
2020-11-22T14:39:05.611225+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] actionpack (6.0.3.4) lib/action_dispatch/middleware/debug_exceptions.rb:36:in `call'
2020-11-22T14:39:05.611225+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] actionpack (6.0.3.4) lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
2020-11-22T14:39:05.611226+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] railties (6.0.3.4) lib/rails/rack/logger.rb:37:in `call_app'
2020-11-22T14:39:05.611226+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] railties (6.0.3.4) lib/rails/rack/logger.rb:26:in `block in call'
2020-11-22T14:39:05.611226+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] activesupport (6.0.3.4) lib/active_support/tagged_logging.rb:80:in `block in tagged'
2020-11-22T14:39:05.611227+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] activesupport (6.0.3.4) lib/active_support/tagged_logging.rb:28:in `tagged'
2020-11-22T14:39:05.611227+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] activesupport (6.0.3.4) lib/active_support/tagged_logging.rb:80:in `tagged'
2020-11-22T14:39:05.611228+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] railties (6.0.3.4) lib/rails/rack/logger.rb:26:in `call'
2020-11-22T14:39:05.611228+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] actionpack (6.0.3.4) lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
2020-11-22T14:39:05.611228+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] actionpack (6.0.3.4) lib/action_dispatch/middleware/request_id.rb:27:in `call'
2020-11-22T14:39:05.611229+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] rack (2.2.3) lib/rack/method_override.rb:24:in `call'
2020-11-22T14:39:05.611229+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] rack (2.2.3) lib/rack/runtime.rb:22:in `call'
2020-11-22T14:39:05.611230+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] activesupport (6.0.3.4) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
2020-11-22T14:39:05.611230+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] actionpack (6.0.3.4) lib/action_dispatch/middleware/executor.rb:14:in `call'
2020-11-22T14:39:05.611231+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] actionpack (6.0.3.4) lib/action_dispatch/middleware/static.rb:126:in `call'
2020-11-22T14:39:05.611231+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] rack (2.2.3) lib/rack/sendfile.rb:110:in `call'
2020-11-22T14:39:05.611232+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] actionpack (6.0.3.4) lib/action_dispatch/middleware/host_authorization.rb:76:in `call'
2020-11-22T14:39:05.611232+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] railties (6.0.3.4) lib/rails/engine.rb:527:in `call'
2020-11-22T14:39:05.611232+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] puma (4.3.6) lib/puma/configuration.rb:228:in `call'
2020-11-22T14:39:05.611233+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] puma (4.3.6) lib/puma/server.rb:713:in `handle_request'
2020-11-22T14:39:05.611233+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] puma (4.3.6) lib/puma/server.rb:472:in `process_client'
2020-11-22T14:39:05.611233+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] puma (4.3.6) lib/puma/server.rb:328:in `block in run'
2020-11-22T14:39:05.611234+00:00 app[web.1]: [a6b71e5b-f13a-4224-a857-9b791da3ecc4] puma (4.3.6) lib/puma/thread_pool.rb:134:in `block in spawn_thread'
2020-11-22T14:39:05.612663+00:00 heroku[router]: at=info method=GET path="/" host=sleepy-beyond-32826.herokuapp.com request_id=a6b71e5b-f13a-4224-a857-9b791da3ecc4 fwd="133.32.232.41" dyno=web.1 connect=1ms service=4ms status=404 bytes=1902 protocol=https
2020-11-22T14:39:05.858618+00:00 heroku[router]: at=info method=GET path="/favicon.ico" host=sleepy-beyond-32826.herokuapp.com request_id=831fe7f9-bc01-496d-a9ac-380c9c89dd3c fwd="133.32.232.41" dyno=web.1 connect=1ms service=2ms status=200 bytes=143 protocol=https
```
- なるほどねえ(分かっていない)
`ActionController::RoutingError (No route matches [GET] "/"):`
```ruby
# ./config/routes
Rails.application.routes.draw do
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
```
- ないものは作ればいいんだよ(ドヤ顔)
```shell
rails generate controller Home index
```
- ルーティング `./config/routes` もイジる

- よしよし(手動デプロイで動作を確認)

### GitHub Actions を設定していく
- 空の設定ファイルを作る
```bash
mkdir -p ./.github/workflows
tocuh -p ./.github/workflows/sample_cd.yml
```
- 設定していく
- 設定ファイル
```yaml
name: Deploy to sample-rails-with-gha
on:
# master ブランチにプッシュされた場合に動作する
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# GitHub リポジトリからコードを引っ張ってくる
- name: Checkout
uses: actions/checkout@v2
# 前回のコンテナキャッシュがあれば使用する。なければキャッシュを作る
- name: Cache multiple paths
uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-
# Ruby をインストールする
- name: Set up Ruby 2.7
uses: actions/setup-ruby@v1
with:
ruby-version: 2.7
# バンドラーをインストールし、初期化する
- name: Bundle install
run: |
gem install bundler
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
# ヘロクにデプロイする
- uses: akhileshns/heroku-deploy@v3.6.8 # This is the action
with:
heroku_api_key: ${{secrets.HEROKU_API_KEY}}
heroku_app_name: "sleepy-beyond-32826"
heroku_email: "yoshikouki@gmail.com"
```
- 簡単な設定ファイルなので説明はファイル内のコメントを参考にしてください。
- アクションという Ruby でいう Gem のような機能を使用しています。
- ドキュメント
- [actions/cache](https://github.com/actions/cache/blob/main/examples.md#ruby---bundler)
[https://github.com/actions/cache/blob/main/examples.md#ruby---bundler](https://github.com/actions/cache/blob/main/examples.md#ruby---bundler)
- [actions/setup-ruby
https://github.com/actions/setup-ruby#setup-ruby](https://github.com/actions/setup-ruby#setup-ruby)
- [actions/deploy-to-heroku](https://github.com/marketplace/actions/deploy-to-heroku#getting-started)
[https://github.com/marketplace/actions/deploy-to-heroku#getting-started](https://github.com/marketplace/actions/deploy-to-heroku#getting-started)
- `secrets.HEROKU_API_KEY` が必要なので Heroku で取得する
- [https://dashboard.heroku.com/account](https://dashboard.heroku.com/account)
もしくは `自分のアイコン` > `Account settings`
- 下部 `API Key`
ここのKeyをコピーする
- 取得した Key をGitHub で使用できるように設定する
- `sample-rails-with-gha` のリポジトリ
> `Settings` > `Secrets`
- GitHub Actions で使用する非公開環境変数の設定画面になる
> `New repository secret`
- Name に `HEROKU_API_KEY`
Value に先程Herokuで取得したキーをセット
> `Add secret`
- 準備は整ったので、master/main ブランチにプッシュしてみる
- **そして順調にエラーになった**
- エラーになった画面
https://github.com/yoshikouki/sample-rails-with-gha/runs/1438564960?check_suite_focus=true
### デバッグ
- [actions/cache](https://github.com/actions/cache/blob/main/examples.md#ruby---bundler) のドキュメントは bundle がインストールしてある前提だったので `gem install bundler` を追加した
- Gemfile に記述されている ruby-version と GitHub Actions のコンテナ内のRubyを一致させること
```yaml
uses: actions/setup-ruby@v1
with:
ruby-version: 2.7
```
- GitHub Actions [setup-ruby](https://github.com/actions/setup-ruby#setup-ruby) ではマイナーバージョンまでしか指定できないみたいなので、手元Rubyのメンテナンス (ビルド) バージョンをあげることになるかもしれない
- マイナーバージョンが上がるわけではないので、素直に上げておけばいいと思う
### そして...

- 最後の push を済ませれば・・・

- 感極まる😂

# まとめ
以上で Rails アプリ (6系) で自動デプロイまでを最速で実装しました。
以後は、master (main) ブランチへ push するだけで heroku へ自動デプロイされます。これで2〜3コマンド分の実行の手間が省けたことになりますし、あなたはもう完全に CI/CD を習得したエンジニアです。
(実際のところ、今回はCI部分を何も扱っていませんし、デプロイに関してもデータベースを触っていないので中途半端な状態ではあるのですが、最低限は実装できたので良しとしましょう。してください。)
# 参考URL
https://github.co.jp/features/actions
https://docs.github.com/ja/free-pro-team@latest/actions