やりたいこと
- Railsアプリをインターネットに公開したい
- AWS EC2で公開したい
- 独自ドメインで公開したい
- HTTPSで公開したい
- 単一のEC2で複数のアプリを公開したい
この記事では、独自ドメインやHTTPSにも対応した形でEC2でアプリを公開する手順を紹介する。
なお、独自ドメインの発行およびAWSの運用には料金がかかる。 筆者はだいたい月に3000円くらいかかっている。(AWS: 3000円/月くらい, ドメイン: 10円/月くらい)
この記事に書かれていることを実践し何らかの不利益を被ったとしても自己責任であり、筆者は責任を負わない旨をご了承いただきたい。
また、「EC2インスタンスを立ち上げる」「Route53でDNSレコードを登録する」などといった基本的な作業の手順はここには載せない。ググれば易しく解説してくれている記事がたくさん見つかるので、それらを参照していただきたい。
1. AWS EC2の環境を整える
まずはAWSコンソールにログインしよう。
https://ap-northeast-1.console.aws.amazon.com/console/
EC2インスタンスを1台立ち上げる。本格運用しないのであれば安いインスタンスで構わない。 この記事ではOSがUbuntuのイメージを使用したという前提で話を進める。
1.1 ユーザ作成
EC2にログインしたら、まずアプリごとにLinuxユーザを作ろう。 まず、デフォルトユーザ(ユーザ名ubuntu)でログインする。
以下のコマンドを実行してユーザを作っていく。ここではユーザ名はbanana
として話を進めていく。
ubuntuユーザとして実行
sudo useradd -m banana sudo gpasswd -a banana sudo sudo passwd banana
パスワードを聞かれれるので任意の文字列を打ち込む。これは今後、banana
ユーザのパスワードとなるので覚えておこう。
次に、ログイン時のシェルを指定する。
sudo chsh banana
シェル名を聞かれるので、/bin/bash
と打ち込もう。
1.2 sshでログインできるようにする
ubuntu
ユーザのキーをコピーして使うことにする。
sudo su banana cd /home/banana mkdir .ssh sudo cp ../ubuntu/.ssh/authorized_keys .ssh/ sudo chown banana .ssh/authorized_keys sudo chgrp banana .ssh/authorized_keys exit
これで、banana
ユーザとしてsshログインできるようになる。いったんEC2からログアウトし、banana
ユーザとして入り直そう。以下の操作は全てbanana
ユーザとして行う。
1.3 もろもろ環境設定
ここは、各自好きなように環境設定すれば良い。
## 誤ってrmコマンドで削除してしまうのを防止する echo "alias rm='rm -i'" >> ~/.bashrc exec $SHELL ## sshが切れないようにする sudo vi /etc/ssh/sshd_config ClientAliveInterval 30 # 末尾に追加 sudo /etc/init.d/ssh restart ## OS最新化 sudo apt-get update -y sudo apt-get upgrade -y ## IPv6サポートをoffにする sudo vi /etc/default/ufw IPV6=no # yesからnoに変更 ## ポート22, 80, 3000番を開けファイアウォールを有効化 sudo ufw allow 22 sudo ufw allow 80 sudo ufw allow 3000 sudo ufw enable sudo ufw status ## よく使うコマンドのインストール (自身の好みに合わせてこの辺は自由にインストールしてください) sudo apt-get install -y pwgen zip unzip nkf screen imagemagick sudo apt-get install mecab libmecab-dev mecab-ipadic # Mecab sudo apt-get install mecab-ipadic-utf8 echo "本日は晴天なり" | mecab # => 形態素解析のテスト ## 開発ツールのインストール sudo apt-get install -y build-essential automake libssl-dev libreadline-dev libyaml-dev libpq-dev libbz2-dev libsqlite3-dev sudo apt-get upgrade ## pythonのインストール git clone https://github.com/pyenv/pyenv.git ~/.pyenv echo '' >> ~/.bashrc echo '# pyenv' >> ~/.bashrc echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc echo 'eval "$(pyenv init -)"' >> ~/.bashrc exec $SHELL pyenv install 2.7.11 pyenv global 2.7.11 python --version ## rubyのインストール git clone https://github.com/rbenv/rbenv.git ~/.rbenv echo '' >> ~/.bashrc echo '# rbenv' >> ~/.bashrc echo 'export RBENV_ROOT="$HOME/.rbenv"' >> ~/.bashrc echo 'export PATH="$RBENV_ROOT/bin:$PATH"' >> ~/.bashrc echo 'eval "$(rbenv init -)"' >> ~/.bashrc exec $SHELL git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build rbenv install --list rbenv install 2.4.1 rbenv global 2.4.1 ruby --version ## nodeのインストール curl -L git.io/nodebrew | perl - setup echo '' >> ~/.bashrc echo '# nodebrew' >> ~/.bashrc echo 'export NB_ROOT="$HOME/.nodebrew/current"' >> ~/.bashrc echo 'export PATH="$NB_ROOT/bin:$PATH"' >> ~/.bashrc exec $SHELL nodebrew ls-remote mkdir .nodebrew/default/src nodebrew install-binary v8.1.2 nodebrew use v8.1.2 node --version ## 各種パッケージ最新化 pip install --upgrade pip gem update --system npm install -g npm ## よく使うgemをグローバルインストール gem install bundler gem install unicorn gem install execjs
2. データベースの設定
PostgreSQLを使用する。
2.1 PostgreSQLのインストール・設定
### (参考) https://www.postgresql.org/download/linux/ubuntu/ sudo vi /etc/apt/sources.list.d/pgdg.list deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main # この行を追加 wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo apt-get update sudo apt-get -y install postgresql-9.4 sudo apt autoremove sudo locale-gen ja_JP.UTF-8 sudo service postgresql restart ## PostgreSQLの設定 sudo vi /etc/postgresql/9.4/main/postgresql.conf # PostgreSQL設定ファイル shared_buffers = 512MB # パフォーマンス向上のためサイズ拡大(物理メモリの1/4程度が望ましい) sudo service postgresql restart
DBを作る
アプリごとにDBを作ろう。
ここでは、banana
という名前のPostgreSQLユーザ、banana_production
という名前のデータベースを例として話を進める。
パスワードはpwgen
で生成したものから適当に選んで使う。
pwgen # => Nohwee3k sudo su - postgres psql CREATE DATABASE banana_production WITH TEMPLATE template0 ENCODING = 'UTF-8' LC_COLLATE = 'ja_JP.UTF-8' LC_CTYPE = 'ja_JP.UTF-8'; CREATE USER banana WITH LOGIN PASSWORD 'Nohwee3k'; ALTER USER banana CREATEDB; GRANT ALL PRIVILEGES ON DATABASE banana_production TO banana;
3. ソースコードをEC2に持ってくる
ローカルで開発したアプリのコードをEC2上に持ってこよう。これはGitリポジトリで経由で行う。 わたしはBitBucketをよく使う。BitBucketは少人数開発であれば無料でプライベートリポジトリを複数作ることができるので便利だ。
ただ、事前にBitBucketにSSHキーを登録しておく必要があるので注意。ローカルPCとEC2の両方で、以下のようにしてキーをBitBucketに登録する必要がある。
cd ~/.ssh ssh-keygen -t rsa -C your_mail_address@example.com mv id_rsa bitbucket_rsa # わかりやすくするためリネーム mv id_rsa.pub bitbucket_rsa.pub # わかりやすくするためリネーム chmod 600 bitbucket_rsa vi ~/.ssh/config ### 以下を追記 Host bitbucket.org HostName bitbucket.org IdentityFile ~/.ssh/bitbucket_rsa User git ### ここまで cat ~/.ssh/bitbucket_rsa.pub # ここで表示される文字列をクリップボードへコピーしておく
コピーした公開鍵情報をBitBucketの「SSH鍵」に登録する。 以下のURL (2018.9.10現在)の画面から登録する。
https://bitbucket.org/account/user/YOUR_USER_NAME/ssh-keys/
SSH鍵を登録できたら、BitBucketにリポジトリを作り、ここにローカルアプリをプッシュしよう。
# ローカル等の開発環境で実行 git init git add . git commit -m "First commit" git remote add origin git@bitbucket.org:YOUR_NAME/banana_app.git git push -u origin master
EC2の/home/banana
上で、リポジトリをクローンする。
# EC2で実行 cd /home/banana git clone git@bitbucket.org:YOUR_NAME/banana_app.git
4. Production環境で動かす
本番環境でRailsアプリを動かすために、環境変数やデータベース接続の設定を行おう。
4.1 本番用GEMのインストール
unicornとpostgresqlを使うのでGemfile
に以下を追加しておくこと。
gem 'pg' gem 'unicorn'
まずgemをインストールする。
bundle install --path vendor/bundle --without development test # 本番環境依存のパッケージをローカルインストール
インストール時にこんなエラーが出ることがある
Important: You may need to add a javascript runtime to your Gemfile in order for bootstrap's LESS files to compile to CSS. ********************************************** ExecJS supports these runtimes: therubyracer - Google V8 embedded within Ruby therubyrhino - Mozilla Rhino embedded within JRuby Node.js **********************************************
対処方法は以下。
gem install execjs
4.2 データベースの接続設定
vi config/database.yml ### 以下を追記 production: adapter: postgresql encoding: unicode database: banana_production pool: 5 username: banana password: Nohwee3k min_messages: WARNING ### ここまで
ここで、database
とusername
、password
は先ほどPostgreSQLの設定で使った値と同じものを使うこと。
4.3 環境変数の設定
## 本番環境であることを表す環境変数をセットする echo "export SECRET_KEY_BASE='production'" >> ~/.bashrc rake secret echo "export SECRET_KEY_BASE='435c1b5517e10640b8d661e361cac2682c6dc0c4690ad4c952d6af84098c02538d0a5acb138e34bb313a420b2a4dd97743d182a6307cea12ace817d8db844e6d'" >> ~/.bashrc exec $SHELL RAILS_ENV=production bundle exec rake db:setup # DBをproduction環境でセットアップする ## アセットコンパイルを行う RAILS_ENV=production bundle exec rails assets:precompile ## 起動確認 bundle exec rails s -e production # ブラウザでhttp://xxx.xxx.xxx.xxx:3000を開いて表示されるか確認してみる
ここまで行えば、http://xxx.xxx.xxx.xxx:3000 という風にIPとポート番号でアクセスできるようになる。
次からは独自ドメイン、HTTPSで公開する方法を説明する。
5. Unicorn/Nginxの設定
5.1 Unicornの設定
Unicornというミドルウェアを使用する。Unicornは、次に紹介するNginxというWebサーバとRailsアプリの間を橋渡ししてくれる。
# アプリのルートディレクトリに入って実行 vi config/unicorn.rb # 新規作成 ### 以下を追加 # set lets $app_dir = "/home/banana/banana_app" # Railsアプリケーションのドキュメントルート $worker = 2 # HTTPリクエストを処理するプロセスの数. 最低でもCPUコア数以上とすること. コア数は cat /proc/cpuinfo コマンドで確認できる. $timeout = 30 $listen = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir # 任意のパスで良い. Nginxが参照可能な, UNIXドメインソケットのパス $pid = File.expand_path 'tmp/pids/unicorn.pid', $app_dir # 同じく任意のパスで良い. $std_out = File.expand_path 'log/unicorn.stdout.log', $app_dir # 任意のパス. ログの出力先 $std_err = File.expand_path 'log/unicorn.stderr.log', $app_dir # 任意のパス. エラーログの出力先 # set config working_directory $app_dir worker_processes $worker stdout_path $std_out stderr_path $std_err timeout $timeout listen $listen pid $pid # loading booster preload_app true # before starting processes before_fork do |server, worker| defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! old_pid = "#{server.config[:pid]}.oldbin" if old_pid != server.pid begin Process.kill "QUIT", File.read(old_pid).to_i rescue Errno::ENOENT, Errno::ESRCH end end end # after finishing processes after_fork do |server, worker| defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end ### ここまで
ここで、上部のこのあたりは各自の環境に合わせて変えていただきたい。その他の部分はオマジナイだと思って、そっくりそのままコピペで構わない。
$app_dir = "/home/banana/banana_app" # Railsアプリケーションのドキュメントルート $worker = 2 # HTTPリクエストを処理するプロセスの数. 最低でもCPUコア数以上とすること. コア数は cat /proc/cpuinfo コマンドで確認できる. $timeout = 30
次に、
sudo vi /etc/init.d/banana ### 以下を追加 #!/bin/sh # BEGIN INIT INFO # Provides: banana # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts banana (a rails app) # Description: starts banana (a rails app) using start-stop-daemon # END INIT INFO USER=banana APP_ROOT=/home/banana/banana_app RAILS_ENV=production PID_FILE=$APP_ROOT/tmp/pids/unicorn.pid CONFIG_FILE=$APP_ROOT/config/unicorn.rb CMD="/home/banana/.rbenv/shims/bundle exec /home/banana/.rbenv/shims/unicorn_rails" ARGS="-c $CONFIG_FILE -D -E $RAILS_ENV" export RAILS_SERVE_STATIC_FILES=1 export SECRET_KEY_BASE='435c1b5517e10640b8d661e361cac2682c6dc0c4690ad4c952d6af84098c02538d0a5acb138e34bb313a420b2a4dd97743d182a6307cea12ace817d8db844e6d'' export PATH=/home/banana/.rbenv/shims/:$PATH case $1 in start) start-stop-daemon --start --chuid $USER --chdir $APP_ROOT --exec $CMD -- $ARGS || true ;; stop) start-stop-daemon --stop --signal QUIT --pidfile $PID_FILE || true ;; restart|force-reload) start-stop-daemon --stop --signal USR2 --pidfile $PID_FILE || true ;; status) status_of_proc -p $PID_FILE "$CMD" banana && exit 0 || exit $? ;; *) echo >&2 "Usage: $0 <start|stop|restart|force-reload|status>" exit 1 ;; esac ### ここまで
ここで、banana_app
とbanana
と書かれている箇所だけ、自身の環境に合わせればOK。たとえば、mikan
というユーザでmikan_app
というアプリを作る場合は以下のように置換すれば良い。
cat | perl -pe "s/banana_app/mikan_app/g" | perl -pe "s/banana/mikan/g"
また、Railsアプリ内でENV["HOGE_HOGE"]
のように環境変数を使っている場合は、このファイルに環境変数を書いておく必要がある。たとえば、ツイッターのAPIを使っていて、ツイッターのAPI KEYとAPI SECRETを環境変数から呼び出したいときは
# Twitter export TWITTER_KEY='xxxxxxxxxxxxxxxxxxxxxxxxxxxx' export TWITTER_SECRET='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
みたいに書いておこう。
ここまで出来たら有効化する。
# 有効化 sudo chmod 755 /etc/init.d/banana sudo update-rc.d banana defaults ### bananaアプリの動作確認 sudo service banana start sudo service banana stop sudo service banana restart sudo service banana status # ※ statusがrunningになっているかどうか確認する。
5.2 Nginxの設定
Nginxがインストールされていなければ、まずインストールするところから。
### (1) nginxサイトが配布するPGPキーを追加 curl http://nginx.org/keys/nginx_signing.key | sudo apt-key add - ### (2) リポジトリを一覧に追加 sudo sh -c "echo 'deb http://nginx.org/packages/ubuntu/ xenial nginx' >> /etc/apt/sources.list" sudo sh -c "echo 'deb-src http://nginx.org/packages/ubuntu/ xenial nginx' >> /etc/apt/sources.list" ### (3) アップデート後、nginxをインストール sudo apt-get update sudo apt-get install nginx
インストールできたら、設定ファイルをいじっていく。
## バーチャルホストの設定 cd /etc/nginx sudo rm koi-utf koi-win win-utf # これらはロシア語の文字コード返還テーブル. ただ邪魔なだけなので削除しておく. sudo vi nginx.conf ### 以下のように変更 user nginx; worker_processes 1; # CPUコア数と同じ値が推奨 error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; # 同時接続数 } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; include /etc/nginx/sites-enabled/*.conf; } ### ここまで
ここで、最後の行include /etc/nginx/sites-enabled/*.conf;
が重要な役割を担っている。sites-enabled
ディレクトリの中に入っている*.conf
というコンフィグファイルがアルファベット順に実行される。
ただし、Ngingの作法としては、sites-enabled
に直接コンフィグファイルを置かず、代わりにsites-available
というディレクトリにコンフィグファイルを置くという風にされている。sites-available
内に作ったコンフィグファイルに対してシンボリックリンクを張り、そのリンクをsites-enabled
に置くというやり方をする。
sudo mkdir sites-available # コンフィグファイルの置き場を作る sudo mkdir sites-enabled # コンフィグファイルに張るシンボリックリンクを置く場所を作る sudo cp conf.d/default.conf sites-available/ # デフォルトのコンフィグファイルをコンフィグ置き場にコピーする
デフォルトのコンフィグを以下のように修正する。
vi sites-available/default.conf ### server { listen 80; server_name localhost; # 適当なランダムな文字列↓ location /_LJoilLNe5KJHIy84LL4lKJEZ { root /usr/share/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ### ここまで
ここで_LJoilLNe5KJHIy84LL4lKJEZ
というのは適当なランダムな文字列だ。これは、後ほど紹介するEC2ロードバランサのヘルスチェック用として使う。
以下のヘルスチェック用のページを用意しよう。
sudo vi /usr/share/nginx/html/_rQhFwH9PWgMXZyUtPrrfi2JQ9nVMfPUeyInYcdEZ/index.html ### 以下を追加 <!DOCTYPE html> <html> <head> <title>Check Page</title> </head> <body> Check Page </body> </html> ###
次に、banana
用のコンフィルファイルを作る。
sudo vi sites-available/banana.conf # 新しいコンフィグ新規作成 ### ここから limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m; limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=1r/s; upstream app_server { server unix:/home/banana/banana_app/tmp/sockets/.unicorn.sock; } server { listen 80; server_name example.com; # このドメインでアプリを公開する # 静的コンテンツ(public/****)の場所 root /home/banana/banana_app/public; error_log /home/banana/banana_app/log/nginx.error.log; access_log /home/banana/banana_App/log/nginx.access.log; # 接続制限関連 keepalive_timeout 5; # 接続を保つ秒数 (国内であれば5-10でOK) client_max_body_size 256k; # クライアントからのリクエストボディは256KBまで許容する. client_header_buffer_size 128k; # クライアントからのリクエストヘッダは128KBまで許容する. large_client_header_buffers 4 128k; # クライアントからのリクエストヘッダは128KBまで許容する. limit_conn conn_limit_per_ip 10; # 1つのIPに対する同時接続は10本まで許容する. limit_req zone=req_limit_per_ip burst=5; # リクエスト数/秒の制限にかかったリクエストは5件まで許容する # Railsアプリへのルーティング try_files $uri/index.html $uri.html $uri @app; location @app { # HTTP headers proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://app_server; } # Railsエラーページ error_page 500 502 503 504 /500.html; location = /500.html { root /home/banana/banana_app/public; } } ### ここまで sudo ln -s /etc/nginx/sites-available/default.conf /etc/nginx/sites-enabled/000_default.conf # シンボリックリンクを張る sudo ln -s /etc/nginx/sites-available/banana.conf /etc/nginx/sites-enabled/ # シンボリックリンクを張る
ここで、例としてexample.com
というドメインでアプリを公開するという体で話を進める。ここは、自分が使いたいドメイン名で置き換えていただきたい。
最後に、これまで作ったNginxのファイルが間違っていないかチェックする。
sudo nginx -t # コンフィグが正常かどうかテスト # syntax ok と出ればOK. # okと出ない場合は、どこかタイピングミスしていると思われる。
最後に、Nginxを有効化する。
sudo service nginx restart sudo service nginx status # ※ statusがrunningになっているかどうか確認する。
6. 独自ドメイン/HTTPSで公開
6.1 お名前.comでドメインを買う
6.2 お名前.comでネームサーバをRoute53に設定する
AWSのRoute53で、新たにHosted zoneを作り、先程購入したドメインと同じ名前のhosted zoneを作る。新しく割り当てられた4のネームサーバ (nsと書かれている)をコピーしておく。 https://console.aws.amazon.com/route53/
コピーしたネームサーバを、お名前.comのネームサーバに設定する。
https://www.onamae.com/domain/navi/ns_update/input
このページで、「他のネームサーバを利用」というタブををクリックし、先程コピーしておいた4つのNSを入力する。
6.3 AWS Certificate ManagerでSSL証明書を取得する
https://ap-northeast-1.console.aws.amazon.com/acm/home
「証明書のリクエスト」を押して、先程取得したドメイン名と同じドメインの証明書を取得する。その際、「Route53にレコード(CNAME)を追加する」という旨のボタンを押しておく。(AWSのアップデートで、ボタン名の表現は頻繁に変わるので一言一句同じボタンは無いかもしれない。ニュアンスで判断していただきたい。)
※ 10分ほど待つと有効になる。有効にならないと、次の処理が失敗するので必ず有効になっているかどうか確認すること。
6.4 クラシックロードバランサを立てる
https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home#LoadBalancers
クラシックロードバランサはドメインごとに立てる。(アプリを2つ公開したい場合は2つのドメイン、2つのロードバランサを立てる。) クラシックロードバランサを立てるとき、設定画面で「HTTPS:443をHTTP:80にリダイレクトする」ように設定する。また、SSL証明書として、上記で取得したACMの証明書を指定する。また、紐付けるインスタンスは当然、本番運用するEC2とする。
ロードバランサが出来たら、ヘルスチェックの設定を行う。
これは、定期的にロードバランサがEC2の死活をチェックするという機能だ。特定のURLでアクセスしEC2から反応があれば「生きている」と判断される。ここで、「特定のURL」というのが、先程作成した適当な名前(_LJoilLNe5KJHIy84LL4lKJEZ
とした)のサイトだ。
「ヘルスチェックの編集」画面で以下の様に設定する。
Aレコードでロードバランサのエイリアスを指定
Route53で、Aレコードを作成する。 AWSでは、Aレコードを選んだときに「エイリアス」というものを指定できる。
関連付け対象に、先程作ったロードバランサを指定する。
以上で準備は全て整った。
7. 確認
でアプリが見れるようになった。
以上、今回はRailsアプリをAWS EC2で公開する超簡単な手順 【独自ドメイン/HTTPS対応】を紹介しました。 この記事が役にたったという方は以下の「★+」ボタンのクリック、SNSでのシェア、「読者になる」ボタンのクリックをお願いいたします。 ではまた〜