【Rails, AWS】CI/CDによる自動化とインフラ周りを解説【GitHub Actions, Capistrano】


# はじめに

RailsアプリケーションでCI/CDパイプラインを構築するために、Railsチュートリアル内でも推奨されている以下の教材で学びましたが、インフラ周りの登場人物が多く、それぞれの関連性と役割を整理したくて記事にしました。
セクションごとに変わるインフラ構成に応じて、登場する技術について解説していきます。
[Techpit - RailsアプリケーションをAWSに自動デプロイする方法を学ぼう！](https://www.techpit.jp/courses/200/)

## 対象読者

RailsチュートリアルなどでRailsを学んだ方で、以下の技術の概要を知りたい方。また、教材で取り組む成果物の全体像をもう少し深く理解したい方が対象です。長くなるので、右の目次から気になるページに飛んでもらうこともオススメです！

- Docker
- Nginx
- Puma
- AWS
- GitHub Actions
- Capistrano
    
![final.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/88a48bc1-3359-e74b-defa-82fad1f1f072.png)
    

# 0章-Herokuでデプロイ

教材では最初にHerokuにデプロイするところからスタートします。
Herokuは開発環境からpushするだけで簡単にアプリケーションをインターネットに公開することができます。
ここから周辺知識を学んでいきます。
※これまでHerokuの一部プランは無料で利用できておりましたが、22年10月から無料プランは無くなっておりますのでご注意ください。

![only_heroku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/d0daad58-132a-3992-7512-759465356c8d.png)


## Heroku構成の全体像について

開発者はDockerからHerokuにソースコードをpushすることで、簡単にアプリケーションを公開することができます。Heroku内ではPumaがWebサーバー兼アプリケーションサーバーとなり、クライアントのリクエストに応じてPostgreSQLやAmazon S3と通信を行いリクエストを返します。

### Docker

簡単にいうと、PC上に誰でも同じような開発環境を、作ったり壊したりすることができる仮想環境構築技術です。皆さんも環境構築で詰まる経験があると思いますが、複雑な環境構築手順がコード化された状態で配布できるため、瞬時に環境を構築し開発に専念することができます。

### Puma

Rails開発中に`Rails server`コマンドを入力することでログに出現していたPumaです。
上述したWebサーバー兼アプリケーションサーバーであり、Rackでもあります。
？？という感じだと思いますので、一度それぞれの役割を記します。

- Webサーバー
    
    クライアントから受け取ったリクエストに応じてHTMLを返したり、動的な処理をアプリケーションサーバーに依頼する役割をもつサーバーです
    
- アプリケーションサーバー
    
    データベースとやり取りしながら、動的にHTMLを生成し、Webサーバーに返す役割をもつサーバーです
    
- Rack
    
    Railsなどのフレームワークとアプリケーションサーバーが通信することができるインターフェースです。従来はサーバーとフレームの組み合わせに制限がありましたが、Rackが緩衝材となることで様々な組み合わせができるようになります。
    

まとめると、PumaによってRailsアプリケーションを動かすことができ、クライアントとやり取りすることができます。１つで３役も担っていてすごいPumaですが、Webサーバー機能に関しては貧弱なため、本格的なサービスで運用し、複数のアクセスがあるとすぐにダウンしてしまいます。以降のセクションで、どんどん実用的なインフラ構成に進化していきます。

# 1章 - GitHub Actionsでテストを自動化

GitHubはソースコードを保管するだけでなく、レポジトリと連携した様々なサービスがあります。GitHub Actionsもその１つでCI/CDツールで、Continuous Integration（継続的インテグレーション）/ Continuous Delivery（継続的デリバリー）を実現します。それぞれの概要は以下の通りで、本セクションではCIに特化してテストを自動化します。

### 継続的インテグレーション

継続的インテグレーションは、開発者がコードを変更したら、その変更を自動的にビルド、テストするプロセスで次のメリットがあります。

- コードの品質を維持し、開発者がエラーを修正するのに必要な時間を短縮できる
- バグの発見や修正が早くなるため、品質を向上させることができる
- 複数の開発者が同時に作業する場合に競合や問題が発生することを防止できる

### 継続的デリバリー

継続的デリバリーとは、開発者がコードを変更すると自動で本番環境にデプロイするプロセスで、教材では4章で取り入れます。主に次のメリットがあります。

- リリースの手動作業を短縮でき、リリースに費やす時間を減らし開発に専念することができる
- テストやデプロイの手順を自動化することで、ヒューマンエラーを減らし、アプリケーションの品質を向上させることができる

### テストの自動化

GitHub Actionsでは自動化したい大項目に対しジョブという単位を使用します。今回のテスト自動化で言えばテストジョブです。テストジョブとしてYAMLファイルに定義したワークフローを、指定したイベントに反応してGitHub Actions上の使い捨てである仮想環境（ランナー）で実行します。

![https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/e3fe6e05-99f1-c76f-09e0-099e5c5e4c1e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/e3fe6e05-99f1-c76f-09e0-099e5c5e4c1e.png)

今回はプルリクエストに反応し、ビルドとテストを実行するジョブになります。前述した通り使い捨ての環境なので、実行するたびにRubyや必要なgemのインストールも一緒に実行します。

# 2, 3章 - AWS EC2へデプロイ

いよいよAWSサービスを使用してのデプロイになります。教材ではAWSの各サービスは大半がCloudFormationという自動作成サービスで作られるので詳しく解説されません。そこで各サービスにも軽く触れながら周辺知識を解説していきます。

![only_ec2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/ba736bdd-19ee-36cb-b20b-a1e8fab5cd6c.png)



## EC2構成の全体像について

開発者はEC2にSSH接続し、必要なソフトウェアやライブラリをインストールします。またEC2内からGitHubにSSH接続しソースコードをEC2内に反映してデプロイします。クライアントからのリクエストに対しては、今度はNginxがWebサーバーとして一旦受けた後、ソケット通信によりPumaに処理を渡してレスポンスを返します。

### VPC

Virtual Private Cloudの略で、広大なAWSの領域の中に各サービスを配置していく自分専用の領域のことです。CIDRという記法を用いてIPアドレスの範囲（自分専用の領域）を設定します。

### サブネット

VPCで設定した領域を用途に応じて分割した細かな領域のことです。ルーティングにより通信できる相手を設定することができ、外部と通信できる領域をパブリックサブネット、内部でのみ通信可能な領域はプライベートサブネットと呼ばれます。

### EC2

Elastic Compute Cloudの略で、簡単に言えば仮想サーバーでありパソコンです。前述のVPCとサブネットが確保されてはじめて、その領域内に設置することができます。Railsを動かすために、このサーバーのインスタンスを作成し、その中にRubyやgemなどの必要なソフトウェアを組み込みます。

### インターネットゲートウェイ

VPCと外のインターネットとを接続するための門です。この門を通ることでEC2などのサービスに接続することができるようになります。

### Elastic IP

EC2はその他と通信するためにパブリックIPアドレスを持ちますが、デフォルトだと停止・起動させるたびにパブリックIPアドレスが変化してしまいます。確保したElastic IPを紐づけることでパブリックIPを固定させることができます。

### CloudFormation

上述したAWSサービスは基本はAWSウェブサイトのコンソール画面をポチポチして作成することになります。CloudFormationはJSONまたはYAMLで記述されたテンプレートをもとに複数のAWSリソースを自動で作成することができます。

### Nginx

Webサーバーソフトウェアでクライアントからの要求を受けレスポンスを返す役割をもちます。静的なファイルであるHTMLやCSS, JavaScriptを返す他、アプリケーションサーバーに処理を分散させる役割も持ち、PumaとNginxはソケット通信を用いて通信して処理を実行します。同じソフトウェアのApacheと比べるとメモリ使用量が少なく高パフォーマンスと言われています。

# 4章 - GitHub Actionsでデプロイ

本セクションでははGitHub Actionsを用いてEC2への自動デプロイに取り組みます。EC2へのデプロイ作業はSSH接続後にビルドを行ったりPumaを再起動したりと、ソースコードを更新するたびに複雑な操作を何度も実施することになります。GitHub Actionsのワークフローにデプロイジョブを追加することで、複雑な作業を自動化し開発に専念できるため、一日に何度も本番環境にアプリケーションをデプロイすることができるようになります。

![deploy_action copy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/10048819-13e4-0a04-c6c2-84880c23a6e9.png)


今回はテストジョブに加えデプロイジョブを追加しています。プルリクエストが出されてからデプロイされるまでの流れを簡単に解説します。

1. リポジトリのmasterに対してプルリクエストが出される
2. プルリクエストに反応してテストジョブが実行され、問題なくパスした場合masterへマージ可能となる
3. masterへマージする
4. マージに反応しテストジョブが実行され、問題なくパスすれば続けてデプロイジョブが実行される
5. デプロイジョブでは暗号鍵を生成しEC2へSSH接続するところからスタート
6. EC2接続後はソースコードを反映、ビルド後、Pumaを再起動してデプロイ完了

# 5章 - Capistranoでデプロイ

4章ではワークフロー内にデプロイするためのLinuxコマンドを記載していましたが、Capistranoを組み合わせることで設定内容にしたがって１コマンドでデプロイ作業を実行することができ、ワークフローの見通しがよくなります。

![capistrano_with_actions copy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/0ebded7c-2ac2-2da7-050a-7b15ee4fbf04.png)


### Capistrano

CapistranoはRubyのデプロイツールです。設定後はHerokuにデプロイするような感覚でCapistranoはEC2へのデプロイを自動的にやってくれます。また、開発環境でのデプロイテストも実行可能です。その他の特徴としてデプロイ先に複数世代のソースコードを保持することができるため、デプロイ後にトラブルがあった場合はすぐに前の世代に戻すといったことが可能になります。

### GitHub Actionsとの組み合わせ

GitHub Actionsと組み合わせることで以下のようにデプロイを実行します。

1. 前章と同様にmargeに反応してテストジョブが実行され、問題なければデプロイジョブが実行される
2. Capistranoを実行するためにRuby環境をビルドし、暗号鍵を生成する
3. CapistranoがEC2にSSH接続を行い、最新のソースコードをpullする
4. 本番環境に最新のソースコードを反映するために、該当するコードにリンクを付与する
5. リンクされたコードのビルドを行い、Pumaを起動する

このように、GitHubでのイベントをトリガーにして、テストが通ったらCapistranoでデプロイするといったCI/CD処理を実行することで品質の高いコードを本番環境にデプロイすることが可能になります。また、GitHub Actionsのロールバック処理を利用して、デプロイに失敗した場合に前の世代に戻すこともできるようになります。

# 6, 7章 - 踏み台サーバ経由でデプロイ

これまではアプリケーションサーバー(webapp)をパブリックサブネットに配置していましたが、誰でもアクセスできるエリアに配置しておくのはセキュリティ上よろしくありません。そこでwebappはプライベートサブネットに移管し、パブリックサブネットに踏み台サーバー(bastion)を設置して、bastionを経由してwebappにアクセスする構成を取ります。その構成を取るために登場人物が更に増えることになりますが、７章のAppdendixを織り交ぜて全部解説します。

![final.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754248/2335bd2a-2d4a-cf67-f592-b83a68c60eb9.png)

## デプロイとサービス利用の流れ

これまでの解説を元に、最終的な構成ではどのようにデプロイが行われ、ユーザーは公開されたサービスをどのように利用できるのかを整理したいと思います。

### デプロイ（開発者）

1. GitHubのmasterリポジトリにプルリクエストを出すとGitHub Actionsのテストジョブが実行され、問題なくパスするとマージ可能になる
2. masterにマージするとデプロイジョブが実行され、ランナー上でCapistranoが実行される
3. Capistranoは踏み台サーバー経由でwebappインスタンスにSSH接続し、NAT gateway経由でソースコードを反映する
4. 最新のソースコードに対してビルドを行いPumaを起動してデプロイ完了となる。

### サービス利用（ユーザー）

1. ブラウザからURLを叩いてリクエストを出す
2. URLの名前解決のためRote 53に問い合わせ、ALBのIPアドレスを入手する
3. IPアドレスを元にALBにアクセスし、ALBは踏み台サーバーを経由してリクエストをwebappサーバーに渡す
4. webappサーバー内ではまず最初にNginxがリクエストを受け取り、静的ファイルをレスポンスとして返し、それ以外のリクエストをPumaに渡してRailsが実行される
5. Railsは必要に応じてRDSとの接続やNAT gateway経由でS3や他サービスと連携して動的にHTMLを組み立てる
6. 組み上がったHTMLは元にきた経路でレスポンスとして返され、ブラウザに画面が表示される

## 登場する技術の概要について

新たに登場するのは全てAWSのサービスになります。

## ALB

Application Load Balancerの略で、負荷分散を実現するロードバランサーです。EC2などに負荷分散することで、たくさんの人がアクセスしてもサービスが止まらず、スムーズに動くようになります。具体的には、複数のEC2インスタンスを用意して、それぞれに同じアプリケーションを動かします。そして、リクエストが来るたびに、アプリケーションロードバランサーというものが、どのEC2にリクエストを送るか決めます。これによって、一つのEC2に負荷がかかりすぎることがなく、快適にサービスを利用できるようになります。

## NAT Gateway

NAT GatewayはプライベートサブネットにあるEC2インスタンスが外部と通信するための門です。プライベートサブネットにあるEC2はインターネットに直接接続されていないため、NAT Gatewayを経由して外部と通信します。NAT Gatewayは、EC2インスタンスが外部と通信するためのプライベートIPアドレスを保持しており、外部との通信が安全に行われます。

## Route 53

Amazon Route 53は、AWSのDNSウェブサービスで、ウェブサイトやアプリケーションのURLをIPアドレスに変換するサービスです。例えば、「[www.example.com](http://www.example.com/)」のようなウェブサイトのURLは、IPアドレスに変換されることで、実際にウェブサイトにアクセスすることができます。Route 53を使うことで、自分のドメイン名を使って、ウェブサイトやアプリケーションにアクセスすることができます。

## Certificate Manager

AWS Certificate Manager（ACM）は、ウェブサイトやアプリケーションの通信を守るための証明書を簡単に管理できるサービスです。証明書を使うことで、通信内容が盗聴されたり、改ざんされたりすることを防ぐことができます。ACMを使うと、安全に通信することができます。

## RDS

Relational Database Serviceの略で、AWSが提供するマネージドデータベースサービスです。MySQL、PostgreSQLなど、さまざまなデータベースが元となり、データベースのセットアップ、バックアップ、スケーリングなどの業務をAWSが自動的に行ってくれるため、データベース管理にかかる手間や管理コストを削減することができます。

## その他

- Publicサブネットが２つある理由は、ALBを設置する際に少なくとも２つ以上のAZが異なるPublicサブネットを指定する必要があるためです。一般的なWebサービスではもう１つ同じようなサーバーを設置して冗長構成にさせます。
- Privateサブネットが２つある理由は、RDSを設置する際に２つ以上のAZが異なるPrivateサブネットをグループにしたサブネットグループを作成する必要があるためです。こちらももう片方のプライベートサブネットに同様のRDSを設置しておき障害に備えるのが一般的です。

# さいごに

いかがでしたでしょうか。何気なく使っているサービスには複数の技術が使われており、それぞれが連携することで快適に利用できていたのですね。

また、品質が確保されたソースコードを自動で本番環境にアップロードするCI/CD環境を整備することで、開発サイクルを高速化することができることを学びました。

これらの知識は、個人開発でも取り入れることができるので、今後の開発に役立てたいと思います。

## 参考記事
[基本的なシステム構成図を理解するためのAWS基礎をまとめてみた - Qiita](https://qiita.com/g_ayushi/items/0e0f34d19813b8fdc2b8)
[AWS　(デプロイを自動化) - yuuki blog](https://yuukii5.hatenablog.com/entry/2020/11/16/214808)
[【Puma】Rails 5.2 + Puma + Nginx のデプロイ設定 - 7839](https://serip39.hatenablog.com/entry/2020/12/23/235700)