デプロイ方法③(Github/Unicorn)


※デプロイの方法の手順③ 今後の自分のメモ用に

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

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

GithubにSSH鍵を登録

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

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

[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              |
+-----------------+

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

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

[ec2-user@ip-172-31-23-189 ~]$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2E......

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

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

[ec2-user@ip-172-31-23-189 ~]$ ssh -T [email protected]
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コマンドを打った時の様子です。

【例】ローカルでアプリケーションサーバを起動

#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にアクセスする」ということを行なっていたのです。

①rails sコマンドでPuma(appサーバ)を起動
②ブラウザからlocalhostにアクセス

アプリケーションサーバが動いていれば、ブラウザからのリクエストを受け付けてRailsアプリケーションが動作します。
という訳で、全世界に公開するEC2サーバ上でもアプリケーションサーバを動かす必要があるのです。

Unicorn

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

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

①unicorn_railsコマンドでUnicorn(appサーバ)を起動
②ブラウザからドメインにアクセス

プロセスについて

プロセス

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

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

プロセスを確認してみる

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

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

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

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

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

Unicornをインストール

UnicornはRubyで作成されており、gem化されています。

  1. 次のように ローカルでGemfile を編集しましょう。

・Gemfile

group :production do
  gem 'unicorn', '5.4.1'
end
  1. ローカルのChatSpaceディレクトリでbundle installコマンドを実行してください。

・ターミナル(ローカル)

$ bundle install

group :production do ~ end 

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

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

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

この後すぐ、ファイルの中身がそれぞれ何を行なっているか説明しますので、まずはコピー&ペーストで作成したファイルに貼り付けましょう。

・config/unicorn.rb

#サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく
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と増やすことでアプリケーションからのレスポンスを早くすることができます。

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

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

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

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

Uglifierについての記述をコメントアウトしましょう

・config/environments/production.rb(修正前)

config.assets.js_compressor = :uglifier

・config/environments/production.rb(修正後)

# config.assets.js_compressor = :uglifier

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

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

ここまで、ローカルのフォルダ内で変更修正を行ったので、こちらをリモートリポジトリへ反映します。

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

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

Githubからコードをクローンしよう

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

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

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

・ターミナル(EC2サーバ)

#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」を取得します。

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

・ターミナル(EC2サーバ)

[ec2-user@ip-172-31-23-189 ~]$ cd /var/www/
[ec2-user@ip-172-31-23-189 www]$ git clone https://github.com/<ユーザー名>/<リポジトリ名>.git

上記の入力でエラーがでなければ、先に進みましょう。