※デプロイの方法の手順④ 今後の自分のメモ用に
#本番環境での設定
※これ以降特に指示がない場合、作業は/var/www/App名で行うので注意してください。
###EC2の能力を拡張しよう
現状動かしているEC2のインスタンスではコンピューターの能力が足りず、Gemのインストール時などにエラーが発生する可能性があります。具体的には、コンピューターの処理能力に関係するメモリというものが足りません。これは、無料で動かせるインスタンスの限界であるため仕方ありません。
そこで、今後の設定を行う前にメモリを増強する処理を行います。
###Swap(スワップ)領域
コンピュータが処理を行う際、メモリと呼ばれる場所に処理内容が一時的に記録されます。メモリは容量が決まっており、容量を超えてしまうとエラーで処理が止まってしまいます。Swap領域は、メモリが使い切られそうになった時にメモリの容量を一時的に増やすために準備されるファイルです。
EC2はデフォルトではSwap領域を用意していないため、これを準備することでメモリ不足の処理エラーを防ぎます。
###Swap領域を用意しましょう
まずは、EC2にログインしてホームディレクトリに移動します。
・ターミナル(EC2サーバ)
#ホームディレクトリに移動
[ec2-user@ip-172-31-25-189 ~]$ cd
続いて、以下の順番でコマンドを実行します。
・ターミナル(EC2サーバ)
#処理に時間がかかる可能性があるコマンドです
[ec2-user@ip-172-31-25-189 ~]$ sudo dd if=/dev/zero of=/swapfile1 bs=1M count=512
# しばらく待って、以下のように表示されれば成功
512+0 レコード入力
512+0 レコード出力
536870912 バイト (537 MB) コピーされました、 7.35077 秒、 73.0 MB/秒
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-25-189 ~]$ sudo chmod 600 /swapfile1
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-25-189 ~]$ sudo mkswap /swapfile1
# 以下のように表示されれば成功
スワップ空間バージョン1を設定します、サイズ = 524284 KiB
ラベルはありません, UUID=74a961ba-7a33-4c18-b1cd-9779bcda8ab1
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-25-189 ~]$ sudo swapon /swapfile1
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-25-189 ~]$ sudo sh -c 'echo "/swapfile1 none swap sw 0 0" >> /etc/fstab'
※最後のコードは見づらくなっていますが、「/swapfile1 none」より右に続きがありますのでご注意ください
これで、Swap領域を確保することができました。
#gemをインストールしよう
クローンしたアプリケーションを起動するために必要なgemを以下のコマンドでインストールしましょう。
###1. クローンしたディレクトリに移動し、 rbenvでインストールされたRubyが使われているかチェックしましょう。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 www]$ cd /var/www/chat-space
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ruby -v
ruby 2.5.1p112 (2016-04-26 revision 54768) [x86_64-linux]
ruby 2.5.1 ... となっていれば成功です。そうでない場合は、もともと用意されているRubyが利用されているので、Rubyのインストールが成功しているか確認してください。
###2.本番環境でgemを管理するためのbundlerをインストールして、bundle installを実行しましょう。
まず今まで開発環境(ローカル)で開発してきたChatSpaceにおいて、どのバージョンのbundlerが使われていたのか確認します。
・ターミナル(ローカル)
#Appのディレクトリで以下を実行
$ bundler -v
Bundler version 2.0.1
# 開発環境によってバージョンは異なります。
開発環境で仕様しているbundlerのバージョンがわかったので、同じバージョンのものをEC2サーバ側にも導入します。上記の場合では、bundler 2.0.1のバージョンを導入して bundle install を実行します。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ gem install bundler -v 2.0.1
# ローカルで確認したbundlerのバージョンを導入する
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ bundle install
# 上記コマンドは、数分以上かかる場合もあります。
#環境変数の設定をしよう
データベースのパスワードなどセキュリティのためにGithubにアップロードすることができない情報は、環境変数というものを利用して設定します。
環境変数は、Railsからは ENV['<環境変数名>'] という記述でその値を利用することができます。
今、config/secrets.yml と config/database.yml を見てみてください。
例えば <%= ENV["SECRET_KEY_BASE"] %> と書かれている部分は、 SECRET_KEY_BASE という環境変数の値になります。
###secret_key_base
secret_key_baseとは、Cookieの暗号化に用いられる文字列です。Railsアプリケーションを動作させる際は必ず用意する必要があります。また、外部に漏らしてはいけない値であるため、こちらも環境変数から参照します。
secret_key_baseは以下のコマンドを打つことで生成できます。
###1. secret_key_baseを作成しましょう。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ rake secret
69619d9a75b78f2e1c87ec5e07541b42f23efeb6a54e97da3723de06fe74af29d5718adff77d2b04b2805d3a1e143fa61baacfbf4ca2c6fcc608cff8d5a28e8d
この長い英数の羅列は、この後利用するのでコピーしておきましょう。
###2. 以下の指示にしたがって、環境変数を設定しましょう。
実際にEC2インスタンスに環境変数を設定してみましょう。
環境変数は /etc/environment というファイルに保存することで、サーバ全体に適用されます。環境変数の書き込みはvimコマンドを使用して行います。
※vimコマンドについて曖昧な方は、こちら
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 ~]$ sudo vim /etc/environment
i と打ち込んで入力モードに切り替えた後、下記の記述を打ち込みます。
= の前後にスペースは入れません。
・/etc/environment
#前章で設定したMySQLのrootユーザーのパスワードを入力
DATABASE_PASSWORD='MySQLのrootユーザーのパスワード'
SECRET_KEY_BASE='先程コピーしたsecret_key_base'
###3. 設定した環境変数を反映させるために、一度本番環境をログアウトしましょう。
書き込みができたら esc(エスケープキー)を押下後、:wqと入力して内容を保存します。保存できたら環境変数を適用するために一旦ログアウトします。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 ~]$ exit
logout
Connection to 52.xx.xx.xx closed.
###4. もう一度本番環境へログインしましょう。
exitでログアウトすると、ローカル環境となります。再度SSHし直します。
・ターミナル(EC2サーバ)
$ ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
(ダウンロードした鍵を用いて、ec2-userとしてログイン)
###5. 設定した環境変数が本当に適用されているか確認しましょう。
SSHし直したら、 env というコマンドと grep を組み合わせて、先程設定した環境変数が適用されているか確認します。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 ~]$ env | grep SECRET_KEY_BASE
SECRET_KEY_BASE='secret_key_base'
[ec2-user@ip-172-31-23-189 ~]$ env | grep DATABASE_PASSWORD
DATABASE_PASSWORD='MySQLのrootユーザーのパスワード'
#ポートを解放しよう
立ち上げたばかりのEC2インスタンスはSSHでアクセスすることはできますが、HTTPなどの他の通信方法では一切つながらないようになっています。そのため、WEBサーバとして利用するEC2インスタンスは事前にHTTPがつながるように「ポート」を開放する必要があります。
先程、 config/unicorn.rb に listen 3000 と記述しましたが、これはRailsのサーバを3000番ポートで起動するということを意味するのでした。
###セキュリティグループについて
ポートの設定をするためにはEC2の「セキュリティグループ」という設定を変更する必要があります。
セキュリティグループとは、EC2サーバが属するまとまりのようなもので、複数のEC2インスタンスのネットワーク設定を一括で行うためのものです。
###セキュリティグループのポートを設定
下図の手順に従って、セキュリティグループのポートを設定しましょう。
まず、EC2インスタンス一覧画面から、対象のインスタンスを選択し、「セキュリティグループ」のリンク(図中では「launch-wizard-1」)をクリックします。
すると、インスタンスの属するセキュリティグループの設定画面に移動するので、「インバウンド」タブの中の「編集」をクリックします。
モーダルが開くので、「ルールの追加」をクリックします。
タイプを「カスタムTCPルール」、プロトコルを「TCP」、ポート範囲を「3000」、送信元を「カスタム」「0.0.0.0/0」に設定します。
「0.0.0.0」は「全てのアクセスを許可する」という意味です。
以上で、ポートの開放が完了です。
この作業が終わっていないと、起動したRailsにアクセスできないので注意してください。
#本番環境でRailsを起動
###unicorn_railsコマンド
-c config/unicorn.rb は設定ファイルの指定、 -E production は環境を「本番モードとして動作させる」ことを示します。
-Dは「Daemon(デーモン)」の略で、プログラムを起動させつつターミナルで別のコマンドを打てるようにするオプションです。詳しくは、「プログラム デーモン」で調べてみましょう。
###1. 以下のコマンドを実行し、ユニコーンを起動させてみましょう
unicorn のgemをインストールしていると unicorn_rails というコマンドが使えるようになっています。これは、ローカル環境でrails sを行うのと同じように利用します。EC2サーバにて、以下のコマンドを実行します。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 ~]$ cd /var/www/[リポジトリ]
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ bundle exec unicorn_rails -c config/unicorn.rb -E production -D
このコマンドを実行すると、以下のようにすぐにコマンドが終了してしまいます。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ bundle exec unicorn_rails -c config/unicorn.rb -E production -D
master failed to start, check stderr log for details
これはもう一つ必要な作業をしていないからなのですが、何が原因だかわかるでしょうか?原因を調査するために、エラーログを見に行きましょう。エラーログの確認は、どんなエラーに対してもまずはじめにすべきことです。
###2. Unicornのエラーログを確認しましょう
先程の config/unicorn.rb をもう一度確認してみると、 stderr_path "#{app_path}/log/unicorn.stderr.log" という記述があります。これは、「Unicorn関係で起きたエラーをlog/unicorn.stderr.log」に記録するという指定になっています。
それでは log/unicorn.stderr.log を確認してみましょう。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ less log/unicorn.stderr.log
I, [2016-12-21T04:01:19.135154 #18813] INFO -- : Refreshing Gem list
I, [2016-12-21T04:01:20.732521 #18813] INFO -- : listening on addr=0.0.0.0:3000 fd=10
E, [2016-12-21T04:01:20.734067 #18813] ERROR -- : Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/mysql2_adapter.rb:29:in `rescue in mysql2_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/mysql2_adapter.rb:12:in `mysql2_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:721:in `new_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:765:in `checkout_new_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:744:in `try_to_checkout_new_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:705:in `acquire_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:501:in `checkout'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:364:in `connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:875:in `retrieve_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_handling.rb:128:in `retrieve_connection'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activerecord-5.0.0.1/lib/active_record/connection_handling.rb:91:in `connection'
config/unicorn.rb:36:in `block in reload'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:502:in `spawn_missing_workers'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:132:in `start'
/home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/unicorn-5.1.0/bin/unicorn_rails:209:in `<top (required)>'
/home/ec2-user/.rbenv/versions/2.5.1/bin/unicorn_rails:23:in `load'
/home/ec2-user/.rbenv/versions/2.5.1/bin/unicorn_rails:23:in `<main>'
このERRORという行を見てみると、これは本番環境でインストールするmysqlの設定がローカルとは異なるため、mysqlへ接続できなくなっている状態です。
###3. database.ymlの本番環境の設定を編集しましょう
本番環境のmysqlの設定に合わせるため、ローカルのdatabase.ymlを以下のように編集して下さい。
・config/database.yml(ローカル)
production:
<<: *default
database: ~~~(それぞれのアプリケーション名によって異なっています。こちらは編集しないでください)
username: root
password: <%= ENV['DATABASE_PASSWORD'] %>
socket: /var/lib/mysql/mysql.sock
###4. ローカルでの編集をコミットして、GitHubにプッシュしましょう。
リモートリポジトリが更新されたため、サーバ上のアプリケーションにも反映させましょう。
先ほどはgit cloneコマンドを利用しましたが、今回はすでにEC2とGithubは接続できているため、git pullコマンドを利用します。
サーバのアプリケーションのあるディレクトリで以下のようにコマンドを実行してください。
※別にブランチを切っている場合は、masterブランチにmergeしてから以下のコマンドを実行しましょう。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>] git pull origin master
###5. データベースを作成しマイグレーションを実行し直しましょう。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ rails db:create RAILS_ENV=production
Created database '<データベース名>'
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ rails db:migrate RAILS_ENV=production
もしここでMysql2::Error: Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock'というエラーが起こった場合、mysqlが起動していない可能性があります。sudo service mysqld startというコマンドをターミナルから打ち込み、mysqlの起動を試してみましょう。
###6. 再度Railsを起動させましょう
これでデータベースの準備が整ったので、再びRailsを起動しましょう。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ bundle exec unicorn_rails -c config/unicorn.rb -E production -D
今度はエラーが出ることなくコマンドが終了したと思います。
###7. ブラウザで http://<サーバに紐付けたElastic IP>:3000/ にアクセスしてみましょう
ブラウザにCSSの反映されていない(ビューが崩れている)画面が表示されていれば成功です。
#アセットファイルをコンパイルしよう
無事Railsが起動できましたが、今のままではレイアウトが崩れてしまっているでしょう。
開発中には正常に表示されていたのに、本番ではうまく表示されないのはなぜでしょうか?
これは、開発中はアクセス毎にアセットファイル(画像・CSS・JSファイルの総称)を自動的にコンパイル(圧縮)する仕組みが備わっていますが、本番モードのときにはパフォーマンスのためアクセス毎には実行されないようになっているためです。
###1. アセットコンパイルを実行しましょう。
本番モードでは、事前にアセットをコンパイルする必要があります。そのために次のコマンドを実行してください。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ rails assets:precompile RAILS_ENV=production
###2. Unicornのプロセスを確認しましょう
コンパイルが成功したら反映を確認するため、Railsを再起動します。しかし、まずは今動いているUnicornをストップします。そのために、Unicornのプロセスを確認し、プロセスを止めます。ターミナルからプロセスを確認するにはpsコマンドを利用します。
###psコマンド
psコマンドは、現在動いているプロセスを確認するためのコマンドです。
###3. psコマンドを入力してUnicornのプロセスを確認しましょう
EC2のターミナルから以下のように入力します。「aux」と打っているのは、psコマンドのオプションです。表示結果を見やすくしてくれます。また、| grep unicornとしているのはpsコマンドの結果からunicorn関連のプロセスのみを抽出するためです。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ps aux | grep unicorn
...
#すると、以下のようにプロセスが表示されるはずです。
ec2-user 17877 0.4 18.1 588472 182840 ? Sl 01:55 0:02 unicorn_rails master -c config/unicorn.rb -E production -D
ec2-user 17881 0.0 17.3 589088 175164 ? Sl 01:55 0:00 unicorn_rails worker[0] -c config/unicorn.rb -E production -D
ec2-user 17911 0.0 0.2 110532 2180 pts/0 S+ 02:05 0:00 grep --color=auto unicorn
大事なのは左から2番目の列です。ここに表示されるのがプロセスのid、つまりPIDになります。
「unicorn_rails master」と表示されているプロセスがUnicornのプロセス本体です。この時のPIDは、17877となっています。
###4. UnicornのプロセスをKillしましょう
ターミナルからプロセスをストップするにはkillコマンドを利用します。
###killコマンド
killコマンドは、現在動いているプロセスを停止させるためのコマンドです。
###5. killコマンドを入力してUnicornのプロセスを停止しましょう
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ kill <確認したunicorn rails masterのPID>
...
#再度、プロセスを表示させ終了できていることを確認しましょう。
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ps aux | grep unicorn
...
ec2-user 17911 0.0 0.2 110532 2180 pts/0 S+ 02:05 0:00 grep --color=auto unicorn
実行結果が上記のようになっていればUnicornを停止が完了です。ローカルで行なっていた、ctrl + cでサーバをストップするという作業と同じことをしたことになります。
####プロセスが終了できない場合
下記の2行が表示が消えていない場合はプロセスが終了できていないことになります。
・ターミナル(EC2サーバ)
ec2-user 17877 0.4 18.1 588472 182840 ? Sl 01:55 0:02 unicorn_rails master -c config/unicorn.rb -E production -D
ec2-user 17881 0.0 17.3 589088 175164 ? Sl 01:55 0:00 unicorn_rails worker[0] -c config/unicorn.rb -E production -D
そのような場合は、プロセスを強制終了しましょう。オプション-9をkillコマンドにつけると強制終了を実行できます。通常のkillコマンドで削除できない場合はこちらを使用しましょう。
$ kill -9 [プロセスID]
#プロセスIDはpsコマンドで検索した結果の数字に置き換えてください。上記であれば17877です。
###6. 先頭にRAILS_SERVE_STATIC_FILES=1をつけて、unicornを起動しましょう
続いて再びunicornを起動しましょう。このとき RAILS_SERVE_STATIC_FILES=1 という指定を先頭に追加してください。これは、コンパイルされたアセットをRailsが見つけられるような指定になります。以下のようにコマンドを実行してください。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -D
...
###7.ブラウザで確認してみましょう
もう一度、ブラウザで http://<Elastic IP>:3000/ にアクセスしてみましょう。今度はレイアウト崩れも無くサイトが正常に表示されていることでしょう。
####通常のログの確認方法
アプリケーションが上手に起動している際はログを気にする事はないかもしれませんが、以下のようにコマンドを実行する事で正常に動いている際のログも確認することができます。
・ターミナル(EC2サーバ)
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$tail -f log/production.log
...
実行すると、アプリケーションにアクセスがあった場合にリアルタイムにログが流れることが確認できます。
###tail コマンド
指定したファイルの最後の行を表示するコマンドです。-fオプションを追加することで、そのままリアルタイムに更新されるようになります。
tail -fコマンドによる表示を終了したい場合は、ctrl + cで中断できます。
#Railsの起動がうまくできなかった時
上記のコマンドを実行してもRailsが起動しないときや、起動できてもIPアドレス:3000にアクセスするとエラーが表示されていることがあります。
・pushのし忘れ、またはEC2サーバ側でのpullのし忘れは無いか
・EC2サーバ側で、/var/www/<リポジトリ名>/log/unicorn.stderr.logをlessまたはcatコマンドで確認し、エラーが出ていないか確認する(下に行くほど最新のログです。時刻表記がUTCであることに注意してください)
・ローカルでの編集のpushやEC2でのgit pullを忘れていないか
・mysqlの起動は正しく行えているか
・EC2サーバ側のSECRET_KEY_BASE等は正しく設定できているか
・EC2インスタンスの再起動を行ってみる