【CircleCI】AWSにデプロイしたRails6アプリをCircleCIで自動デプロイ

## 背景

Rails6でポートフォリオをアプリを作り、AWS EC2にデプロイしました。
EC2デプロイだと、`開発環境でgit push → EC2にログイン　→ git pull`で反映させなければなりません。
この手間を削減したいと思い、CircleCIでの自動デプロイを導入するに至りました。

### 学習コスト比較

自動デプロイ化するにあたり、CircleCIとCapistranoで検討しました。
比較するとこんな感じ。

||CircleCI|Capistrano|
|---|---|---|
|gem|不要|必要(5くらい)|
|参考記事|ほぼv2~2.1|v2とv3が混在|

gemを使うときは影響を考える必要がありますし、参考記事もバージョンを考慮すると混乱しがちです。
Capistranoと比較してCircleCIは学習コストが低いため導入を決めました。

## 使用技術/ツール

- Ruby 2.7.2
- Rails 6.1
- AWS (VPC/RDS/EC2/S3)
- CircleCI 2.1

## CircleCIとは？

[CircleCI Docks](https://circleci.com/docs/ja/2.0/about-circleci/)によると、CircleCI(サークルシーアイ)はテストやデプロイを自動化してくれるツールです。

>継続的インテグレーションの概要と、CircleCI を使用してエンジニアリング チームが自動化を行うしくみについて説明します。 CircleCI は、ソフトウェアのビルド、テスト、デプロイを自動化します。
引用: CircleCI Docks

これを導入することで開発者の手間、つまり企業にとっての<b>人件費の削減</b>に繋がるんですね〜。
現職が経理なのでわかるのですが、人件費は経営者にとっては結構シビアな問題です。
基本給は上げることは簡単ですが、下げるのは難しいですからね。(手当なんかはすぐ外せるので昇給と言って手当ばっかりつける企業というのはそういう事情があるかもですｗ全部がそうとは言いませんが。)ボーナスも基本給から計算されますし。

話が脱線しましたが、要するに<b>手間を削減できる</b>ので開発者にとっても経営者にとってもCircleCIは有用なツールということがわかりました。

### CI/CDとは

CircleCIのCIって何？と疑問に感じる人もいると思います。少なくとも私は感じました。

CIは、Continuous Integrationの略で<b>「継続的インテグレーション」</b>と言います。
よくセットで登場するのがCD(Continuous Delivery)、<b>「継続的デリバリー」</b>です。
インテグレーションは「統合」、デリバリーは「受け渡し」ですかね。

実はCI/CDは技術ではなく、手法です。
どんな手法かというと、本番環境にいつでもリリースできるように自動でテストとかをやっておく手法です。
そして、CI/CDツールはCircleCIだけではありません。オンプレミス型やクラウド型があり、Jenkins(オンプレミス)やTravisCI, CircleCI(クラウド)等たくさんあります。

CircleCIを選ぶ理由は、以下のとおりです。
- 比較的簡単なクラウド型
- 保守のしやすさ(sshコンテナでデバック)
- イニシャルコストが無料
- githubとの親和性


## 準備

それでは本題に入ります。
まずは[CircleCI](https://circleci.com/ja/)の登録をしましょう。
github(もしくはbitbucket)アカウントで登録します。

登録する際に、Githubのリポジトリの内、全てと連携するか、一部リポジトリのみ(publicのみだったと思います。)連携するかで聞かれますが、個人開発なので好みで良いと思います。CIを使いたいリポジトリが入る方を選びましょう。

## 前提

私はAWS EC2にデプロイしたあとにCircleCIを設定しました。
再現したい場合はまずはEC2にデプロイしてください。

## アプリ側の設定

アプリのルートディレクトリ直下に `.circleci/config.yml` を作成します。

```yaml:.circleci/config.yml
version: 2.1
jobs:
  deploy:
    machine:
      enabled: true
    steps:
      - add_ssh_keys:
          fingerprints:
            - "XXXXXXXXXXXXXXXXXXXX" # CircleCI上のssh keyにアプリの秘密鍵を登録した後に発行されるFingerPrintを記載
      - run: ssh -p $SSH_PORT $SSH_USER@$SSH_HOST "/var/www/アプリ名/deploy.sh"
 
workflows:
  version: 2
  deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only: master # もしくはmain
```

これはcircleciの設定ファイルです。
細かい内容は[公式ドキュメント](https://circleci.com/docs/ja/2.0/configuration-reference/)を御覧ください。
version: CircleCIのバージョン
jobs: 実行内容。その配下に任意の名前で実行内容を記述する。(今回のケースでいうとdeploy)
steps: 上から順番に実行されます。
workflows:stepsは実行命令の順序だったリスト。jobsで宣言した順に実行される。

今回はEC2インスタンスにログインするために
- stepsにadd_ssh_keysで秘密鍵としてfingerprintを持たせる
- run: 実行するものをこれで指定しました。CircleCI上の環境変数を展開してdeploy.shを実行するようにしています。
branchesには、デフォルトブランチのmaster(main)にしています。

次に、push後に自動でpullしてくれるようにルートディレクトリに`deploy.sh`というシェルスクリプトを作成します。

```sh:deploy.sh
cd /var/www/アプリ名/ && git pull && /bundleのpathがあるディレクトリ]/bundle install
RAILS_ENV=production /bundleのpathがあるディレクトリ]/bundle exec rake assets:precompile
sudo systemctl restart puma
```

一旦これをpushして、EC2でもpullしておきましょう。
ちなみにbundleはEC2インスタンスのパスを設定してください。

#### 注意１
git pullの前にアプリのディレクトリに移動するコマンドを実行させてください。
そうしないと以下のエラーが出ます。

```
fatal: not a git repository (or any of the parent directories): .git
```
なぜなら、自動デプロイがやっていることは単純でCircleCIに`deploy.sh`のシェルコマンドを実行させているだけだからです。
ローカル上で全然関係ないディレクトリでgitコマンドしても同じエラーが発生するはずです。


#### 注意2

bundle installについて。
パスが通ってない判定になってしまうので、絶対パスでbundleコマンドがある場所を指定しましょう。
コマンドがどこにあるか忘れちゃったよ。という方は以下を参考にして探しましょう。

[bundleコマンドにパスが通っていなかったときの対処法](https://qiita.com/hyshhryk/items/7e728ad57d963454b142)

## CircleCI側の設定

登録してCircleCIを開き、Projectsタブにすると自分のGithubリポジトリが表示されていると思います。
`Unfollow Project`は既に設定済みのリポジトリ、青い`Set Up Project`ボタンの場合は未設定のリポジトリです。
設定したいリポジトリをクリックしましょう。(青いボタン、リポジトリ名どちらでもOK)

未設定の場合、`Sample configs for sample`というモーダルウィンドウが出てくると思います。
これはどれでも良いと思うのですが、とりあえず連携したいリポジトリに使った言語を選びましょう。


![スクリーンショット 2021-08-05 9.21.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/09c3f91d-9f18-3d9e-7f86-b03756dec4df.png)


### 環境変数/SSH Keyの設定

CircleCIで対象リポジトリを開き、右上にある`「Project Settings」`を開きます。
![スクリーンショット 2021-08-05 11.33.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/1bab22e2-ac92-706d-8b7f-ffe6cadf1142.png)



Environment Variablesで環境変数を、
SSH KeysでEC2デプロイするときに使った秘密鍵
を登録してください。
![スクリーンショット 2021-08-05 11.38.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/e4517ec0-b82a-ddd7-2c6e-b1a3a43f8350.png)


秘密鍵を`Add SSH Key`で登録すると、Finferprintが生成されます。
これを`.circleci/config.yml`の`"XXXXXXXXXXXXXXXXXXXX"`部分にコピペしましょう。

![スクリーンショット 2021-08-05 11.44.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/b14aef23-9b1a-2a70-60bd-66a2fa559206.png)


環境変数はこんな感じです。
各情報は一般的には`~/.ssh/config`に情報が入っていると思います。(なかったら探してください。笑)
要はssh認証を登録した時の情報です。
アプリの秘密鍵は`.pem`ファイルの情報です。
![スクリーンショット 2021-08-05 11.51.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/4fdcd404-3ca8-c138-0366-fd980479116a.png)

##### 注意: 秘密鍵は以下の部分も含めてすべてコピペしましょう。

```
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
```



以上
これで開発環境からpushしたときに自動デプロイが走ります。
エラーが起きたときはCircleCIのエラーログで調べましょう。


## まとめ

- CI/CDは開発者の手間を省いてくれる有用なツールです。
- CircleCIはCI/CDツールの一つです。
- 実装自体は簡単だが、Linuxやssh等の基本知識が必要。

恥ずかしながら、CircleCI導入まではLinux, ssh, サーバー等の基本技術の理解が浅かったです
これを実装することでLinux, ssh等の知識をインプット/アウトプットすることができ良い機会となりました。

サーバーサイドを志望する上でサーバーやLinux関連は必須なのでこれからも学習を続けていきたいです。
次は自動テストにも挑戦したいです。

## 参考

- [CircleCI Docs](https://circleci.com/docs/ja/2.0/about-circleci/)
- [CodeZine (CI/CDのエキスパートが解説：CI/CDとは何か？ なぜ今、必要とされるのか？)](https://codezine.jp/article/detail/11083)
- [bundleコマンドにパスが通っていなかったときの対処法](https://qiita.com/hyshhryk/items/7e728ad57d963454b142)
- [ソフトウェアエンジニアの技術ブログ：Software engineer tech blog](http://hpscript.com/blog/circleci%E3%81%8B%E3%82%89ec2%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B%E9%9A%9B%E3%81%ABthe-authenticity-of-host-cant-be-established%E3%81%8C%E5%87%BA%E3%81%9F%E6%99%82/)
- [PATHを通すとは？ (Mac OS X)](https://qiita.com/soarflat/items/09be6ab9cd91d366bf71)
- [ADACHIN SERVER LABO](https://blog.adachin.me/archives/10997)