はじめに
プライベートでサービスを立ち上げるときには、今まではいつもherokuを使っていました。herokuはすべてがサービス化されていてすごい楽なんですけど、反面、コスパが悪くて、ちゃんとプライベートのサービスやるには向かねーなあって思ってました。
そんなわけで重い腰を上げて今回からは自分でVPSを使って環境構築してみることにしました。自分はサーバ設定まわりがとても苦手で、かつ、まとまった情報もあまりネットになく、けっこう苦労したので、普段qiitaとか書かないのですが、せっかくなのでまとめておきます。
CentOS7 + Ruby on Rails 5 + Pumaで動作。Capistranoでのデプロイができるというところまで。
おおまかな流れ
- Railsを動作させる@ローカルMac
- プロジェクト作成
- MySQLユーザ作成
- git, github初期設定
- VPS初期設定
- ユーザ作成
- firewall設定
- swap領域作成
- MySQLインストール
- Rubyインストール
- rbenv
- ruby
- bundler
- Nginxインストール
- Capistranoでデプロイ
Railsを動作させる@ローカルMac
まずはローカルでrailsプロジェクトが動くようにします。最終的にローカルからCapistranoでdeployを行います。RubyやMySQLなどはインストールされているものとします。
プロジェクト作成
$ rails new アプリ名 -d mysql
MySQLユーザの作成
$ mysql -u root
> CREATE USER 'DBユーザ名'@'localhost' IDENTIFIED BY 'パスワード';
> GRANT ALL PRIVILEGES ON DB名.* TO 'DBユーザ名'@'localhost';
> FLUSH PRIVILEGES;
> exit;
DBユーザ名、パスワード、DB名はあとでdatabase.ymlに書く必要があるのでメモっておく。
git, githubの設定
gitを初期し、githubへアップします。
$ cd アプリ名
$ git init
.gitignoreを編集して不要なファイルがgit管理されないようにしましょう。
/.bundle
/vendor/bundler/
/log/*.log
/tmp
/uploads/tmp/
/config/settings.local.yml
/config/settings/*.local.yml
/config/environments/*.local.yml
/config/secrets.yml
/config/database.yml
.pryrc
.DS_Store
コミットしてプッシュします
$ git add .
$ git commit -m "first commit"
$ git remote add origin git@github.com:eiei19/アプリ名とか.git
$ git push -u origin master
database.yml と sercrets.ymlの編集
こんな感じになるはずです。
default: &default
adapter: mysql2
encoding: utf8
pool: 5
host: 127.0.0.1
development:
<<: *default
database: DB名
username: DBユーザ名
password: DBパスワード
test:
<<: *default
database: dos_test
production:
<<: *default
database: DB名(production)
username: DBユーザ名(production)
password: DBパスワード(production)
development:
secret_key_base: xxx
test:
secret_key_base: xxx
production:
secret_key_base: xxx
Production環境のDBパスワードなどは、あとでVPS側で設定するときに決めます。
railsを動かす
DBの作成と起動
$ bin/rake db:create
$ bin/rails server
http://localhost:3000 にアクセスしてRailsのWelcome画面が表示されればOK。
適当にコントローラーとか作る
class PostsController < ApplicationController
def index
end
end
<h1>Hello World</h1>
Rails.application.routes.draw do
get '/posts', to: 'posts#index'
end
http://localhost:3000/posts にアクセスしてHello Worldが表示されればOK。commit&pushしておきましょう。
VPS初期設定
今回はvultrというVPSサービスを使ってみました。よくわかってないですが、コンパネから一発でVPSを立ち上げることができて、snapshotとかもとれて、Tokyoリージョンもあります。ミニマムで$5/monthからと値段もお手頃。
SSHの設定
サーバをCentOS7で立ち上げます。まずはssh周りを設定します。
$ root@IPアドレス
-- ここからVPS --
# adduser ei
# passwd ei
# gpasswd -a ei wheel
# mkdir /home/ei/.ssh
# vi /home/ei/.ssh/authorized_keys
> 公開鍵を貼り付ける
# chmod 700 /home/ei/.ssh/authorized_keys
# chown -R ei:ei /home/ei/.ssh/
-- ローカルで違うターミナルを開く --
$ ssh ei@IPアドレス #接続できることを確認する
-- ここからVPS --
$ sudo vi /etc/ssh/sshd_config
> 以下を変更
PermitRootLogin no
PasswordAuthentication no
$ sudo systemctl reload sshd
これでrootログインとパスワードログインができなくなった。
Timezone設定
$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
$ date
Firewall設定
次にiptablesによるFirewallの設定をします。
$ sudo yum remove -y firewalld
$ sudo yum install -y iptables-services
$ sudo systemctl start iptables
$ sudo systemctl enable iptables
$ sudo /usr/libexec/iptables/iptables.init save
$ sudo vi /etc/sysconfig/iptables
> こんな感じにする。Web用に80と433をあける。
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [48:7196]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
$ sudo systemctl reload iptables
swap領域の確保
初期設定だとswap領域が確保されていないので、メモリを使いすぎると、使いすぎてるプロセスがキルされます。それだと困るのでswap領域を確保しておきます。
# dd if=/dev/zero of=/swapfile bs=1M count=1024
# mkswap /swapfile
# swapon /swapfile
# vi /etc/fstab
> 以下追記
/swapfile swap swap defaults 0 0
ここまでできたら自動マウントされるか試すためにサーバを再起動する。topコマンドswap領域が確保されてればOK。
MySQLインストール
つぎはミドルウェアとかを入れていきます。まずはMySQLから。
インストール
# yum -y remove mariadb-libs
# rm -rf /var/lib/mysql/
# yum -y localinstall http://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm
# yum -y install mysql-community-server mysql-devel
# systemctl enable mysqld.service
# systemctl start mysqld.service
# cat /var/log/mysqld.log | grep "A temporary password"
> 表示されるパスワードを使ってインストールする
# mysql_secure_installation
> 新しいパスワードを決める。質問は全部YESでOK.
# vi /etc/my.cnf
> 以下追記
character-set-server = utf8
default_password_lifetime = 0
ユーザ作成
# mysql -u root -p
> CREATE USER 'DBユーザ名'@'localhost' IDENTIFIED BY 'パスワード';
> GRANT ALL PRIVILEGES ON DB名.* TO 'DBユーザ名'@'localhost';
> FLUSH PRIVILEGES;
> exit;
Rubyインストール
Rubyをインストールします。今回はrbenvを使います。
# yum -y install git gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel bzip2
# cd /usr/local
# git clone git://github.com/sstephenson/rbenv.git rbenv
# git clone git://github.com/sstephenson/ruby-build.git rbenv/plugins/ruby-build
# vi /etc/profile.d/rbenv.sh
> 以下を記述
export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init --no-rehash -)"
# source /etc/profile.d/rbenv.sh
# rbenv install 2.3.0
# rbenv global 2.3.0
# rbenv rehash
# gem install bundler --no-ri --no-rdoc
最後にnodejsをいれておく。railsで必要。
# yum -y install nodejs
Nginxインストール
# vi /etc/yum.repos.d/nginx.repo
> 以下記載
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1
# yum -y update
# yum -y install nginx
# systemctl start nginx
# systemctl enable nginx
Capistranoでデプロイ
デプロイ準備
デプロイ用のディレクトリとユーザを作成します。
# mkdir /var/www
# adduser www
# chown -R www:www /var/www
# chmod -R 770 /var/www/
# adduser deploy
# gpasswd -a deploy www
# gpasswd -a nginx www
# mkdir /home/deploy/.ssh
# vi /home/deploy/.ssh/authorized_keys
> デプロイ用ユーザの公開鍵を貼り付ける。最初のユーザとは変えておく。macでは、ssh-keygen -t rsa でキーペアを作れる。
# chown -R deploy:deploy /home/deploy/.ssh
# chmod -R 700 /home/deploy/.ssh
$ ssh -i ~/.ssh/さっき貼り付けた公開鍵とペアの秘密鍵 deploy@IPアドレス
> ログインできるか確認する
Capistrano設定
あともう一息です。ローカルのRailsプロジェクトに戻ります。gemファイルに追記。
group :development do
gem 'capistrano', require: false
gem 'capistrano-rails', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano3-puma', require: false
gem 'capistrano-rbenv', require: false
end
$ bundle install --path vendor/bundle
$ bundle exec cap install
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rails'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/puma'
server 'IPアドレス', port: 22, roles: [:app, :web, :db], primary: true
set :repo_url, 'git@github.com:eiei19/アプリ名とか.git'
set :application, 'アプリ名'
set :user, 'deploy'
set :ssh_options, {
forward_agent: true,
user: fetch(:user),
keys: %w(~/.ssh/deployユーザの秘密鍵)
}
set :puma_threads, [4, 16]
set :puma_workers, 0
set :pty, true
set :use_sudo, false
set :stage, :production
set :deploy_via, :remote_cache
set :deploy_to, "/var/www/#{fetch(:application)}"
set :puma_bind, "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state, "#{shared_path}/tmp/pids/puma.state"
set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{release_path}/log/puma.access.log"
set :puma_error_log, "#{release_path}/log/puma.error.log"
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :rbenv_type, :system
set :rbenv_ruby, '2.3.0'
set :linked_dirs, fetch(:linked_dirs, []).push(
'log',
'tmp/pids',
'tmp/cache',
'tmp/sockets',
'vendor/bundle',
'public/system',
'public/uploads'
)
set :linked_files, fetch(:linked_files, []).push(
'config/database.yml',
'config/secrets.yml'
)
namespace :puma do
desc 'Create Directories for Puma Pids and Socket'
task :make_dirs do
on roles(:app) do
execute "mkdir #{shared_path}/tmp/sockets -p"
execute "mkdir #{shared_path}/tmp/pids -p"
end
end
before :start, :make_dirs
end
namespace :deploy do
desc "Make sure local git is in sync with remote."
task :check_revision do
on roles(:app) do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
puts "WARNING: HEAD is not the same as origin/master"
puts "Run `git push` to sync changes."
exit
end
end
end
desc 'Initial Deploy'
task :initial do
on roles(:app) do
before 'deploy:restart', 'puma:start'
invoke 'deploy'
end
end
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
invoke 'puma:restart'
end
end
before :starting, :check_revision
after :finishing, :compile_assets
after :finishing, :cleanup
end
$ cap production deploy:initial
database.ymlがないとか言われるはずなので、サーバにdeployユーザでログインして配置します。ホントはここも自動化するべき。
nginx設定
最後にnginxを設定して終了。
/etc/nginx/conf.d/xxxx.conf とか置く。
upstream アプリ名 {
server unix:/var/www/アプリ名/shared/tmp/sockets/アプリ名-puma.sock fail_timeout=0;
}
server {
listen 80;
server_name サーバのIPもしくはドメイン;
root /var/www/アプリ名/current/public;
location ~ ^/assets/ {
root /var/www/アプリ名/current/public;
}
try_files $uri/index.html $uri @アプリ名;
location / {
proxy_pass http://アプリ名;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}