こんにちは。
Nginxをローカルで動かすテストをしてみるために、RailsのPumaにsocketをbindした時に、なかなかbindできねえ〜と思っていたら、原因はforemanだったので、その解決方法をご紹介します。
Nginxとは
Nginxとは、Webサーバーの一種です。
Railsのアプリケーションサーバーを本番環境で動かす時に、Nginxと組み合わせて使うことが多いです。
なんで必要なの?と思って少し調べてみたのですが、Pumaはあくまでアプリケーションサーバーで、バッファリングの処理などが遅くなることが原因のようです。
(実際に検証してみた!みたいなことはしていないので、下記の記事をご覧いただければと思います)
Pumaとは
Pumaとは、Railsのアプリケーションサーバーの一種です。
今だとDefaultでPumaが使われています。
Railsサーバーをrails sで起動すると、Pumaが起動します。
$ rails s
=> Booting Puma # ここでPumaが起動している
=> Rails 7.0.4.3 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.5 (ruby 3.2.1-p31) ("Birdie's Version")
* Min threads: 5
* Max threads: 5
* Environment: development
* PID: 12
* Listening on http://0.0.0.0:3000
Use Ctrl-C to stop
foremanとは
foremanとは、複数のプロセスを管理するためのツールです。
bin/dev
コマンドを実行すると、foreman経由でRailsサーバーとbin/rails tailwindcss:watch
だったりが起動します。
Procfile.devに記述されたコマンドを確認してみてください。
socketをbindしたい
さて、本題です。
NginxをDockerの開発環境で動かすために、Pumaのsocketをbindしたいと思いました。
# port ENV.fetch("PORT") { 3000 } # ここをコメントアウトして、下記を追加
app_root = File.expand_path('..', __dir__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
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;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
server_name localhost;
root /app/public;
location / {
try_files $uri $uri/index.html @app;
}
location @app {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://unix:/app/tmp/sockets/puma.sock;
}
}
}
FROM nginx:latest
RUN rm -f /etc/nginx/conf.d/*
COPY ./docker/development/nginx.conf /etc/nginx/nginx.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
app: # railsのコンテナ
# 下記を追加
volumes:
- public:/app/public
- tmp:/app/tmp
nginx:
build:
context: .
dockerfile: ./docker/development/Dockerfile.nginx
volumes:
- ./docker/development/nginx.conf:/etc/nginx/nginx.conf
- public:/app/public
- tmp:/app/tmp
ports:
- "80:80"
depends_on:
- app
volumes:
public:
tmp:
こんな感じの記事が調べるとたくさん出てくると思います。
しかし、これをやっても、Pumaがsocketをbindしてくれません。
$ docker compose up
Starting rails-nginx-example_app_1 ... done
Starting rails-nginx-example_nginx_1 ... done
Attaching to rails-nginx-example_app_1, rails-nginx-example_nginx_1
app_1 | => Booting Puma
app_1 | => Rails
app_1 |
app_1 | => Ctrl-C to shutdown server
app_1 | Puma starting in single mode...
app_1 | * Puma version: 5.6.5 (ruby 3.2.1-p31) ("Birdie's Version")
app_1 | * Min threads: 5
app_1 | * Max threads: 5
app_1 | * Environment: development
app_1 | * PID: 12
app_1 | * Listening on http://127.0.0.1:3000 # 本当はこれじゃなくて、socketをbindしたい
app_1 | Use Ctrl-C to stop
解決策
bin/devではなく、bundle exec pumactl start
で実行すると、socketをbindしてくれます。
原因を調査しているとこんなpumaのissueを見つけました。
Bind is ignored when starting Rails with foreman
どうやら、foremanが自動でPORTを設定するようで、確かにPORT周りを消しても勝手に5000番になって動いていたんですよね。
ここに解決策としてあるunset PORT
を追加してみる方法については、ローカルの開発環境の影響で試す前にやめておく判断をしてしまい、とりあえずbundle exec pumactl start
でローカルでもsocket接続ができたことだけを確認して今回はよしとしました。
実際に開発環境でも常にNginxを使う方は上記のようにunset PORT
を試す価値はあるかもしれません。
今回はこのあたりで。