Apacheをやめてnginxにする

Conoha VPSにUbuntu 22.04をインストールして、ApacheにPassengerというモジュールを組み込んでSinatraで書いたアプリケーションを動かしてました。PHPみたいにサーバーへファイルを置くだけで動くのは手軽で便利だったのだけど、勉強としていろいろ試してみようということでnginx + Pumaの構成へ変更してみます。

Apacheをアンインストール

$ sudo systemctl stop apache2
$ sudo apt-get remove apache2

$ apache2ctl -v で、コマンドがありませんと返されたらOK 🎉

設定ファイルはあとで見たくなるかも… と思って、ひとまずそのままにしておきます。
消したいときは $ sudo apt-get purge apache2 で消せるとのこと。

いったんポートも閉じておきます。

$ sudo ufw delete allow "Apache"
$ sudo ufw delete allow "Apache Secure"

$ sudo ufw status でルールが消えていることも確認。

nginxをインストール

$ sudo apt-get update
$ sudo apt install nginx

$ nginx -v で、バージョン情報が返されたらOK 🎉
OSを起動したらnginxも起動するように設定しておく。

$ sudo systemctl enable nginx

ポートを開きます。

$ sudo ufw allow "Nginx Full"

$ sudo ufw status でルールが増えていることを確認。

ここでブラウザで http://{サーバーのIPアドレス} へアクセスしてみると、nginxのデフォルトのHTMLファイルが表示されました 🎉

nginxの設定ファイルを作成

サーバーのIPアドレスではなくドメインで表示できるようにしたいし、バーチャルホストを設定したい。というわけで、nginxの設定ファイルを作成します。

設定ファイルには /etc/nginx/sites-available/etc/nginx/sites-enabled ってディレクトリがあって、 sites-available に書いた設定ファイルのうち有効にしたいやつを sites-enabled からシンボリックリンクを貼る… という運用だと理解しました(合ってますか?)。まずは sites-available にファイルを作成します。

$ sudo vim /etc/nginx/sites-available/shikakun.dev
server {
  listen 80;
  listen [::]:80;

  root /var/www/shikakun.dev;
  index index.html index.htm index.nginx-debian.html;

  server_name shikakun.dev;

  location / {
    try_files $uri $uri/ =404;
  }
}

作成したファイルへ sites-enabled から sites-available へシンボリックリンクを貼る。

$ sudo ln -s /etc/nginx/sites-available/shikakun.dev /etc/nginx/sites-enabled/shikakun.dev

サーバー名を追加することで「ハッシュバケットメモリの問題」なるものが発生する可能性があるとのことで、よく理解していないのですが /etc/nginx/nginx.conf に書かれている server_names_hash_bucket_size の設定をコメントアウトを外して保存。
参考: Ubuntu 20.04にNginxをインストールする方法 | DigitalOcean

$ sudo vim /etc/nginx/nginx.conf
- #server_names_hash_bucket_size 64;
+ server_names_hash_bucket_size 64;

ここまででnginxの設定は完了。念のため $ sudo nginx -t を実行すると、設定ファイルのシンタックスに問題がないか確認できて便利。
nginxを再起動!

$ sudo systemctl restart nginx

/var/www/shikakun.dev/index.html に適当な内容のHTMLファイルを置いておくと、ブラウザから http://shikakun.dev/ でアクセスできるようになりました 🎉
ちなみにChromeは .dev のトップレベルドメインのサイトは自動でhttpsプロトコルでアクセスする仕様になっているそうで、Firefoxで確認しました。

常時SSL化

Let's Encryptで取得したワイルドカードのSSL証明書をnginxで使います。SSL証明書を取得した手順は以下の記事に書いたので省略します。

さきほど作成したnginxの設定ファイルを開いて、ポート番号の80へアクセスしたら443でアクセスするようにリダイレクトを設定し、443でアクセスされたらSSL証明書を読み込むように設定。

$ sudo vim /etc/nginx/sites-available/shikakun.dev
server {
  listen 80;
  listen [::]:80;

  server_name shikakun.dev;

  location / {
    return 301 https://$server_name$request_uri;
  }
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name shikakun.dev;

  root /var/www/shikakun.dev;
  index index.html index.htm index.nginx-debian.html;

  ssl_certificate /etc/letsencrypt/live/shikakun.dev/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/shikakun.dev/privkey.pem;
  ssl_session_tickets on;
  ssl_protocols TLSv1.2;
  ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  location / {
    try_files $uri $uri/ =404;
  }
}

$ sudo systemctl restart nginx でnginxを再起動すると、ブラウザから https://shikakun.dev/ でアクセスできるようになりました 🎉

SinatraアプリをPumaで動かす

Pumaで起動できるようにSinatraアプリのコードに手を加えます。
GemfileにPumaを追記。

gem "sinatra"
gem "puma"

app.rb にpumaで起動するように設定。

set :server, :puma

Sinatraアプリのルートディレクトリに、以下のような内容で puma.rb ってファイルを追加。
なにもわからないので Gistで @ctalkington さんが共有されていたコード をそのまま使用させていただきました。ありがとうございます。自分のアプリや環境に応じてチューニングしていきたいです。

root = "#{Dir.getwd}"

bind "unix://#{root}/tmp/puma/socket"
pidfile "#{root}/tmp/puma/pid"
state_path "#{root}/tmp/puma/state"
rackup "#{root}/config.ru"

threads 4, 8

activate_control_app

手を加えたSinatraアプリのコードをVPSへアップロードしたあと、アプリのルートディレクトリへ移動してGemをインストール。

$ cd /var/www/example-sinatra-app.shikakun.dev
$ bundle config set path "vendor/bundle"
$ bundle install

pumaを実行した際に作成されるファイルを置くディレクトリを用意しておく。

$ mkdir -p tmp/puma

ここからnginxの設定をします!
まずは、ここまでに作成した設定ファイルをコピーして、Sinatraアプリの設定ファイルを用意。

$ cd /etc/nginx/sites-available
$ sudo cp shikakun.dev example-sinatra-app.shikakun.dev
$ sudo vim example-sinatra-app.shikakun.dev
upstream app {
  server unix:///var/www/example-sinatra-app.shikakun.dev/tmp/puma/socket;
}

server {
  listen 80;
  listen [::]:80;

  server_name example-sinatra-app.shikakun.dev;

  location / {
    return 301 https://$server_name$request_uri;
  }
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name example-sinatra-app.shikakun.dev;

  root /var/www/example-sinatra-app.shikakun.dev/public;

  access_log /var/www/example-sinatra-app.shikakun.dev/log/nginx.access.log;
  error_log /var/www/example-sinatra-app.shikakun.dev/log/nginx.error.log info;

  ssl_certificate /etc/letsencrypt/live/shikakun.dev/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/shikakun.dev/privkey.pem;
  ssl_session_tickets on;
  ssl_protocols TLSv1.2;
  ssl_ciphers AESGCM:HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  location / {
    try_files $uri @puma;
  }

  location @puma {
    include proxy_params;

    proxy_pass http://app;
  }
}

sites-enabledへ設定ファイルへのシンボリックリンクを貼って、nginxを再起動。

$ sudo ln -s /etc/nginx/sites-available/example-sinatra-app.shikakun.dev /etc/nginx/sites-enabled/example-sinatra-app.shikakun.dev
$ sudo systemctl restart nginx

Sinatraアプリのルートディレクトリへ移動してpumaを起動。

$ cd /var/www/example-sinatra-app.shikakun.dev
$ bundle exec puma --config puma.rb

https://example-sinatra-app.shikakun.dev でSinatraアプリが動きました 🎉

Pumaの起動をデーモン化

わ〜い!と無事に動いて喜んだものの、このままだとSSH接続を切るとSinatraアプリが終了してしまうので、デーモン化しておきます。以前は puma.rbdaemonize って書けばデーモン化できたようなのだけど、最近のバージョンではデーモン化はOSに任せるべきという方針から廃止されたらしい。なるほど。ということで、systemdでデーモン化することに。

PumaのGitHubのリポジトリにある設定例 を参考に、以下のように書いてみました。

$ sudo vim /etc/systemd/system/example-sinatra-app.shikakun.dev.service
[Unit]
Description=Puma HTTP Server for example-sinatra-app.shikakun.dev
After=network.target

[Service]
Type=simple
WorkingDirectory=/var/www/example-sinatra-app.shikakun.dev
ExecStart=/home/shikakun/.rbenv/shims/bundle exec puma --config /var/www/example-sinatra-app.shikakun.dev/puma.rb
Restart=always

[Install]
WantedBy=multi-user.target

保存したら、以下のコマンドを実行して有効化。

sudo systemctl daemon-reload
sudo systemctl enable example-sinatra-app.shikakun.dev.service
sudo systemctl start example-sinatra-app.shikakun.dev.service
sudo systemctl status example-sinatra-app.shikakun.dev.service

SSH接続を切っても、サーバーを再起動しても、 https://example-sinatra-app.shikakun.dev でSinatraアプリが動きつづけるようになりました 🎉