今月から真剣にwebサービス作りに励むため、今までおろそかにしてきたRailsの環境構築、つまりサーバの設定とかインフラチックな部分を学習し直しました。今はMacBook Air上で暫定的に動かしてますが、今後はたぶん、定番?のさくらVPS上に構築することになるでしょう。
今後の自分のためにもなると思うので、覚書を自分なりにまとめてみます。
※RubyおよびRailsのインストール、そもそもUnicornやNginxが何なのか、はさすがにちょっとググればすぐわかるレベルなので割愛します。
てかこの記事って番煎じになるんだろう
あーいやいや、この手の環境構築は正直ググればたくさん出てくるし乱立してるんだけど、設定にいくらか手を加えられているものが多数。そこで、既存の解説をもうちょっとだけ噛み砕いた内容にします。なるべく動作するのに最低限の中身にして、スケルトン的に出来たらと思います。
ちなみにこのメモについては下記を参考にしました。
いや〜それにしてもQiitaって便利だな〜
マシン、OS
MacBook Air(OS X EI Caption)
Unicorn, Nginxのインストール(割愛)
テストアプリの作成(割愛)
デフォルトの設定
一切のconfをいじらずにWEBrickのフレームワークを使ってみます。Ruby, Railsを入れたらすぐに実行できます。
起動方法
bundle exec rails server
下記URLでアクセス可能、ログが出力される。WEBrickを起動した窓はそのままログ出力窓になる。
上記画面が出ればOK。
機能が貧弱で、このまま本番環境として使うことはまずない。アクセスできるか確認したら用済み。ただ、Unicorn等を設定した後でもWEBrick自体は利用可能。
Unicornの設定
まずは設定ファイルを見てみましょう。unicorn, nginxはともに設定ファイルを書き換えて自分なりにカスタマイズしていきます。主にsocketの位置、ログファイルの位置、listenするポート番号など。
設定ファイルの位置
デフォルトでは存在しないため、下記のようにconfig/以下などにunicorn.rbのファイル名でtouchする。
touch RAILS_ROOTのパス/config/unicorn.rb
※RAILS_ROOTの部分は作成したアプリのディレクトリ直下のこと。
==>たとえばtestというテストアプリを作ったなら、test/のこと。
Unicornは1種のRackアプリケーションであるため、本来であれば中のモジュールrackupが設定ファイルconfig.ruを参照する。Railsのテストアプリを作成したのであれば、デフォルトで作成済み。
Rackが何?って人は以下参照。
でも、ここではconfig.ruの代わりに、下記に示すコマンド中の-cオプションでconfig/unicorn.rbを指定し、これを用いる。つまりconfig.rbはスルーでOK
設定例
==> Qiitaのリンクを参考に。
下記が一番簡単だと思います。
# -*- coding: utf-8 -*- rails_root = File.expand_path('../../', __FILE__) rails_env = ENV['RAILS_ENV'] || "development" worker_processes 2 working_directory rails_root listen "#{rails_root}/tmp/#{rails_env}_unicorn.sock" pid "#{rails_root}/tmp/#{rails_env}_unicorn.pid" stderr_path "#{rails_root}/log/#{rails_env}_unicorn_error.log" stdout_path "#{rails_root}/log/#{rails_env}_unicorn.log"
起動方法
bundle exec unicorn_rails -c config/unicorn.rb -p 8080 -D -E development
下記にアクセスしたとき、#{rails_root}/log/development.logにログが出て、かつWEBrick起動直後にアクセスした画面と同じ画面が出ていればOK
http://localhost:8080/
8080のポート番号は基本何でもいいですがほかとバッティングしないようにps -efしてプロセスを見た方がいい。もしバッティングするなら、既存のプロセスをkill -9するか上記のポート番号を変える
=> シェルにでも起動/停止/再起動コマンドを用意しておくと楽。
developmentをstagingとかproductionとか分けることで起動する環境を選べる。
環境ごとの設定の違いは、
RAILS_ROOTのパス/environments/環境名.rb
をいじればOK。
Nginxの設定
ここではサーバ名、リダイレクト(rewrite)設定などを行えます。
設定ファイルの位置
/usr/local/etc/nginx/nginx.conf
マシンによって/etc/以下とかあるみたいだけどこの辺は調べてないのでよくわかりませ んw
見つからないなら、それっぽい場所にfind nginx {どっかのディレクトリ} -name dする。
設定例
worker_processes 1; # nginxのworkerプロセス数 events { worker_connections 1024; # 最大同時接続数 } http { include mime.types; default_type application/octet-stream; # 2進数のMIME TYPE sendfile on; # よくわからない。。これの方が効率が良いらしいのでonにしているだけ。 keepalive_timeout 65; # リクエストが来るまでのサーバの待ち時間 upstream unicorn { server "unix:RAILS_ROOTのパス/tmp/development_unicorn.sock"; # unicorn.rbで指定したsocketを指定して、連携 } server { listen 8081; # アクセス先ポート名 server_name localhost; # サーバ名 access_log /var/log/nginx/server.log; # nginxアクセスログファイルパス error_log /var/log/nginx/server_error.log; # nginxエラーログファイルパス try_files $uri/index.html $uri @unicorn; # ファイル存在チェック root RAILS_ROOTのパス; # 作成したアプリのディレクトリ直下を指定(これがないとunicornを利用するアプリの特定ができない) location @unicorn { proxy_set_header X-Real-IP $remote_addr; # HTTPヘッダ設定 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # HTTPヘッダ設定 proxy_set_header Host $http_host; # HTTPヘッダ設定 proxy_pass http://unicorn; # nginxをリバースプロキシとして使用した場合の、プロキシ先サーバURI } } } include servers/*;
ログ出力形式はlog_formatでいじれる。
その他、下記を参考に。
注意点
- nginx設定ファイル内ではunicorn.rbで利用していたような環境変数を利用できない。envディレクティブでexportできるようだが、正直よくわからん。。利用できる変数は$から始まる一部の変数のみ。
- 設定ファイル修正後はnginx -s reloadで読み込むことで反映される
起動方法
nginx
-s reloadなど、オプションをつければほかの動作も可能。実行後は
ps -ef | grep nginx | grep -v 'grep'
でworkerプロセス、masterプロセスが生きていることをチェック。
例) 501 843 1 0 1:23AM ?? 0:00.00 nginx: master process nginx 501 844 843 0 1:23AM ?? 0:00.00 nginx: worker process
上記例では8081番ポートでlistenしているので、
http://localhost:8081/
にアクセスして下記画面が出ればOK(Nginx自体の起動確認)
=>以下を参考に。
さあ連携してみよう!
nginxを起動し、先ほどUnicornで8080番ポートを利用していたのは不要になるので、ps -efしてunicorn masterプロセスを特定して、kill -9して閉じましょう。そして-pオプションなしで再度unicornを起動。これで準備ができました。
そもそも何を以って連携できていると判断するのか。それはアクセスログで分かります。
==> unicornとnginxともにログ出力されていれば、どちらも利用されていることになります。
設定ファイルに書いたログファイルパスをtail -f して、アクセス時にログが出力されていることをチェックしよう!
画面的にはWEBrickを起動した時と同様の画面が出ていればOK。
注意点、問題解決
設定を保存してさあ実行だ!という時はまだ安心できるときではありません。syntax errorが出ていなくても実行時にうまくいかない恐れがあるため。
Permission denied ~
権限がないエラーです。ログファイル出力先をls -alしてみてください。設定ファイルで実行ユーザを定義したならそのユーザが(定義してないならその他ユーザ扱い)書き込めるように-drwxrwxrwx、これは極端すぎるけど例えばchmodでごり押しするなり、出力先を変更するなりすべきです。ただし前者はセキュリティ的に色々アレなのでナンセンス。
Unicorn起動コマンド実行時に
/RAILS_ROOTのパス/rails_vendor/bundle/ruby/2.0.0/gems/unicorn-5.1.0/lib/unicorn/configurator.rb:644:in `parse_rackup_file': rackup file (production) not readable (ArgumentError)
実行したコマンドがおかしい可能性が大。オプション忘れとか。シェルにすれば防げそう。
nginx実行時に
nginx: [emerg] bind() to 0.0.0.0:8081 failed (48: Address already in use) nginx: [emerg] bind() to 0.0.0.0:8081 failed (48: Address already in use) nginx: [emerg] bind() to 0.0.0.0:8081 failed (48: Address already in use) nginx: [emerg] bind() to 0.0.0.0:8081 failed (48: Address already in use) nginx: [emerg] bind() to 0.0.0.0:8081 failed (48: Address already in use) nginx: [emerg] still could not bind()
これはNginxにて、8081のポート番号がバッティングしているために起こります。すでに起動中に再度nginxを実行すると起こります。このエラーが出てる時はすでにnginxは立ち上がっているのでアクセスできるはずです。
ただし、Unicorn等他のアプリで8081をすでに占有していると同様の事象が起こるため、未使用ポートを割り当てること。
502 Bad Gateway
こういうのが出たら即座にログをチェック。
2016/06/11 20:21:53 [crit] 1855#0: *21 connect() to unix:#{RAILS_ROOT}/tmp/development_unicorn.sock failed (2: No such file or directory) while connecting to upstream, client: 127.0.0.1, server: localhost, request: "GET / HTTP/1.1", upstream: "http://unix:#{RAILS_ROOT}/tmp/development_unicorn.sock:/", host: "localhost:8081"
この例ではsocketファイルがないって言われています。もっと言うと、ファイルパスの中に#{RAILS_ROOT}があってこれが悪さをしている模様。先述している通り、nginx.confには環境変数は基本的に使えないのでこういう書き方をするとsyntax errorにならないまでも連携がうまくいかない。
心得
- エンジニアたるもの、どう動いてるか、また意味のわからないコードをコピペするのは学生で卒業しないといけません。=> わかるものだけ書く。
- 手元の環境は人それぞれ違うので、お手本に書いてあった設定をそのまま入れたからといって動くとは限りません。=> 動かないなら自分で修正して解決する。分かるようになるまで諦めない。お手本に書いてある設定がおかしいから、とか甘え。まるで3歳か4歳の幼児のようにこの世を自分中心 求めれば周りが右往左往して世話を焼いてくれる そんな風にry)
こういうちょっと複雑そうな設定は他人のうまく動いててカスタマイズ済みのを使えば楽勝じゃんとか思ってると、後で痛いしっぺ返しをくらうのは目に見えていますね。
途中詰まった時に自分でトラブルシューティングできないどころか、さらにデタラメにいじりすぎて起動全然うまくいかなくなっちゃったり。。そういう恥ずかしいことにならないよう、コピペしたとしてもわからない部分は調査して挙動をはっきりさせておくことが重要ですね!(過去の自分にこれを直接言いたい^^;)
あとエラーメッセージは英語だけど中身は中学生レベルだからビビらずに読んで慣れるべし!
これらをもっと簡単に、ほぼワンボタン or ワンコマンドで構築できちゃう仕組みが将来出来てくれることを願う!