
CentOS7にnginx&puma&Postgresql&Let's Encrypt&Rails5.2環境をitamaeで作成する
先日公開したecdo、とりあえずherokuにデプロイしていたのですが、諸事情により国内VPSに引っ越しする必要がありました🚚
まあ引っ越しといってもユーザー登録受け付けてなかったし、dbの引っ越しが無いので単純にアプリケーションのデプロイ先を変えてDNS変えるだけでカンタンカンタン🙆……だったはずなのに、CentOS7とLet's Encryptという新しい要素もあってなかなか大変でした。ので、その作業を備忘録として残しておきます。
CentOS 7
まず国内のVPSで良いのが無いかな?と探してみました。第1候補はやっぱりさくらのVPSだったのですが、さくらのVPSはスナップショット機能が2018年7月26日時点で無い(それに初期費用とかスケールアップ費用が高いよ……)ので却下。他にどこかないかなーと思って探したらConoHaのVPSが良さそうだったのでConoHaにしました。
ConoHaのVPSはサーバー追加時にRails構成など作れるみたいですが、それだと勉強にならないし、そもそも何が入っているか分からないので、真っ更な状態で始めます。
建てたら簡単にrootでsshしてみて繋げるかテストしておきました。ここからitamaeでプロビジョニングしていきます。
nginx
CentOS7環境で普通にyum install -y nginx
すると1.12が入るのですが、これが動きません😭 具体的には
nginx: [emerg] module “/usr/lib64/nginx/modules/ngx_http_geoip_module.so” version 1010002 instead of 1011009 in /usr/share/nginx/modules/mod-http-geoip.conf:1
というエラーが発生します。
少し調べると1.10を使えと書いてあるのですが、yum list --showduplicates nginx
してみて1.12しか無い……。過去のrpmを探してyum install
してみても依存関係が解決しない。
えー……参ったな。と思ってふとnginxの最新版っていくつだ?と思って調べてみると、1.15が最新版のようす。
sudo vim /etc/yum.repos.d/nginx.repo
に
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1
を書き込んで、yum remove -y nginx
からのyum install -y nginx
して動かしてみると……動いた❗😭
あとは不必要なdefaultのファイルとかrailsの実行ユーザーと揃えたりなどの作業をすれば、とりあえず🆗👌なはず。
レシピはこんな具合になってます。
# webサーバー系のポートを開ける
execute "Open web server ports" do
user "root"
cmd = [
'firewall-cmd --add-port=80/tcp --permanent',
'firewall-cmd --add-port=443/tcp --permanent'
]
command cmd.join('&&')
end
execute 'firewall-cmd --reload'
service 'firewalld' do
action [:restart]
end
# CentOS7で普通にyumでnginxを入れると1.12が入るが、
# これが動かないので、最新版を入れる必要有り
template "/etc/yum.repos.d/nginx.repo"
package 'nginx'
%w(default.conf ssl.conf virtual.conf).each do |conf|
file "Delete #{conf}" do
path "/etc/nginx/conf.d/#{conf}"
action :delete
end
end
# railsの実行ユーザーと合わせないといけないので変更する
file "Edit nginx.conf" do
path "/etc/nginx/nginx.conf"
action :edit
block do |content|
content.gsub!(/^.?user .+$/, "user root;")
end
end
service 'nginx' do
action [:enable, :start]
end
Let's Encrypt
さてLet's Encryptですが、itamaeで使えそうなプラグインを試してみるけど……自分の環境で動かない。ので、2つだけなのでもうシェルスクリプトでいきました。
Let's Encryptの注意点としては申請するドメインにhttpでアクセスできる必要があります。なので先にnginxを起動させ、申請するドメインにアクセスしてnginxのデフォルト画面表示できる状態でないとダメみたいです。
package "git"
git "#{node.letsencrypt.cert_bot_path}" do
repository "https://github.com/certbot/certbot"
end
execute "#{node.letsencrypt.cert_bot_path}/certbot-auto -n certonly --webroot -w #{node.letsencrypt.webroot} -d #{node.letsencrypt.domain} -m #{node.letsencrypt.mail_address} --agree-tos --server https://acme-v02.api.letsencrypt.org/directory"
execute %(echo "00 05 01 * * root #{node.letsencrypt.cert_bot_path}/certbot-auto renew --webroot -w #{node.letsencrypt.webroot} --post-hook 'systemctl reload nginx'" > /etc/cron.d/certbot-auto)
service 'nginx' do
action [:restart]
end
node.yml
letsencrypt:
domain: YOUR_DOMAIN
mail_address: YOUR_MAIL_ADDRESS
cert_bot_path: /usr/local/certbot
webroot: /usr/share/nginx/html
Rails向けのnginx設定
無事Let's Encryptで証明書が取得できたら、Rails用のnginxコンフィグファイルを作成し、適用します。レシピとconfはこんな具合になりました。
directory YOUR_APP_PATH do
owner node[:user][:name]
group node[:user][:name]
end
template "/etc/nginx/conf.d/app.conf" do
source "./templates/app.conf.erb"
variables app_path: YOUR_APP_PATH
not_if "test -e /etc/nginx/conf.d/app.conf"
end
execute "openssl dhparam 2048 -out /etc/nginx/ssl/dhparam.pem"
execute 'npm install -g yarn'
file "/home/#{node['user']['name']}/.bashrc" do
action :edit
not_if "test $RAILS_MASTER_KEY"
block do |content|
content << %(export RAILS_MASTER_KEY="#{ENV["RAILS_MASTER_KEY"]}"\n)
end
end
nodeの所は各自で置き換えてくださいませorz
app.conf.erb
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream <%= node.app.name %> {
server unix://<%= @app_path %>/shared/tmp/sockets/puma.sock;
}
server {
listen 80;
listen [::]:80;
return 301 https://$host$request_uri;
}
server{
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name <%= node.app.domain %>;
root <%= @app_path %>/current/public;
gzip on;
gzip_types text/css application/javascript application/json application/font-woff application/font-tff image/gif image/png image/jpeg;
gzip_min_length 1000;
gzip_vary on;
gzip_proxied any;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate /etc/letsencrypt/live/<%= node.app.domain %>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<%= node.app.domain %>/privkey.pem;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!aNULL!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;';
client_max_body_size 64M;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/<%= node.app.domain %>/fullchain.pem;
resolver 8.8.8.8;
location / {
gzip_static on;
proxy_pass http://<%= node.app.name %>;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~* ^/assets|iamges|favicon|packs/ {
gzip_static on;
# Per RFC2616 - 1 year maximum expiry
expires 1y;
add_header Cache-Control public;
# Some browsers still send conditional-GET requests if there's a
# Last-Modified header or an ETag header even if they haven't
# reached the expiry date sent in the Expires header.
add_header Last-Modified "";
add_header ETag "";
break;
}
}
いくつか注意点
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
という記述をしたので、opensslで生成してあげる必要があります。
execute "openssl dhparam 2048 -out /etc/nginx/ssl/dhparam.pem"
RAILS_MASTER_KEYを登録
今回はRails5.2なので、シークレット系は基本的に全てcredentials.yml.encで管理するようにしました。なので、環境変数RAILS_MASTER_KEYを設定しないといけないのですが、以下みたいな書き方が一番安全かつ分かりやすかったです。
file "/home/#{node['user']['name']}/.bashrc" do
action :edit
not_if "test $RAILS_MASTER_KEY"
block do |content|
content << %(export RAILS_MASTER_KEY="#{ENV["RAILS_MASTER_KEY"]}"\n)
end
end
VueJS(というかwebpack)用にlocationにpacksを追加
今回はVue.js(Webpack)を使っているので、public/にpacksが生成されます。なので、locationにpacksを追加してあげました。
SSLテストの結果はA+!! 次はcapistranoでデプロイを書きます
とりあえずサーバーを作る時、辛かった部分は書いてみました。いちおうSSL Server TestではA+の結果がでたので、多分大丈夫かなと。
SSL Server Test: ecdoapp.com (Powered by Qualys SSL Labs)
次はcapistranoでデプロイする時の記事を書きたいと思います。