DockerでNginx、Puma(Rails7.0)の環境構築

前回の記事からの続きになります。

itoka.hatenadiary.com

環境

作業ディレクトリ作成

前回に引き続き、/dockerの下に/nginxを作成します

app
└─ docker
    ├── nginx
    │   ├── Dockerfile
    │   └── nginx.conf
    └── web
        ├── Dockerfile
        ├── entrypoint.sh
        └── start-server.sh
# ディレクトリの作成
$ mkdir -p /app/docker/nginx
$ cd /app/docker/nginx

Dockerfile

今回はベースイメージにnginx:latestを使用します。

デフォルトで用意される80番ポートをlistenするserverコンテキストを持ったdefault.confファイルは不要なので、RUN rm -f /etc/nginx/conf.d/*で削除します。

また、CMD /usr/sbin/nginxでnginxを起動するようにしますが、デフォルトではデーモンで起動してしまい、フォアグラウンドで起動しないとコンテナが止まってしまうようです。

If you add a custom CMD in the Dockerfile, be sure to include -g daemon off; in the CMD in order for nginx to stay in the foreground, so that Docker can track the process properly (otherwise your container will stop immediately after starting)! https://hub.docker.com/_/nginx

-g 'daemon off;' でフォアグラウンド化して起動します。

$ vim Dockerfile
FROM nginx:latest

# デフォルトで用意されている個別設定ファイルを削除
RUN rm -f /etc/nginx/conf.d/*

COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf

# Nginxをforeground起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

参考:/etc/nginx/conf.d/default.confRUN rm -f /etc/nginx/conf.d/* で削除する)

server {
    listen       80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

docker-compose.yml

nginxを追加します。volumesはホストのapp/docker/nginx/nginx.conf/etc/nginx/nginx.confにマウントします。

# appディレクトリにて
$ vim docker-compose.yml
version: '3'

services:
  web:
    build:
      context: .
      dockerfile: ./docker/web/Dockerfile
    volumes:
      - .:/app
    ports:
      - '3000:3000'
    depends_on:
      - db
    stdin_open: true
    tty: true
    environment:
      DB_ROOT_USERNAME: root
      DB_USERNAME: development
      DB_PASSWORD: password
      TZ: Asia/Tokyo
      DB_HOST: db

  db:
    platform: linux/x86_64
    image: mysql:8.0
    environment:
      - MYSQL_DATABASE=development
      - MYSQL_USER=development
      - MYSQL_PASSWORD=password
      - MYSQL_ROOT_PASSWORD=passwordpassword
      - TZ=Asia/Tokyo
    ports:
      - '3306:3306'
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./volumes/mysql/data/:/var/lib/mysql

  nginx: # 追加
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf # ここ
    ports:
      - "80:80"
    depends_on:
      - web

nginx.conf

デフォルトで用意されているnginx.confをベースに、ほかは何もいじらず、必要部分のみを付け足してみます。

railsアプリのリバースプロキシとして、proxy_passhttp://unix:///app/tmp/sockets/puma.sock;を指定し、UNIXドメインソケット通信でpumaに向けます。

app/docker/nginx/nginx.conf

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 {
      # クライアントのリクエスト情報をpumaに伝える
      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;
    }
  }
}

puma.rb

puma側ではPORTを使用せず、ソケットファイルをbindしてソケット通信する設定を記載します。

app/config/puma.rb

# port ENV.fetch("PORT") { 3000 }
bind "unix://#{Rails.root}/tmp/sockets/puma.sock"

Dockerfile

rails用のDockerfileについても、PORTを空ける必要がなくなったため削除します。

app/docker/web/Dockerfile

RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# EXPOSE 3000

# Start the main process.
CMD ["sh", "./docker/web/start-server.sh"]

start-server.sh

TCP通信の必要がなくなったため、rails serverコマンドからポートとバインドの設定を削除します。

app/docker/web/start-server.sh

#!/bin/sh
echo "run start-server.sh" &
rails server

# rails server -p 3000 -b 0.0.0.0

docker-compose.yml

変更点は3箇所です。

  1. ポートを使用しないので、webコンテナのports設定は削除
  2. UNIXドメインソケット通信によるコンテナ間通信となるため、web・nginxコンテナで名前付きvolumeをマウントしてファイルを共有
  3. nginx.confでアプリケーションルートを指定する(rootディレクティブ)ため、2と同様にweb・nginxコンテナで名前付きvolumeをマウントしてファイルを共有

app/docker-compose.yml

version: '3'

services:
  web:
    build:
      context: .
      dockerfile: ./docker/web/Dockerfile
    volumes:
      - .:/app
      - public:/app/public # 追加
      - tmp:/app/tmp # 追加
#   ports:
#     - '3000:3000'
    depends_on:
      - db
    stdin_open: true
    tty: true
    environment:
      DB_ROOT_USERNAME: root
      DB_USERNAME: development
      DB_PASSWORD: password
      TZ: Asia/Tokyo
      DB_HOST: db

  db:
    platform: linux/x86_64
    image: mysql:8.0
    environment:
      - MYSQL_DATABASE=development
      - MYSQL_USER=development
      - MYSQL_PASSWORD=password
      - MYSQL_ROOT_PASSWORD=passwordpassword
      - TZ=Asia/Tokyo
    ports:
      - '3306:3306'
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./volumes/mysql/data/:/var/lib/mysql

nginx:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - public:/app/public # 追加
      - tmp:/app/tmp # 追加
    ports:
      - "80:80"
    depends_on:
      - web

volumes: # 追加
  tmp: # 追加
  public: # 追加

動作確認

ここまできたら再ビルドして立ち上げます。

$ docker-compose build --no-cache # 一応--no-cache
$ docker-compose up -d

localhostにアクセスすると、nginx経由でrailsの画面が見えるかと思います🎉

f:id:itoka_pi:20220304002836p:plain

終わりに

Dockerの環境構築は久しぶりにやったのですが、昔より構築スピードが上がっていて成長を感じました。 nginxの実務ではSSRなNuxtに通信を向けてみたり、CSPやBasic認証の運用をしたり、IPアドレス直打ちの対策等をした程度でしたが、コンテナではログ・ssl・キャッシュ・ロードバランス辺りを試してみたいなと思ってます。

あとは今後Ansible辺りも触ってみたい。terraformも勉強したので、Ansibleと組み合わせて構築を自動化してみたい。

・・目指すところはDevOps・・?

参考記事

nginxをdockerで動かす時のTips 3選 - インフラエンジニアway - Powered by HEARTBEATS

Docker + Rails + Puma + Nginx + MySQL - Qiita

はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL] - Qiita