Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Docker ComposeでNginx Rails MySQL環境を構築してみる

More than 1 year has passed since last update.

今までも何度かdockerでrailsアプリを作成したことがあるのですが、いつもどうやるんだっけと忘れてしまうため、今回こそメモを残します。

前提

Mac上に Docker と docker-compose がインストール済という前提でのメモになります。

今回の構成

リクエストをWebサーバのNginxで受け取り、アプリケーションサーバのPumaに流す構成にします。
DBは個人的に使い慣れているMySQLを使用します。

ディレクトリ構成

~/sample_app
├── containers
│   └── nginx
│       ├── Dockerfile
│       └── nginx.conf
│   └── rails
│       ├── Dockerfile
├── docker-compose.yml
├── Gemfile
└── Gemfile.lock

ファイルの内容

nginx

containers/nginx/Dockerfile

FROM nginx:1.17.5

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

containers/nginx/nginx.conf

worker_processes  auto;
worker_rlimit_nofile 8000;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
  worker_connections  1024;
}

http {
  server_tokens    off;

  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;

  log_format ltsv 'domain:$host\t'
                  'host:$remote_addr\t'
                  'user:$remote_user\t'
                  'time:$time_local\t'
                  'method:$request_method\t'
                  'path:$request_uri\t'
                  'protocol:$server_protocol\t'
                  'status:$status\t'
                  'size:$body_bytes_sent\t'
                  'referer:$http_referer\t'
                  'agent:$http_user_agent\t'
                  'response_time:$request_time\t'
                  'cookie:$http_cookie\t'
                  'set_cookie:$sent_http_set_cookie\t'
                  'upstream_addr:$upstream_addr\t'
                  'upstream_cache_status:$upstream_cache_status\t'
                  'upstream_response_time:$upstream_response_time';

  access_log  /var/log/nginx/access.log  ltsv;

  sendfile       on;
  tcp_nopush     on;

  keepalive_timeout  10;

  client_max_body_size 10m;

  limit_conn_zone $binary_remote_addr zone=lcz:10m;
  limit_req_zone $binary_remote_addr zone=lrz:10m rate=20r/s;

  upstream app_server {
    server app:3000; # appは docker_compose.ymlのRailsのサービス名を指定
  }

  server {
    listen 80;
    server_name localhost;

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

    root /sample_app/public;

    client_max_body_size 100m;
    error_page 404             /404.html;
    error_page 505 502 503 504 /500.html;
    keepalive_timeout 5;

    location / {
      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://app_server;
    }
  }
}

rails

containers/rails/Dockerfile

FROM ruby:2.6.5
RUN apt-get update -qq && \
    apt-get install -y apt-utils \
                       build-essential \
                       libpq-dev \
                       nodejs \
                       default-mysql-client
RUN mkdir /sample_app
ENV APP_ROOT /sample_app
WORKDIR $APP_ROOT
ADD Gemfile $APP_ROOT/Gemfile
ADD Gemfile.lock $APP_ROOT/Gemfile.lock
RUN bundle install
ADD . $APP_ROOT

CMD ["bundle", "exec", "puma", "-p", "3000", "-C", "config/puma.rb"]

Gemfile

railsだけ記載したGemfileを作成します。

source 'https://rubygems.org'
gem 'rails', '5.2.3'

Gemfile.lock

空のGemfile.lockを作成します。

$ touch Gemfile.lock

docker-compose.yml

version: '3'
services:
  app:
    build:
      context: .
      dockerfile: ./containers/rails/Dockerfile
    ports:
      - '3000:3000'
    volumes:
      - .:/sample_app
    depends_on:
      - db

  db:
    image: mysql:5.7
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
    ports:
      - '3306:3306'

  nginx:
    build:
      context: .
      dockerfile: ./containers/nginx/Dockerfile
    ports:
      - '80:80'

実行

先に sample_app に移動しておきます。

$ cd sample_app

1. Railsプロジェクトを生成

$ docker-compose run app rails new . --force --database=mysql --skip-bundle

Creating network "sample_app_default" with the default driver
Pulling db (mysql:5.7)...
...省略
Successfully tagged sample_app_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
       exist
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
       force  Gemfile
         run  git init from "."
Reinitialized existing Git repository in /sample_app/.git/
      create  package.json
      create  app
      ...省略

2. イメージのビルド

$ docker-compose build

db uses an image, skipping
Building app
Step 1/10 : FROM ruby:2.6.5
 ---> d98e4013532b
...省略
Step 2/3 : RUN rm -f /etc/nginx/conf.d/*
 ---> Running in d0b3c12300b7
Removing intermediate container d0b3c12300b7
 ---> 079a82dca96a
Step 3/3 : ADD ./containers/nginx/nginx.conf /etc/nginx/nginx.conf
 ---> 92dae2e2a5af
Successfully built 92dae2e2a5af
Successfully tagged sample_app_nginx:latest

3. DB設定

DBの設定を行います。
defaultの host を localhost から docker_compose.yml で設定した MySQL の サービス名(db) に修正します。

$ vi config/database.yml

# MySQL. Versions 5.1.10 and up are supported.
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
  host: db  # docker-compose.ymlのMySQLのservice名を指定

development:
  <<: *default
  database: sample_app_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: sample_app_test

# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>
#
production:
  <<: *default
  database: sample_app_production
  username: sample_app
  password: <%= ENV['SAMPLE_APP_DATABASE_PASSWORD'] %>

設定が完了したら、DBを作成します。

$ docker-compose run app rails db:create

Creating network "sample_app_default" with the default driver
Pulling db (mysql:5.7)...
5.7: Pulling from library/mysql
...省略
Creating sample_app_db_1 ... done
Created database 'sample_app_development'
Created database 'sample_app_test'

4. コンテナの起動

$ docker-compose up

sample_app_db_1 is up-to-date
Creating sample_app_app_1   ... done
Creating sample_app_nginx_1 ... done
Attaching to sample_app_db_1, sample_app_nginx_1, sample_app_app_1
...省略
app_1    | Puma starting in single mode...
app_1    | * Version 3.12.1 (ruby 2.6.5-p114), codename: Llamas in Pajamas
app_1    | * Min threads: 5, max threads: 5
app_1    | * Environment: development
app_1    | * Listening on tcp://0.0.0.0:3000
app_1    | Use Ctrl-C to stop

http://localhost にアクセスして Yay! You're on Rails!
表示されればOK!

docker_rails_init.png

参考

Docker + Rails + Puma + Nginx + MySQL

SinceK13
RubyとNBAが好きなバラベレン民
tabelog
お店選びで失敗したくない人のためのグルメサイト「食べログ」を開発しています
https://tabelog.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away