LoginSignup
1
2

More than 1 year has passed since last update.

【Docker】Rails + Puma + Nginx + MySQL + React

Posted at

はじめに

 本記事は、プログラミング初学者、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
 そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
 間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

Rails + Puma + Nginx + MySQL + React

docker-compose.yml
version: '${COMPOSE_VER}'
services:
  db:
    platform: linux/x86_64
    build: ./docker/db
    command: --default-authentication-plugin=mysql_native_password && bash -c "chmod +x /docker-entrypoint-initdb.d/00_grant.sh"
    healthcheck:
      test: mysqladmin ping -h db -u$${MYSQL_USER} -p$${MYSQL_PASSWORD}
      interval: 1s
    restart: on-failure
    volumes:
      - mysql_data:/var/lib/mysql
      - type: bind
        source: ${MY_CNF_PATH}
        target: /etc/mysql/conf.d/my.cnf
      - type: bind
        source: ${INITDB_PATH}
        target: /docker-entrypoint-initdb.d
    env_file:
      - ${DB_ENV_FILE_PATH}
    ports:
      - "${DB_PORT}:3306"
  api:
    build:
      context: .
      dockerfile: ./docker/api/Dockerfile
    command: bash -c "mkdir -p ./tmp/sockets && bundle exec puma -C config/puma.rb"
    restart: on-failure
    volumes:
      - .:/myapp
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    environment:
      RAILS_ENV: ${APP_ENV}
    ports:
      - "${API_PORT}:3000"
    depends_on:
      db:
        condition: service_healthy
  web:
    build: ./docker/web
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    ports:
      - 80:80
    depends_on:
      - api
volumes:
  mysql_data:
  public-data:
    driver_opts:
      type: none
      device: ${PWD}/public
      o: bind
  tmp-data:
     driver_opts:
      type: none
      device: ${PWD}/tmp
      o: bind
.env
COMPOSE_VER = 3.9

# db-variables.env
DB_ROOT_PASSWORD = myappdbrootpassword
DEV_DATABASE_NAME = myapp_development
TEST_DATABASE_NAME = myapp_test
DB_USER_NAME = user
DB_USER_PASSWORD = userpassword

# docker-compose.yml_db
DB_IMAGE_TAG = 8.0.28
MY_CNF_PATH = ./docker/db/my.cnf
INITDB_PATH = ./docker/db/docker-entrypoint-initdb.d
DB_ENV_FILE_PATH = ./docker/db/db-variables.env
DB_PORT = 3306

# docker-compose.yml_api
APP_ENV = development
API_PORT = 3001

Rails

Dockerfile
FROM ruby:3.1.1
ENV APP_NAME=myapp
ENV USER_NAME=myuser
ENV TZ=Asia/Tokyo

WORKDIR /${APP_NAME}

COPY Gemfile /${APP_NAME}/Gemfile
COPY Gemfile.lock  /${APP_NAME}/Gemfile.lock
RUN bundle install

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

RUN adduser ${USER_NAME} && \
    chown -R ${USER_NAME} /${APP_NAME} && \
    chown -R ${USER_NAME} ${GEM_HOME}
USER ${USER_NAME}

EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
entrypoint.sh
#!/bin/bash

set -e

rm -f /myapp/tmp/pids/server.pid

exec "$@"
Gemfile
source "https://rubygems.org"
gem "rails", "~> 7.0.2", ">= 7.0.2.3"

MySQL

Dockerfile
FROM mysql:8.0.28

ENV USER_NAME=myuser
ENV TZ='Asia/Tokyo'

RUN adduser ${USER_NAME}

USER ${USER_NAME}

00_grant.sh
#!/bin/bash

mysql=( mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" )

"${mysql[@]}" <<-EOSQL
    CREATE DATABASE IF NOT EXISTS ${MYSQL_TEST_DATABASE};
    GRANT ALL ON ${MYSQL_TEST_DATABASE}.* TO '${MYSQL_USER}'@'%' ;
EOSQL
my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci

[client]
default-character-set=utf8mb4
database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV["DB_USER_NAME"] %>
  password: <%= ENV["DB_USER_PASSWORD"] %>
  host: db

development:
  <<: *default
  database: <%= ENV["DEV_DATABASE_NAME"] %>

test:
  <<: *default
  database: <%= ENV["TEST_DATABASE_NAME"] %>

production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV["MYAPP_DATABASE_PASSWORD"] %>

Nginx

Dockerfile
FROM nginx:1.21.6

RUN rm -f /etc/nginx/conf.d/*

COPY nginx.conf /etc/nginx/conf.d/myapp.conf

ENV USER_NAME=myuser
RUN adduser ${USER_NAME} && \
    chown -R ${USER_NAME} /var/cache/nginx/ && \
    chown -R ${USER_NAME} /var/run/
USER ${USER_NAME}

CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
nginx.conf

upstream myapp {
  server unix:///myapp/tmp/sockets/puma.sock;
}

server {
  listen 80;

  server_name localhost;
  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  root /myapp/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @myapp;
  keepalive_timeout 5;

  location @myapp {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://myapp;
  }
}
puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true

React

docker-compose.yml
version: '3.9'
services:
  front:
    build: .
    volumes:
      - .:/myapp/front
    environment:
      NODE_ENV: development
    command: sh -c "npm start"
    ports:
      - 3000:3000
    networks:
      - nginxrailspumamysql_default
networks:
  nginxrailspumamysql_default:
    external: true

Dockerfile
FROM node:16.14.2

ENV APP_NAME=myapp
ENV USER_NAME=myuser
ENV TZ=Asia/Tokyo

WORKDIR /${APP_NAME}/front

RUN adduser ${USER_NAME} && \
  chown -R ${USER_NAME} /${APP_NAME}
USER ${USER_NAME}

EXPOSE 3000
CMD ["npm", "start"]
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2