LoginSignup
36

More than 3 years have passed since last update.

EC2、RDSを利用してRailsアプリをデプロイする [NGINX + puma + PostgreSQL + Rails 6]

Posted at

目標:AWSのEC2、RDSを利用してRailsアプリをデプロイする

このような目的のため、とりあえずAWS上でアプリが動くことを目標にしています
そのためセキュリティ面で本来必要そうな手順(環境変数定義や、SSL対応)を排除して進行しております

備忘録的に記録を残しておりますので、
思い出したことや、不備あれば随時訂正いたします

気づき

  • 環境構築は学習コスト(時間)高い、けど仮想環境なら壊しても怒られないし、やってみようでなんとかなる

  • 作ったアプリが最終的にどのような環境で動くのか、開発にトップダウンの視点が加わる

  • AWSの無料範囲内で学べる、学習コスト(マネー)低すぎる

環境

  • AWS EC2 (amazon linux2)

  • AWS RDS (PostgreSQL 11.6)

  • NGINX: 1.12.2

  • puma: 3.12.1

  • Rails: 6.0.0

  • Ruby: 2.6.3

備忘録

前提

  • EC2インスタンスが稼働している
  • VPCはインターネット接続が可能(セキュリティグループ、サブネット、ルートテーブルの設定が適切)
  • EC2インスタンスとローカル環境はSSH接続が可能(鍵認証)
  • root権限をもつユーザーを作成しログインしていること
  • RDSでデータベースインスタンスを作成している(今回はPostgreSQL 11.6, 初期データベースsample_app_production)
  • EC2はGitHubと接続しており、/var/www/railsディレクトリにgit cloneでアプリを設置済み
  • master.keyを設定済み

参考:上記については以下のサイトを参考に進めました、感謝。

(DBの選定、設定の部分が異なっております)

【画像付きで丁寧に解説】AWS(EC2)にRailsアプリをイチから上げる方法【その1〜ネットワーク,RDS環境設定編〜】 - Qiita

パッケージのアップデート

まずは

sudo yum update

NGINXインストール

amazon-linux-extrasからインストール可能

sudo amazon-linux-extras install nginx1.12 -y

起動と自動起動設定

sudo systemctl start nginx && sudo systemctl enable nginx && systemctl status nginx

この時点でブラウザからEC2のElastic IPにアクセスすると
"Welcome to nginx on Amazon Linux!"というページが表示される(NGINXがポート: 80ポートで待ち受けている)

参考: NGINX関連コマンド

起動終了

$ sudo systemctl start nginx
$ systemctl status nginx
● nginx.service - The nginx HTTP and reverse proxy server
.
.
.
[sample_app@ip-10-0-0-44 rails]$ sudo systemctl stop nginx

自動起動

sudo systemctl enable nginx

PostgreSQL(クライアント)をインストール

sudo amazon-linux-extras install postgresql11

参考:クライアント以外もインストールする

sudo yum install postgresql-server postgresql-devel postgresql-contrib

参考:amazon-linux-extrasを使わない方法やアンインストールなど

EC2(Amazon Linux2)にPostgreSQLをインストールする | my opinion is my own

PostgreSQL install Amazon linux2
Amazon linux2にpostgresqlをインストールする手順 | 瀬戸内の雲のように

PostgreSQL install
PostgreSQL: Linux downloads (Red Hat family)

PostgreSQL uninstall
How To Completely Uninstall PostgreSQL | ObjectRocket

PostgreSQLに接続

PostgreSQLクライアントであるpsqlからRDSに作成したDBインスタンスに接続

psql --host=<DBインスタンスのエンドポイント> --port=5432 --dbname=sample_app_production  --username=sample_app
--port=5432 --dbname=sample_app_production  --username=sample_app

ユーザ sample_app のパスワード:
psql (11.5、サーバ 11.6)
SSL 接続 (プロトコル: TLSv1.2、暗号化方式: ECDHE-RSA-AES256-GCM-SHA384、ビット長: 256、圧縮: オフ)
"help" でヘルプを表示します。

sample_app_production=> 

RDSに接続可能なこと、初期データベースが存在することが確認できれば問題ないです

参考:AWS RDS公式

PostgreSQL データベースエンジンを実行する DB インスタンスへの接続 - Amazon Relational Database Service

psql を使用した PostgreSQL DB インスタンスへの接続

psql コマンドラインユーティリティのローカルインスタンスを使用して、PostgreSQL DB インスタンスに接続できます。PostgreSQL またはクライアントコンピュータにインストールされた psql クライアントのいずれかが必要です。psql を使用して PostgreSQL DB インスタンスに接続するには、ホスト情報とアクセス認証情報を指定する必要があります。

以下の形式のいずれかを使用して、Amazon RDS 上の PostgreSQL DB インスタンスに接続します。接続時にパスワードを求められます。バッチジョブまたはスクリプトには、--no-password オプションを使用します。

この DB インスタンスに初めて接続する場合、デフォルトのデータベース名 **postgres* を --dbname オプションに使用してみてください。*

Unix の場合、次の形式を使用します。

psql \
--host=<DB instance endpoint> \
--port=<port> \
--username=<master user name> \
--password \
--dbname=<database name> 

(中略)

たとえば、次のコマンドは、架空の認証情報を使用して、mypgdb という PostgreSQL DB インスタンス上の mypostgresql というデータベースに接続します。

psql --host=mypostgresql.c6c8mwvfdgv0.us-west-2.rds.amazonaws.com --port=5432 --username=awsuser --password --dbname=mypgdb 

参考:psql認証のtrust, ident, md5

RDSでDBインスタンスを作成したときの情報を使用します
接続認証にOSのユーザー名とDBのユーザー名の一致を求める仕組みがあるようですが
RDSとのhost-client接続ではユーザー名が違ってもパスワード認証で問題なかったです

PostgreSQL 認証に失敗しないための Ident、MD5、Trust 比較 - eTuts+ Server Tutorial

うまくいかない時は

タイムアウトしてしまい、認証に至らない(超重要)

セキュリティグループの設定を確認する
私はアウトバウンドの設定が必要でした(控えめに言って半日悩んだ)
EC2からRDS(MySQL)に接続できない。セキュリティグループの設定について・・・ - Qiita

クライアント(EC2)とホスト(RDS)のPostgreSQLバージョンが異なる

amazon-linux-extrasを使用せずにインストールすると
クライアント側が古いバージョンになってしまうことがありました
(RDS側は初期設定のPostgreSQL 11.6)
古いバージョンのアンインストールをして、新しいものをインストールしてください

警告: psql のメジャーバージョンは 9 ですが、サーバーのメジャーバージョンは 11 です。
         psql の機能の中で、動作しないものがあるかもしれません。

参考(メモ):以下はクライアントとしてpsqlを利用するだけなら関係ないかも

設定ファイルの場所

postgresql.conf

sudo vi /var/lib/pgsql/data/postgresql.conf

pg_hba.conf(認証関係)

sudo vi /var/lib/pgsql/data/pg_hba.conf

ユーザーpostgresのパスワードがわからない

途中自動生成されたユーザーpostgresのpasswordがわからず再設定しました
Postgresでパスワードを忘れた場合の対策 - Qiita

local(EC2)にDBを設置する場合のコマンドメモ

$ sudo postgresql-setup initdb
$ sudo systemctl enable postgresql.service
$ sudo systemctl start postgresql.service

PostgreSQLの設定をする

config/database.ymlを編集

default: &default
  adapter: postgresql
  encoding: unicode
  username: postgres
  password: password
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  host: db
  database: myapp_development

test:
  <<: *default
  host: db
  database: myapp_test

production:
  <<: *default 
  database: sample_app_production
  username: postgres # RDS DBインスタンスのユーザー名
  password: password # RDS DBインスタンスのパスワード
  host: xxxx.xxxx.ap-northeast-1.rds.amazonaws.com # RDS DBインスタンスのエンドポイント

本来ここに平文でユーザー名やパスワードを記録しないほうがよい

productionの<<: *defaultが抜けていてadapterが指定されていないよと半日怒られ続けました...
"adapterActiveRecord::AdapterNotSpecified: database configuration does not specify adapter"

hostを指定していればportの指定は不要でした

参考:database.ymlのPostgreSQL向け設定情報 ActiveRecord::ConnectionAdapters::PostgreSQLAdapter

NGINXの設定をする

dockerで環境構築したものが引き継がれたらいいのに...

pumaとsocket接続するようにしています

/etc/nginx/conf.d/sample_app.confを作成、編集

vi /etc/nginx/conf.d/sample_app.conf
error_log  /var/www/rails/sample_app/log/nginx.error.log;
access_log /var/www/rails/sample_app/log/nginx.access.log;

##ココ
upstream sample_app {
        server unix:///var/www/rails/sample_app/tmp/sockets/puma.sock;
}

server {
    listen 80;
    client_max_body_size 4G;
    server_name 54.238.15.249;

    keepalive_timeout 5;

    # Location of our static files
    root /var/www/rails/sample_app/public;

    location ~ ^/assets/ {
        root /var/www/rails/sample_app/public;
    }

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;

        if (!-f $request_filename) {
            proxy_pass http://sample_app;
            break;
        }
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /var/www/rails/sample_app/public;
    }
}

NGINX再起動

sudo systemctl restart nginx

参考:NGINXコマンドほか

自動起動設定

sudo chkconfig nginx on

pumaの設定をする

アプリケーションサーバーとしてunicornは使用しません
Railsに組み込まれているpumaを利用してみます

NGINXとpumaはソケット接続するためconfig/puma.rbに以下を2点編集
(私はDockerで開発環境をNGINX + pumaに設定をした時点で設定済みでした)

# ポート: 3000をlistenしない
#port        ENV.fetch("PORT") { 3000 }

# socketの設定
bind "unix://#{Rails.root}/tmp/sockets/puma.sock"

参考:puma.rbの設定 puma/dsl.rb at master · puma/puma

参考:puma関連コマンド

production環境でpuma起動

bundle exec rails s -e production

puma, pumactlコマンドは私の環境では使えませんでした

bundle exec pumactl start

Pumaの起動におけるpumaコマンドとpumactlコマンドの違い - Qiita

pumaコマンドとpumactlコマンドの違い

このあたりちゃんと説明した日本語の情報がとても少なくて、リポジトリのREADMEを読んだり「what is deference between puma command and pumactl command」みたいなキーワードでがんばって調べたらRuby Journalになんかそれっぽいことが書いてあった。英語ができないととてもつらい。

As we can see that above operations can be tedious and error prone and definitely not fun to work with a big deployment scale. Introducing pumactl, this utility automates all of above tasks.
Digesting Pumactl

要するに、pumaコマンドで起動したりしてるといちいちオプション付けないといけないから大規模開発だとめっちゃつらいしやばいくらい闇だし、pumaコマンドでやってることはpumactlコマンドで自動化しような。ということを言っている。

なるほど、確かに。pumactlコマンドだと問答無用で設定ファイルが読まれるようになっているから、スレッドの数はどうするんだだの、ポート番号はどうするんだだの、ちゃんとコードで管理できる。

停止や再起動においてもpumactlコマンドで安全にできるらしい。

Available commands: halt, restart, phased-restart, start, stats, status, stop, reload-worker-directory

YarnとNodeをインストール、アップデート

Rails 6以降で必要
以降の過程でYarnが必要、nodeが古いと言われます

Yarn

# wgetインストール済みなら省略可能
$ yum -y install wget
$ wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo
$ curl --silent --location https://rpm.nodesource.com/setup_6.x | bash -


$ sudo yum install yarn

$ yarn --version
1.22.4

Node

$ sudo npm install n -g
$ sudo n stable

$ node -v
v12.18.2

参考:capistrano・EC2・postgresql・rails6で自動デプロイ設定した際のエラー例 - Qiita

アプリを起動させる

マイグレーション、アセットプリコンパイル

# マイグレーション
bundle exec rake db:migrate RAILS_ENV=production

# プリコンパイル
bundle exec rake assets:precompile RAILS_ENV=production

Webサーバー、アプリケーションサーバー起動

# NGINX再起動
sudo service nginx restart

# puma起動
bundle exec rails s -e production

問題なければブラウザからElastic IPにアクセスすることで、
アプリのインターフェイスが表示されるはずです

"We're sorry, but something went wrong."

もうね、怒られるのなれましたよ

ブラウザにに上記メッセージが表示される場合ログを確認しましょう

cd log # アプリルート直下

tail -n 30 production.log

tail -n 30 nginx.error.log

私の場合production.logは空っぽで
nginx.error.logで以下のエラーが記録されていました

"connect() to unix:/home/var/www/rails/sample_app/tmp/sockets/puma.sock failed (2: No such file or directory) while connecting to upstream"

/etc/nginx/conf.d/sample_app.confのsocketパス指定が間違っておりました

その他既知の問題点

  • SSL非対応
    config/environments/production.rbconfig.force_ssl = trueconfig.force_ssl = false

  • 新規ユーザー登録のところでメール認証がうまく行かない

今後対応

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36