Docker で Rails を動かす(その3)

以前、rails と nginx を unix ドメインソケットで連携させるを書いていました。

Docker 環境でも試します。

参考

作るもの

  • Docker で Rails を動作する環境構築
  • ローカル –> Docker コンテナ(nginx–[unix ドメインソケット]–>rails) という接続の構成

環境構築

何ということではなく、ただ愚直に nodenv と rbenv そして yarn を動かせるようにセットアップをします。
ベースのイメージが AmazonLinux なのは、「まぁどうせ自分で何か作るなら EC2 でやるだろう」ぐらいの考えです。

Dockerfile

Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
FROM amazonlinux:latest

ARG NODE_V="14.10.0"
ARG RUBY_V="3.0.0"

RUN yum update -y && \
yum install -y wget which systemd-sysv crontabs && \
wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.8.11/1.fc21/x86_64/sqlite-devel-3.8.11-1.fc21.x86_64.rpm && \
wget https://kojipkgs.fedoraproject.org//packages/sqlite/3.8.11/1.fc21/x86_64/sqlite-3.8.11-1.fc21.x86_64.rpm && \
yum install -y sqlite-3.8.11-1.fc21.x86_64.rpm sqlite-devel-3.8.11-1.fc21.x86_64.rpm && \
yum install -y git tar gcc gcc-c++ make openssl-devel zlib-devel sqlite sqlite-devel bzip2 readline-devel mysql-devel && \
yum --enablerepo=epel,remi,rpmforge install -y libxml2 libxml2-devel && \
yum install -y curl libcurl libcurl-devel && \

# Node.js設定
git clone https://github.com/nodenv/nodenv.git ~/.nodenv && \
echo 'export PATH="$HOME/.nodenv/bin:$PATH"' >> ~/.bash_profile && \
source ~/.bash_profile && \
echo 'eval "$(nodenv init -)"' >> ~/.bash_profile && \
source ~/.bash_profile && \
mkdir -p "$(nodenv root)"/plugins && \
git clone https://github.com/nodenv/node-build.git "$(nodenv root)"/plugins/node-build && \
nodenv install $NODE_V && \
nodenv global $NODE_V && \
npm install yarn && \

# Ruby設定
git clone https://github.com/rbenv/rbenv.git ~/.rbenv && \
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile && \
source ~/.bash_profile && \
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile && \
source ~/.bash_profile && \
mkdir -p "$(rbenv root)"/plugins && \
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build && \
rbenv install $RUBY_V && \
rbenv global $RUBY_V && \

# Yarn設定
wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo && \
curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - && \
yum install yarn -y && \

# nginx設定
amazon-linux-extras install nginx1 && \
systemctl enable nginx.service && \
# /etc/nginx をホスト側でマウントした時に空になるので別ディレクトリでバックアップ
# entrypoint.sh でマウント後にバックアップから復元
cp -pr /etc/nginx /etc/nginx_defult


ENV PATH /root/.rbenv/shims:/root/.rbenv/bin:/root/.nodenv/shims:/root/.nodenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# 作業ディレクトリ作成、設定
RUN mkdir /usr/src/app
WORKDIR /usr/src/app

ENTRYPOINT ["/usr/src/app/entrypoint.sh"]

EXPOSE 80
EXPOSE 443

Docker-compose.yml

Docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
version: "3"
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
- NODE_V=${NODE_V:-14.10.1}
- RUBY_V=${RUBY_V:-3.0.0}
networks:
- default
privileged: true
command: /sbin/init
ports:
- "127.0.0.1:${HTTP_PORT:-80}:80"
- "127.0.0.1:${HTTPS_PORT:-441}:441"

volumes:
# :cached を付与して、高速化
- .:/usr/src/app:cached
# vendor以下などホストと連携しなくてもよいものを、上書きして永続化の対象外にする。
# exclude volumes
- /usr/src/app/vendor
- /usr/src/app/tmp
- /usr/src/app/log
- /usr/src/app/.git
# nginx 設定関連もホスト側で管理する
- ./nginx:/etc/nginx/
tty: true
environment:
- RAILS_DATABASE_HOST=db
- RAILS_DATABASE_PORT=${RAILS_DATABASE_PORT:-3306}
- RAILS_DATABASE_USER=root
- RAILS_DATABASE_PASS=${MYSQL_ROOT_PASSWORD:-password_root}
- APP_NAME_SUFFIX=${APP_NAME_SUFFIX:-app}

db:
image: mysql:5.7
networks:
- default
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
ports:
- "${MYSQL_PORT:-3306}:3306"
volumes:
- ./db/mysql_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-password_root}

config/database.yml

config/database.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
default: &default
adapter: mysql2
encoding: utf8mb4
host: <%= ENV.fetch("RAILS_DATABASE_HOST") { "db" } %>
port: <%= ENV.fetch("RAILS_DATABASE_PORT") { 3306 } %>
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000

devuser: &devuser
username: <%= ENV.fetch("RAILS_DATABASE_USER") { "root" } %>
password: <%= ENV.fetch("RAILS_DATABASE_PASS") { "password_root" } %>

development:
<<: *default
database: <%= ENV.fetch("APP_NAME_SUFFIX") { "app" } %>/DB_developmnt
<<: *devuser

test:
<<: *default
database: <%= ENV.fetch("APP_NAME_SUFFIX") { "app" } %>/DB_test
<<: *devuser

production:
<<: *default
database: <%= ENV.fetch("APP_NAME_SUFFIX") { "app" } %>/DB_production
username:
password:

.env

.env
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Node.js Ruby のバージョン指定
NODE_V=
RUBY_V=

# データベース用の接頭辞
APP_NAME_SUFFIX=

# docker環境外からmysqlにアクセスする際に使用するポート
MYSQL_PORT=3309

# mysqlのrootユーザーパスワード
MYSQL_ROOT_PASSWORD=

# まず変更しない
RAILS_DATABASE_PORT=

# docker環境外から nginx にアクセスする際に使用するポート
HTTP_PORT=
HTTPS_PORT=

nginx 設定ファイル復元スクリプト entrypoint.sh

entrypoint.sh
1
2
3
4
5
6
#!/bin/sh

# nginx設定ファイル群をバックアップから復元存在しているファイルは上書きしない
cp -rp --no-clobber /etc/nginx_defult/* /etc/nginx

exec "$@"

/etc/nginx/conf.d/app.conf

/etc/nginx/conf.d/app.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
upstream rails_app_unix {
server unix:///usr/src/app/tmp/sockets/puma.sock;
}

server {
server_name _;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

root /usr/src/app/public;

location / {
try_files $uri @rails_app;
}

location @rails_app {
proxy_pass http://rails_app_unix;
}
}

config/puma.rb

config/puma.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

# Specifies the `worker_timeout` threshold that Puma will use to wait before
# terminating a worker in development environments.
#
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
#port ENV.fetch("PORT") { 3000 }
bind "unix://#{Rails.root}/tmp/sockets/puma.sock"

# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }

# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

操作

以下の操作を実行し、Docker 内で Rails を起動する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
docker-compose build
docker-compose up -d
docker-compose exec app bash

# 以下コンテナ内操作
mv app.conf nginx/conf.d/app.conf
nginx -s reload
# 警告出るかも

bundle init

# Gemfileの中の gem 'rails' のコメントを削除

bundle install --path=vendor/bundle
bundle exec rails new .
# <= Y

bundle exec rails webpacker:install

mv database.yml config/database.yml
mv puma.rb config/puma.rb

bundle exec rails db:create
bundle exec rails s

いつもの楽しい Rails の初期画面が表示されます。
production 環境で起動するには以下の追加操作が必要です。

1
2
3
4
5
6
7
8
export RAILS_ENV=production

# config/routes.rb に
# root 'hoge#huga' の定義が必須

bundle exec rails assets:precompile

bundle exec rails s

今回は、Docker 内 nginx の構築。
その nginx を中継して rails につないでみました。
いろいろ Rails×Docker の構成を試しているものの、そろそろずっと使う決定版的なものを用意したいところです。

ではでは。