[Rails]AWS EC2へデプロイする
はじめに
おおまかな手順になりますか、AWS EC2上にRailsのCRUDアプリをデプロイしてみました。
Amazon EC2 (Elastic Compute Cloud) は、Amazon Web Services (AWS) のクラウドコンピューティングサービスの1つです。EC2は、仮想サーバー(インスタンス)を提供し、必要に応じてwebサービスのスケーリングや管理を行うことができます。
流れ
- AWSアカウントを作成する
- IAMユーザー&IAMユーザーグループを作成する
- AWS Budgetsを設定する
- インフラ構築
- EC2インスタンスを作成する
- Elastic IPアドレスを割り当てる
- DBインスタンスを作成する
- デプロイ
AWSアカウント作成
公式にある手順に従ってアカウントを作成します。
AWSマネジメントコンソール画面を開き、ルートアカウントの作成に進みます。
ルートアカウント作成
ルートアカウントは、Amazon Web Services (AWS) のアカウント作成時に自動的に生成される特別なアカウントです。ルートアカウントは、AWSアカウントにおける最上位の権限を持ち、すべてのAWSリソースとサービスに対して完全なアクセス権を有しています。
ルートアカウントは、AWSアカウントの初期設定やグローバルな設定の管理など、重要なタスクを実行するために使用されます。ただし、セキュリティの観点から、ルートアカウントを日常的な操作に使用することはおすすめされません。
ルートアカウントは、AWSアカウント全体のリソースと設定を管理できるため、権限の誤使用や不正アクセスによるリソースの削除や変更が発生する可能性があります。そのため、セキュリティを向上させるためには、ルートアカウントの使用を最小限に制限し、代わりにIAM(Identity and Access Management)ユーザーを作成して適切な権限を与えることが推奨されます。
IAMユーザーは、特定のアクセス権限を持った個別のユーザーアカウントとして作成され、必要な操作を実行するために使用されます。IAMユーザーは、適切なセキュリティポリシーやロールを割り当てることで、細かいアクセス制御を実現できます。これにより、セキュリティを強化し、不要な権限のミスや悪意のある行動を防止することができます。
IAMグループ&IAMユーザー作成
IAMでユーザー作成するために、所属するグループの定義から始めます。
IAMグループ作成
コンソール画面の上部にある検索フォームからIAMを検索して、IAMの画面へ移動します。
左メニュー内の「個のユーザーグループ」から「グループ作成」に進みます。
- グループ名:任意
- アクセス許可ポリシーをアタッチ:検索窓で「AdministratorAccess」を検索しチェック
IAMユーザー作成
IAMのコンソール画面の左メニュー内の「ユーザー」から「ユーザーを追加」に進みます。
- ユーザ名:任意
- AWS認証情報タイプを選択:パスワード - AWSマネジメントコンソールへのアクセス
- コンソールのパスワード:カスタムパスワード
- パスワードリセットが必要:チェックなし
- アクセス権限:先ほど作成したIAMグループにチェック
- タグの追加:なし
AWS Budgets設定
コンソール画面の上部にある検索フォームからAWS Budgetsを検索して、AWS Budgetsの画面へ移動します。
AWS Budgetsは、AWSの利用料金やリソース使用状況に関する監視と制御を行うためのサービスです。AWS Budgetsを使用すると、特定の利用料金やリソース使用量が設定したしきい値を超えた場合に通知を受け取ることができます。これにより、予算を管理し、予算を超過することや費用の予測を行うことができます。また、AWS Budgetsは複数の予算を作成し、異なるリソースやアカウントに対して異なる予算を設定することも可能です。
予算の名前、種類、設定、アラート設定などを必要な設定を行ったら、作成をクリックして予算を作成します。
インフラ構築
これからの作業は先程作成したIAMユーザアカウントで進みます。
まずは地域(リージョン)を指定します、画面右上のアカウント名の隣のプルダウンから「東京」を選んでください。
VPC
コンソール画面の上部にある検索フォームからVPCを検索して、VPCの画面へ移動します。
VPC(Virtual Private Cloud)は、Amazon Web Services(AWS)が提供する仮想的なネットワーキング環境です。VPCは、AWSクラウド内で仮想的なプライベートネットワークを構築するために使用されます。VPCはユーザー専用の仮想ネットワークであり、他のAWSユーザーやインターネットから隔離されています。
VPCを使用すると、ユーザーはAWSリソース(EC2インスタンス、RDSデータベース、Elastic Load Balancerなど)を自身が定義した仮想ネットワーク内に配置することができます。これにより、AWS上のリソースが外部からのアクセスに制限され、セキュリティが向上します。
サブネット
左のメニューパネルから「サブネット」をクリックします。
サブネット:VPC内には複数のサブネットを作成することができます。サブネットは、VPC内のセグメント化されたネットワーク領域であり、異なるアベイラビリティーゾーンに分散して配置することができます。
これらの項目に適切な名前をつけていきます。
- VPC
- サブネット
※アベイラビリティゾーンごとで命名します - インターネットゲートウェイ
- ルートテーブル
ルートテーブルをサブネットに紐付け
左のメニューパネルから「ルートテーブル」をクリックします。
ルートテーブルには、トラフィックの宛先ごとに設定されたルーティングルールが含まれており、サブネット内のインスタンスやリソースのトラフィックがこれらのルールに従ってルーティングされます。たとえば、インターネットゲートウェイへのルートを追加することで、サブネット内のインスタンスがインターネットと通信できるようになります。また、仮想プライベートゲートウェイへのルートを追加することで、他のVPCやオンプレミスネットワークとのプライベートな接続を確立できます。
「サブネットの関連付けを編集」から、デフォルトで設定されているルートテーブルを作成したプライベートサブネットと紐づけます。
セキュリティグループの作成
セキュリティグループは、AWSの仮想ネットワーキング環境で使用されるファイアウォールのような機能を持つ、仮想ファイアウォールルールセットです。セキュリティグループは、EC2インスタンスやRDSデータベースなどのAWSリソースに対してトラフィックの制御を行う役割を果たします。
APサーバー接続用とデータベース接続用の二つを作成します
インバウンドルールは、リソースに対する外部からのトラフィック(例: HTTP、SSH、RDP)を許可または拒否するために使用されます。アウトバウンドルールは、リソースからのトラフィックが外部に送信される際の制御に使用されます。
アプリサーバー接続用
- 基本的な詳細
◦ セキュリティグループ名
◦ 説明
◦ VPC - インバウンドルール(2つ)
- 1つ目
◦ タイプ:HTTP
◦ リソースタイプ:Anywhere-IPv4 - 2つ目
◦ タイプ:ssh
◦ リソースタイプ:Anywhere-IPv4
◦ アウトバウンドルール:そのまま
◦ タグ:なし
- 1つ目
※今回はクラウドサービスでアプリを動かすこと目的なので、HTTPのみの設定になっております。
データベース接続用
- 基本的な詳細
◦ セキュリティグループ名
◦ 説明
◦ VPC - インバウンドルール
◦ タイプ:MYSQL/Aurora
◦ ソース:先ほど作成したセキュリティグループ
◦ アウトバウンドルール:そのまま
◦ タグ:なしでok
データベースはアプリサーバーからのアクセスのみ許可したいので、インバウンドルールはセキュリティグループのグループIDを指定します。
AWSにおけるネットワーク構築については以上となります。
次はRDSとEC2インスタンスの作成に移ります。
EC2(Elastic Compute Cloud)
コンソール画面の検索から「EC2」を検索してEC2の画面へ進み、左メニュー内の「インスタンス」を選択します。
右上の「インスタンスを起動」をクリックし、下記に従い設定していきます!
- Amazonマシンイメージ(AMI)
◦ Amazon Linux 2 AMI (HVM) - Kernel 5.10, SSD Volume Type(64ビット x86) - インスタンスタイプの選択
◦ t2.micro - インスタンスの詳細の設定
◦ インスタンス数:1
◦ ネットワーク
◦ サブネット - ストレージの追加:デフォルト
- タグの追加
◦ キー:Name
◦ 値:適切な名前をつける - セキュリティグループの設定
◦ セキュリティグループの割り当て:既存
◦ セキュリティグループID:作成したアプリサーバー
新しいキーペアの作成
キーペアのタイプ:RSA
キーペア名:適切な名前をつける
キーペアを作成してダウンロードをします。
ElasticIP紐づける
先ほど作成したEC2は停止して再度起動すると、パブリックIPが変わり、毎回sshログイン時は不便になるので、固定のIPを紐付けます。
EC2のダッシュボードの左メニューから「Elastic IP」に進み、右上の「Elastic IPアドレスの割り当て」から下記の設定で作成していきます。
ネットワークボーダーグループ
パブリックIPのアドレスプール:AmazonのIPv4アドレスプール
作成後、今のElasticIPをEC2インスタンスに割り当てます。
右上の「アクション」から「ElasticIPアドレスの関連付け」に進み、先ほど作成したEC2インスタンスに紐付け設定をします。
作成したEC2の概要内にElasticIPアドレスが表示されていればokです!
EC2にログイン
ダウンロードした***.pem
を~/.sshに移動させます。
mv ***.pem ~/.ssh
cd ~/.ssh
chmod 400 ***.pem
# ログインコマンドをコピーして打ちます
ssh -i "***.pem" *****.compute.amazonaws.com
, #_
~\_ ####_ Amazon Linux 2023
~~ \_#####\
~~ \###|
~~ \#/ ___ https://aws.amazon.com/linux/amazon-linux-2023
~~ V~' '->
~~~ /
~~._. _/
_/ _/
_/m/'
[ec2-user@... ~]$
RDS(Relational Database Service)
Amazon RDS (Relational Database Service) は、Amazon Web Services (AWS) のマネージドリレーショナルデータベースサービスです。RDSは、一般的なリレーショナルデータベースエンジン(MySQL、PostgreSQL、Oracle Database、Microsoft SQL Server、Amazon Auroraなど)をサポートしています。RDSはこれらのエンジンの管理とメンテナンスを担当し、データベースのバックアップ、パッチ適用、自動スケーリング、ハイアベイラビリティなどのタスクを簡素化します。
DBサブネットグループの作成
DBサブネットグループ(DB Subnet Group)はAmazon RDSで使用される機能です。DBサブネットグループは、RDSインスタンスを配置するためのサブネットのグループを定義します。AWSのVPC(Virtual Private Cloud)内に複数のサブネットを作成することができます。DBサブネットグループは、これらのサブネットを論理的にグループ化し、RDSインスタンスを配置するために使用します。このグループ化により、RDSインスタンスがアベイラビリティゾーン(AZ)にまたがって配置されることができ、高可用性と耐障害性を提供します。
DBサブネットグループは、セキュリティグループやネットワークACL(Access Control List)と組み合わせて使用され、RDSインスタンスへのネットワークアクセスを制御するためのセキュリティレイヤーを提供します。また、DBサブネットグループは、RDSインスタンスのサブネット配置を簡単に変更する際にも使用されます。
コンソール画面の検索窓から「RDS」を検索し進みます。
まずはDB用のサブネットグループを作成します。
左メニューの「サブネットグループ」を選択し、右上「DBサブネットグループを作成」をクリックします。
下記の設定で作成します。
- サブネットグループの詳細
◦ 名前
◦ 説明
◦ VPC - サブネットの追加
◦ アベイラビリティゾーン
◦ サブネット
RDSインスタンスの作成
RDSインスタンスは、Amazon RDS (Relational Database Service) 上で実行されるリレーショナルデータベースのインスタンスです。RDSインスタンスを作成することで、データベースの作成、設定、管理を簡単に行うことができます。
左メニューの「データベース」を選択し、右上「データベースを作成」をクリックします。
下記の設定で作成します。
- データベースの作成方法:標準作成
- エンジンのオプション
◦ エンジンのタイプ:MYSQL
◦ エディション:MySQL Community
◦ バージョン:最新版
◦ テンプレート:無料利用枠 - 設定
◦ DBインスタンス識別子:デフォルト(database-1)
◦ マスターユーザー名:デフォルト(admin)
◦ マスターパスワード:任意のもの
※ここでのユーザ名、パスワードは後ほどアプリ側に設定するので、控えておいてください。 - DBインスタンスクラス:デフォルト
- ストレージ:すべてデフォルト
- 可用性と耐久性:デフォルト
- 接続
◦ VPC
◦ サブネットグループ
◦ パブリックアクセス:なし
◦ VPCセキュリティグループ:既存の選択 先作成したDBセキュリティグループ
◦ アベイラビリティゾーン - データベースポート:3306
- データベース認証:パスワード認証
- 追加設定
- 最初のデータベース名:適切な名前をつける
その他はそのままでok
デプロイ
インスタンス内も同じくRailsが動くように環境構築をしていきます。
EC2にログインして、その中のターミナルでコマンドを実行していきます。
EC2内の環境構築
まずはパッケージをアップデートします。
必要なパッケージをインストールします。
[ec2-user@ip... ~]$ sudo yum -y update
[ec2-user@ip... ~]$ sudo yum -y install \
git make gcc-c++ patch curl \
openssl-devel \
libcurl-devel libyaml-devel libffi-devel libicu-devel \
libxml2 libxslt libxml2-devel libxslt-devel \
zlib-devel readline-devel \
mysql mysql-server mysql-devel \
ImageMagick ImageMagick-devel \
epel-release
nodejsインストール
次にnode.jsのインストールをします。
まずはnode.jsのパッケージをダウンロードします。
[ec2-user@ip... ~]$ curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash -
次にnode.jsをインストールします。
[ec2-user@ip... ~]$ sudo yum install -y nodejs
# 下記コマンドでインストールされたか確認します
[ec2-user@ip... ~]$ which node
/usr/bin/node
yarnインストール
yarnも利用するのでインストールします。
nodejsと同じようにyarnのパッケージをダウンロードします。
[ec2-user@ip... ~]$ curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
yarnをインストールします。
[ec2-user@ip... ~]$ sudo yum -y install yarn
rubyインストール
rubyを利用できるように、rbenvとruby-buildをインストールしていきます。
# rbenvインストール
[ec2-user@ip... ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
[ec2-user@ip... ~]$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
[ec2-user@ip... ~]$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
[ec2-user@ip... ~]$ source ~/.bash_profile
# ruby-buildインストールしてrubyをインストール
[ec2-user@ip... ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
[ec2-user@ip... ~]$ rbenv install -v 3.0.0
[ec2-user@ip... ~]$ rbenv global 3.0.0
[ec2-user@ip... ~]$ rbenv rehash
[ec2-user@ip... ~]$ ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]
公開鍵と秘密鍵の作成
GitHub利用をしてコードをクローンできるようにするため、EC2上で公開鍵と秘密鍵のペアを作成します。
[ec2-user@ip... ~]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ec2-user/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
# 何も入力せずそのままエンター
Enter same passphrase again:
# 何も入力せずそのままエンター
Your identification has been saved in /home/ec2-user/.ssh/id_rsa.
Your public key has been saved in /home/ec2-user/.ssh/id_rsa.pub.
The key fingerprint is:
~~省略~~
GitHubに公開鍵を登録
GitHubと接続できるように作成した公開鍵を登録します。
[ec2-user@ip... ~.]$ cd .ssh
[ec2-user@ip... .ssh]$ cat id_rsa.pub
# ここに表示された文字列を全てコピーする(ssh-rsaから)
自分のGitHubにログインしSettings->SSH and GPG keys->New SSH keyに進んで登録します。
Title:わかりやすい名前
Key:先ほどコピーした公開鍵
これでEC2とGitHubでSSH通信ができるようになりました。
確認してみましょう!
[ec2-user@ip... ~]$ ssh -T git@github.com
# 下記メッセージが出たらyesを入力する
Are you sure you want to continue connecting (yes/no)?
「Hi GitHubアカウント名....」が出れば接続完了です。
production環境の設定
~略~
# SQL用のgem
group :production do
gem 'mysql2'
end
bundle install
これでGemfileとGemfile.lockが変更されたはずです。
変更が確認できたらcommitとGitHubへのpushをしてください。
GitHubからクローン
[ec2-user@ip... ~]$ cd /
[ec2-user@ip... /]$ sudo mkdir /var/www/
[ec2-user@ip... /]$ sudo chown ec2-user /var/www/
[ec2-user@ip... /]$ cd /var/www
[ec2-user@ip... www]$ git clone git@github.com:アカウント名/アプリ名.git
master.key設定
master.keyはgitignoreされているため、自分でサーバーに用意する必要があります。ローカルからファイルをコピーします。
下記のコマンドを参考にvimでconfig/master.keyというファイルを作り、ローカルのmaster.keyの中身をコピーすれば問題ないです。
cd /アプリ名
vim config/master.key
database.ymlの設定
RDS作成した際のdatabase接続設定をします。
下記コマンドでdatabase.ymlを編集します!
vim config/database.yml
default: &default
adapter: mysql2
encoding: utf8mb4
charset: utf8mb4
collation: utf8mb4_general_ci
username: 設定したユーザ名
password: 設定したパスワード
host: RDSのエンドポイント
pool: 5
timeout: 5000
production:
<<: *default
database: # なんでもOK
gemのインストール
gem list bundler
と打つとbundlerのバージョンが確認できるので打ち込んでみます。
[ec2-user@ip... `アプリ名`]$ gem list bundler
***LOCAL GEMS***
bundler (default: 2.2.3)
[ec2-user@ip... `アプリ名`]$ gem install bundler `ローカルと同じバージョン`
準備ができたのでgemをインストールします。
[ec2-user@ip... `アプリ名`]$ bundle install
DB作成
# DBを作成
[ec2-user@ip... `アプリ名`]$ rails db:create RAILS_ENV=production
# テーブル作成
[ec2-user@ip... `アプリ名`]$ rails db:migrate RAILS_ENV=production
Nginxのインストール
webサーバーのnginxを利用します。
まずはインストールします。
[ec2-user@ip... ~]$ sudo amazon-linux-extras install nginx1
Nginxの起動
下記コマンドでnginxを起動して、ブラウザでnginxが起動しているか確認します。
[ec2-user@ip... ~]$ sudo systemctl start nginx
EC2に設定したElasticIPをブラウザのアドレスバーに打ってnginxの初期画面が表示がされたらokです。
Nginxの設定ファイルを作成
今はNginxデフォルトのページに飛ばされているのでこれをRailsアプリに飛ぶように設定をします。
Nginxの設定ファイルはインストールした時に生成されています。
/etc/nginx/nginx.confがデフォルトのNginxの設定ファイルです。
新しく設定ファイルを作成して/etc/nginx/nginx.confに読み込ませて設定します。
[ec2-user@ip... ~]$ cd /etc/nginx/conf.d
[ec2-user@ip... conf.d]$ sudo vi ***.conf
error_log /var/www/アプリ名/log/nginx.error.log;
access_log /var/www/アプリ名/log/nginx.access.log;
client_max_body_size 2G;
upstream web-app {
server unix:///var/www/アプリ名/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
server_name xxx.xxx.xxx.xxx; # 作成したEC2の ElasticIPアドレス。
keepalive_timeout 5;
root /var/www/アプリ名/public;
try_files $uri/index.html $uri.html $uri @app;
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://web-app;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/www/アプリ名/public;
}
}
client_max_body_sizeディレクティブは、アップロードされるリクエストボディの最大サイズを設定しています。ここでは2Gに設定されています。
upstreamディレクティブは、バックエンドのアプリケーションサーバーを指定しています。ここではPumaサーバーがunix:///var/www/アプリ名/tmp/sockets/puma.sockでリスンしています。
serverディレクティブは、Nginxがリクエストを受け付けるサーバーブロックを定義しています。ここではポート80でリクエストを受け付け、server_nameで指定されたElasticIPアドレスに対応するサーバーブロックです。
rootディレクティブは、ドキュメントルートを指定しています。ここでは/var/www/アプリ名/publicがドキュメントルートとなります。
try_filesディレクティブは、ファイルが存在しない場合に代替のファイルを探す方法を指定しています。ここではuri.html、$uri、@appの順でファイルを探します。 uri/index.html、
location @appディレクティブは、アプリケーションへのリバースプロキシの設定です。リクエストヘッダーにX-Forwarded-ForとHostを設定し、プロキシパスはhttp://web-appです。
error_pageディレクティブは、特定のエラーステータスコードに対してカスタムのエラーページを提供する設定です。ここでは500、502、503、504のエラーステータスコードに対して/500.htmlが表示されます。
Pumaを起動する
pumaの本番設定ファイルを確認します。
environment "production"
tmp_path = "#{File.expand_path("../../..", __FILE__)}/tmp"
bind "unix://#{tmp_path}/sockets/puma.sock"
threads 3, 3
workers 2
preload_app!
pidfile "#{tmp_path}/pids/puma.pid"
# stdout_redirect "#{tmp_path}/logs/puma.stdout.log", "#{tmp_path}/logs/puma.stderr.log", true
plugin :tmp_restart
[ec2-user@ip... ~]$ cd /var/www/アプリ名/
[ec2-user@ip... ~]$ RAILS_ENV=production bin/rails assets:precompile
[ec2-user@ip... ~]$ RAILS_ENV=production bin/webpack
Hash: 804a8059a304061a3e74
Version: webpack 4.46.0
Time: 2191ms
Built at: 06/12/2023 6:10:53 AM
Asset Size Chunks Chunk Names
js/application-271cf8931bb708d2d8bb.js 121 KiB application [emitted] [immutable] application
js/application-271cf8931bb708d2d8bb.js.map 133 KiB application [emitted] [dev] application
manifest.json 364 bytes [emitted]
Entrypoint application = js/application-271cf8931bb708d2d8bb.js js/application-271cf8931bb708d2d8bb.js.map
[./app/javascript/channels sync recursive _channel\.js$] ./app/javascript/channels sync _channel\.js$ 160 bytes {application} [built]
[./app/javascript/channels/index.js] 211 bytes {application} [built]
[./app/javascript/packs/application.js] 493 bytes {application} [built]
+ 3 hidden modules
[ec2-user@ip... アプリ名]$ bundle exec puma -C config/puma/production.rb -e production
[16420] Puma starting in cluster mode...
[16420] * Puma version: 5.6.5 (ruby 3.0.0-p0) ("Birdie's Version")
[16420] * Min threads: 3
[16420] * Max threads: 3
[16420] * Environment: production
[16420] * Master PID: 16420
[16420] * Workers: 2
[16420] * Restarts: (✔) hot (✖) phased
[16420] * Preloading application
[16420] * Listening on unix:///var/www/magia_deploy_test/tmp/sockets/puma.sock
[16420] Use Ctrl-C to stop
[16420] - Worker 1 (PID: 16439) booted in 0.0s, phase: 0
[16420] - Worker 0 (PID: 16438) booted in 0.0s, phase: 0
Nginxを再起動する
Pumaがうまく起動できたらNginxを再起動する。
[ec2-user@ip... アプリ名]$ sudo nginx -s stop
[ec2-user@ip... アプリ名]$ sudo systemctl start nginx
エラーログを確認する
[ec2-user@ip... アプリ名]$ sudo vi /var/www/アプリ名/log/nginx.error.log
[ec2-user@ip... アプリ名]$ vi log/production.log
終わりに
テストデプロイが完了したら、作成したEC2インスタンス、DB環境、Elastic IPアドレスを削除しておきます。
Discussion