hiyoko-programingの日記

プログラミングを勉強したてのひよっ子。   エンジニア目指して勉強中。

全世界に公開

EC2のサーバにChatSpaceのコードをクローンする準備

まず、アプリケーションのコードをGithubからEC2サーバへクローンする。全世界に公開できるIPアドレスを持ったEC2サーバ上でChatSpaceを動かすためである。

GithubSSH鍵を登録する

現状、EC2サーバにアプリケーションのコードをクローンしようとしてもpermission deniedとエラーが出てしまう。
これは、Githubから見てこのEC2インスタンスが何者かわからないため。

https://tech-master.s3.amazonaws.com/uploads/curriculums//d3bf10bbc2c2c2760fe34622bd3f8f60.png

EC2インスタンスからGithubにアクセスするためには、作成したEC2インスタンスSSH公開鍵をGithubに登録する必要がある。
SSH鍵をGithubに登録すると、Githubはそれを認証に利用し、コードのクローンを許可してくれるようになる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//4ceb5a3527c15a1ca97986837da096a9.png

 

EC2サーバのSSH鍵ペアを作成する

途中で passphrase など3段階ほど入力を求められることがあるが、全て何も入力せずにEnterキーで進む。
※EC2サーバにログインしていない場合はログインしてから以下の作業を行う。

ターミナル(EC2サーバ)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
[ec2-user@ip-172-31-23-189 ~]$ ssh-keygen -t rsa -b 4096
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:
3a:8c:1d:d1:a9:22:c7:6e:6b:43:22:31:0f:ca:63:fa ec2-user@ip-172-31-23-189
The key's randomart image is:
+--[ RSA 4096]----+
|    +            |
| . . =           |
|  = . o .        |
| * o . o         |
|= *     S        |
|.* +     .       |
|  * +            |
| .E+ .           |
| .o              |
+-----------------+

次に、以下のコマンドで生成されたSSH公開鍵を表示し、値をコピーする。

ターミナル(EC2サーバ)
1
2
[ec2-user@ip-172-31-23-189 ~]$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2E......

そして、catで表示させた公開鍵を、Githubにアクセスして登録していく。
まず、以下のURLにアクセスしてください。
https://github.com/settings/keys

https://tech-master.s3.amazonaws.com/uploads/curriculums//fb528668062ce467f263dc9f66940acc.jpeg

Githubに鍵を登録できたら、SSH接続できるか以下のコマンドで確認。

ターミナル(EC2サーバ)
1
2
[ec2-user@ip-172-31-23-189 ~]$ ssh -T git@github.com
Hi <Githubユーザー名>! You've successfully authenticated, but GitHub does not provide shell access.

途中でこのまま続けるかどうかYes/Noで聞かれることがあるが、Yesで進む。

Permission denied (publickey). と表示された場合は、SSH鍵の設定が間違っているので、作業を確認する。

アプリケーションサーバの設定

アプリケーションサーバの導入と設定を行う。

 アプリケーションサーバ(Appサーバ)

アプリケーションサーバとは、ブラウザからの「リクエスト」を受け付けRailsアプリケーションを実際に動作させるソフトウェアのこと。実はすでにアプリケーションサーバを利用している。それが、rails sコマンド。

rails sコマンドとは何か?

普段、ローカル環境でRuby on Railsのアプリケーションの動作を確認する際、以下の手順を踏む。

①ターミナルからrails sコマンドを打つ

②ブラウザでlocalhost:3000にアクセス

①をせずに②を行なっても、アプリケーションの動作を確認できない。

実は、①の操作はまさに「アプリケーションサーバの起動」を行なっている

rails sコマンドを打つと、pumaと呼ばれるアプリケーションサーバが起動している。以下は、ローカルでrails sコマンドを打った時の様子。

【例】ローカルでアプリケーションサーバを起動
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#rails sコマンドでサーバを起動
$rails s
=> Booting Puma
=> Rails 5.0.7.2 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.0 (ruby 2.3.1-p112), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

3行目に「 Booting Puma」と書かれている。つまり「pumaを起動しています」ということ。rails sコマンドを打つことで、pumaというアプリケーションサーバが起動していることがわかる。

また、localhost:3000とは自身のPCを指すドメインである。

つまり、①、②は言い換えれば「自身のPCをサーバに見立てアプリケーションサーバを起動し、ブラウザから自身のPCにアクセスする」ということを行なっていた。

https://tech-master.s3.amazonaws.com/uploads/curriculums//dc615cd5dcb8b8a11befdef13c4e8a63.png

アプリケーションサーバが動いていれば、ブラウザからのリクエストを受け付けてRailsアプリケーションが動作する。

という訳で、全世界に公開するEC2サーバ上でもアプリケーションサーバを動かす必要がある

 Unicorn

全世界に公開されるサーバ上で良く利用されるアプリケーションサーバrails sコマンドの代わりにunicorn_railsコマンドで起動することができる。

この後、EC2サーバにSSH接続しUnicornを起動することで全世界からアクセスできるようにしていく。イメージとしては、以下の図。

https://tech-master.s3.amazonaws.com/uploads/curriculums//238665045a83e58cb58ea059d1558aa3.png

ここでちょっと脇道にそれて、Unicornを理解するために必要な重要で新しい概念がある。それが、プロセスである。

プロセスについて

 プロセス

プロセスとは、PC(サーバ)上で動く全てのプログラムの実行時の単位。ここで言うプログラムとは、ブラウザや音楽再生ソフト、ExcelといったGUIや、Rubyなどのスクリプト言語の実行などが含まれる。

プログラムが動いている数だけ、プロセスが存在している。例えばテキストエディタを起動する時、テキストエディタ用のプロセスが生み出される。

プロセスを確認してみる

PCがMacの場合、アクティビティモニタというアプリケーションでプロセスを確認することができる。
アクティビティモニタは、現在MacPC上で動いているプログラムの状況をモニタリングするアプリです。
以下が、アクティビティモニタを開いた画面です。

https://tech-master.s3.amazonaws.com/uploads/curriculums//126547c61ef6ab4c2c9292ee71c8b1f8.png

一番左上の「プロセス名」と言う列に、Google Chromeや、アクティビティモニタ自身も表示されている。
つまり、Google Chromeアクティビティモニタもプロセスであるということ。

もう一つ注目したいのは、真ん中あたりにある「PID(プロセスアイディー)」という列である。

https://tech-master.s3.amazonaws.com/uploads/curriculums//e626bddb165744b6715f1d256d064fe3.png

数字が入っているが、これはプロセスを識別するための一意の数字になる。PIDがあることで、あるプログラムから別のプロセスを指定して操作したり、プロセスからプログラムを停止したりできる。

Unicornなどのアプリケーションサーバを起動するとアプリケーションサーバのプロセスが生まれる。アプリケーションサーバのプロセスがあれば、リクエストを受け付けブラウザにレスポンスを返すことができる。

 Unicornをインストールする

UnicornRubyで作成されており、gem化されている。

1. ローカルでGemfile を編集。

Gemfile
1
2
3
group :production do
  gem 'unicorn', '5.4.1'
end

2. ローカルのChatSpaceディレクトリでbundle installコマンドを実行。

ターミナル(ローカル)
1
$ bundle install

 group :production do ~ end

このgroup :production do ~ endの間に記述されたgemは本番環境のみで読み込まれる。
Unicornは本番環境でのみ必要なので、開発環境下では不要。

 config/unicorn.rbを作成し、内容を以下のように編集して保存

Unicornの設定ファイルとして、次の内容でファイルを作成する。
最初からは存在していないので、自分でconfigディレクトリ以下に作成する。

まずはコピー&ペーストで作成したファイルに貼り付ける。

config/unicorn.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく
app_path = File.expand_path('../../', __FILE__)

#アプリケーションサーバの性能を決定する
worker_processes 1

#アプリケーションの設置されているディレクトリを指定
working_directory app_path

#Unicornの起動に必要なファイルの設置場所を指定
pid "#{app_path}/tmp/pids/unicorn.pid"

#ポート番号を指定
listen 3000

#エラーのログを記録するファイルを指定
stderr_path "#{app_path}/log/unicorn.stderr.log"

#通常のログを記録するファイルを指定
stdout_path "#{app_path}/log/unicorn.stdout.log"

#Railsアプリケーションの応答を待つ上限時間を設定
timeout 60

#以下は応用的な設定なので説明は割愛

preload_app true
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true

check_client_connection false

run_once = true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) &&
    ActiveRecord::Base.connection.disconnect!

  if run_once
    run_once = false # prevent from firing again
  end

  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exist?(old_pid) && server.pid != old_pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH => e
      logger.error e
    end
  end
end

after_fork do |_server, _worker|
  defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection
end

上記のファイルの中で、重要な設定とそれにまつわる概念について。

 worker(ワーカー)

Unicornは、プロセスを分裂させることができる。この分裂したプロセス全てをworkerと呼ぶ。プロセスを分裂させることで、リクエストに対してのレスポンスを高速にすることができる。後述するworker_processesという設定項目で、workerの数を決定する。

ブラウザなどからリクエストが来ると、UnicornのworkerがRailsアプリケーションを動かす。Railsは、リクエストの内容とルーティングを照らし合わせ最終的に適切なビュー(HTML)もしくはJSONをレスポンスする。レスポンスを受け取ったUnicornは、それをブラウザに返す。一連の動きはおよそ0.1 ~ 0.5秒程度で行われる。常にそれ以上のスピードでリクエストが頻発するようなアプリケーションだと、1つのworkerだけでは処理が追いつかず、レスポンスまで長い時間がかかってしまったり最悪サーバがストップしてしまう。そんな時、worker_processesの数を 2,3,4と増やすことでアプリケーションからのレスポンスを早くすることができる。

Unicornの設定

設定項目 詳細
worker_processes リクエストを受け付けレスポンスを生成するworker(ワーカー)の数を決める。
working_directory UnicornRailsのコードを動かす際、ルーティングなど実際に参照するファイルを探すディレクトリを指定する。
pid Unicornは、起動する際にプロセスidが書かれたファイルを生成します。その場所を指定する。
listen どのポート番号のリクエストを受け付けることにするかを決定する。今回は、3000番ポートを指定している。

ここまで変更できたら、ファイルをコミットし、Githubにpushしておく。ブランチを切っている場合は、masterブランチにmergeもしておく。

デプロイ時にエラーの原因となる記述の対策

Uglifierというgemがあり、これはJavaScriptを軽量化するためのもの。しかし、ChatSpaceのJavaScriptで使用しているテンプレートリテラル記法(`)に対応していない。そのため、デプロイ時にエラーの原因となる。
そこで、この部分をコメントアウトすることで対策する。

Uglifierについての記述をコメントアウトする

config/environments/production.rb(修正前)
1
config.assets.js_compressor = :uglifier
config/environments/production.rb(修正後)
1
# config.assets.js_compressor = :uglifier

なお、ChatSpace程度のjsの記述量であれば、軽量化をしてもしなくても違いはほとんどありません。

変更修正をリモートリポジトリに反映する

変更修正をコミットしてプッシュ

GitHub Desktopからコミットしてプッシュ。この時必ず、masterブランチで行う。もし、別ブランチでコミット&プッシュした場合は、リモートリポジトリでプルリクエストを作成し、ブランチをmasterへマージする。

Githubからコードをクローンする

続いて、Unicornの設定を済ませたコードをEC2インスタンスにクローンする。

まず、以下のコマンドを入力して、ディレクトリを作成。今回は、ここで作成したディレクトリにアプリケーションを設置する。

 1. /var/wwwディレクトリを作成し、権限をec2-userに変更

ターミナル(EC2サーバ)
1
2
3
4
#mkdirコマンドで新たにディレクトリを作成
[ec2-user@ip-172-31-23-189 ~]$ sudo mkdir /var/www/
#作成したwwwディレクトリの権限をec2-userに変更
[ec2-user@ip-172-31-23-189 ~]$ sudo chown ec2-user /var/www/

2. Githubから「リポジトリURL」を取得

https://tech-master.s3.amazonaws.com/uploads/curriculums//e5163d180a6607ede9bcc85bd035843c.png

3. 取得した「リポジトリURL」を使って、コードをクローン

ターミナル(EC2サーバ)
1
2
[ec2-user@ip-172-31-23-189 ~]$ cd /var/www/
[ec2-user@ip-172-31-23-189 www]$ git clone https://github.com/<リポジトリ名>.git

上記の入力でエラーがでなければ、先に進む。

本番環境での設定

サービスを公開するための設定を行う。
※これ以降、特に指示がない場合、作業は/var/www/chat-spaceで行うので注意。

EC2の能力を拡張する

現状動かしているEC2のインスタンスではコンピューターの能力が足りず、Gemのインストール時などにエラーが発生する可能性がある。具体的には、コンピューターの処理能力に関係するメモリというものが足りない。これは、無料で動かせるインスタンスの限界であるため仕方ない。そこで、メモリを増強する処理を行う。

Swap(スワップ)領域

コンピュータが処理を行う際、メモリと呼ばれる場所に処理内容が一時的に記録される。メモリは容量が決まっており、容量を超えてしまうとエラーで処理が止まってしまう。Swap領域は、メモリが使い切られそうになった時にメモリの容量を一時的に増やすために準備されるファイル。

EC2はデフォルトではSwap領域を用意していないため、これを準備することでメモリ不足の処理エラーを防ぐ。

Swap領域を用意する

まずは、EC2にログインしてホームディレクトリに移動。

ターミナル(EC2サーバ)
1
2
#ホームディレクトリに移動
[ec2-user@ip-172-31-25-189 ~]$ cd

続いて、以下の順番でコマンドを実行。

ターミナル(EC2サーバ)
1
2
3
4
5
6
#処理に時間がかかる可能性があるコマンドです
[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サーバ)
1
[ec2-user@ip-172-31-25-189 ~]$ sudo chmod 600 /swapfile1
ターミナル(EC2サーバ)
1
2
3
4
[ec2-user@ip-172-31-25-189 ~]$ sudo mkswap /swapfile1
# 以下のように表示されれば成功
スワップ空間バージョン1を設定します、サイズ = 524284 KiB
ラベルはありません, UUID=74a961ba-7a33-4c18-b1cd-9779bcda8ab1
ターミナル(EC2サーバ)
1
[ec2-user@ip-172-31-25-189 ~]$ sudo swapon /swapfile1
ターミナル(EC2サーバ)
1
2
[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サーバ)
1
2
3
[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が使われていたのか確認。

ターミナル(ローカル)
1
2
3
4
#ChatSpaceのディレクトリで以下を実行
$ bundler -v
Bundler version 2.0.1
# 開発環境によってバージョンは異なります。

開発環境で仕様しているbundlerのバージョンがわかったので、同じバージョンのものをEC2サーバ側にも導入。上記の場合では、bundler 2.0.1のバージョンを導入して bundle install を実行。

ターミナル(EC2サーバ)
1
2
3
4
[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サーバ)
1
2
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ rake secret
69619d9a75b78f2e1c87ec5e07541b42f23efeb6a54e97da3723de06fe74af29d5718adff77d2b04b2805d3a1e143fa61baacfbf4ca2c6fcc608cff8d5a28e8d

この長い英数の羅列は、この後利用するのでコピーしておく。

2. 以下の指示にしたがって、環境変数を設定。

環境変数は /etc/environment というファイルに保存することで、サーバ全体に適用される。環境変数の書き込みはvimコマンドを使用して行う。

ターミナル(EC2サーバ)
1
[ec2-user@ip-172-31-23-189 ~]$ sudo vim /etc/environment

https://tech-master.s3.amazonaws.com/uploads/curriculums//64fb579a7ae6073b36bb83836fe989d9.png
上記の様な画面になれば、iと打ち込んで入力モードに切り替えた後、下記の記述を打ち込む。=の前後にスペースは入れない。

/etc/environment
1
2
3
#前章で設定したMySQLのrootユーザーのパスワードを入力
DATABASE_PASSWORD='MySQLのrootユーザーのパスワード'
SECRET_KEY_BASE='先程コピーしたsecret_key_base'

3. 設定した環境変数を反映させるために、一度本番環境をログアウトする

書き込みができたら esc(エスケープキー)を押下後、:wqと入力して内容を保存。保存できたら環境変数を適用するために一旦ログアウトする。

ターミナル(EC2サーバ)
1
2
3
[ec2-user@ip-172-31-23-189 ~]$ exit
logout
Connection to 52.xx.xx.xx closed.

4. もう一度本番環境へログイン

exitでログアウトすると、ローカル環境となる。再度SSHし直す。

ターミナル(ローカル)
1
2
$ ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
(ダウンロードした鍵を用いて、ec2-userとしてログイン)

5. 設定した環境変数が本当に適用されているか確認

SSHし直したら、 env というコマンドと grep を組み合わせて、先程設定した環境変数が適用されているか確認。

ターミナル(EC2サーバ)
1
2
3
4
5
[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」)をクリック。

https://tech-master.s3.amazonaws.com/uploads/curriculums//ce1c0c66efdcdd34674a2e0ca64b8dc0.png

すると、インスタンスの属するセキュリティグループの設定画面に移動するので、「インバウンド」タブの中の「編集」をクリック。

https://tech-master.s3.amazonaws.com/uploads/curriculums//7e1db67348f326462c0c121046ab9cb8.png

モーダルが開くので、「ルールの追加」をクリック。

タイプを「カスタムTCPルール」、プロトコルを「TCP」、ポート範囲を「3000」、送信元を「カスタム」「0.0.0.0/0」に設定する。

「0.0.0.0」は「全てのアクセスを許可する」という意味。
https://tech-master.s3.amazonaws.com/uploads/curriculums//e142ed6d793012c40b97f6e0a4d8e4df.png

以上で、ポートの開放が完了。

この作業が終わっていないと、起動したRailsにアクセスできないので注意。

 

Railsを起動する

本番環境でRailsを起動

 unicorn_railsコマンド

-c config/unicorn.rb は設定ファイルの指定、 -E production は環境を「本番モードとして動作させる」ことを示す。
-Dは「Daemon(デーモン)」の略で、プログラムを起動させつつターミナルで別のコマンドを打てるようにするオプション。

1.ユニコーンを起動させてみる

unicorn のgemをインストールしていると unicorn_rails というコマンドが使えるようになっている。これは、ローカル環境でrails sを行うのと同じように利用する。EC2サーバにて、以下のコマンドを実行。

ターミナル(EC2サーバ)
1
2
[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サーバ)
1
2
[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サーバ)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
[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(ローカル)
1
2
3
4
5
6
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サーバ)
1
[ec2-user@ip-172-31-23-189 <リポジトリ名>] git pull origin master

5. データベースを作成しマイグレーションを実行し直す

ターミナル(EC2サーバ)
1
2
3
[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サーバ)
1
[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サーバ)
1
[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サーバ)
1
2
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ps aux | grep unicorn
...

すると、以下のようにプロセスが表示されるはず。

ターミナル(EC2サーバ)
1
2
3
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サーバ)
1
2
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ kill <確認したunicorn rails masterのPID>
...

再度、プロセスを表示させ終了できていることを確認。

ターミナル(EC2サーバ)
1
2
3
[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サーバ)
1
2
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コマンドで削除できない場合はこちらを使用する。

1
2
3
$ kill -9 [プロセスID]  

#プロセスIDはpsコマンドで検索した結果の数字に置き換えてください。上記であれば17877です。

 6. 先頭にRAILS_SERVE_STATIC_FILES=1をつけて、unicornを起動

続いて再びunicornを起動する。このとき RAILS_SERVE_STATIC_FILES=1 という指定を先頭に追加。これは、コンパイルされたアセットをRailsが見つけられるような指定になる。

ターミナル(EC2サーバ)
1
2
[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サーバ)
1
2
[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.loglessまたはcatコマンドで確認し、エラーが出ていないか確認する(下に行くほど最新のログです。時刻表記UTCであることに注意)
  • カリキュラム通りの記載ができているか
  • ローカルでの編集のpushやEC2でのgit pullを忘れていないか
  • 前カリキュラムで実施した、mysqlの起動は正しく行えているか
  • EC2サーバ側のSECRET_KEY_BASE等は正しく設定できているか
  • EC2インスタンスの再起動を行ってみる

まとめ

ついにRailsを起動することに成功!

デプロイの手順を図でまとめると、以下のようになる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//159a92e0d6b47c7b9fc67cb54c2dea2c.png