Rails5の開発環境をDockernizeする(MySQL + Spring + Sidekiq + Redis + docker-sync)

年の瀬など関係なく過ごしております。

既存のRailsアプリの開発環境をDocker化する機会がありましたので、やったことを記録しておきます。

とは言っても、先人たちの知恵がかなり参考になりましたので、それを組み合わせて調整した程度の焼き直しです。

前提

  • docker-compose up 一発で開発環境構築が済むようにしたい
  • 本番環境のコンテナ化は別のDockerfile & docker-compose.ymlで行う

参考にしたページ(一部)

とても参考になりました。ありがとうございます。

成果物

Dockerfile.dev

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
FROM ruby:2.4.2

ENV LANG C.UTF-8

RUN apt-get update -qq
RUN apt-get install -y --no-install-recommends \
build-essential \
curl \
wget \
apt-transport-https \
git \
libpq-dev \
libfontconfig1 && \
rm -rf /var/lib/apt/lists/*

# install node
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y nodejs

# install yarn
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update
RUN apt-get install -y yarn

ENV ENTRYKIT_VERSION 0.4.0

RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink

RUN mkdir /app

WORKDIR /app

RUN bundle config build.nokogiri --use-system-libraries

ENTRYPOINT [ \
"prehook", "ruby -v", "--", \
"prehook", "bundle install -j3 --quiet", "--", \
"prehook", "yarn install", "--"]

ポイント:

  • nodeのインストール
    • railsアプリでuglifierなどのExecJSが必要なgemをいくつか使っていますので、予めnodeをインストールしておきます
  • yarnのインストール
    • こちらも同様に、railsアプリ内で利用するnpmモジュールを package.json で管理しておりますので、予めyarnをインストールしておきます
  • ENTRYKITの活用
    • 参考サイトそのままですが、ENTRYKITを活用することで、起動時に必要な処理(ここでは bundle installyarn install)などを完結に記述できます

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
version: '2'
services:
rails: &app_base
build:
context: .
dockerfile: "Dockerfile.dev"
command: ["bundle", "exec", "rails", "s"]
env_file:
- "./.env.dev"
volumes:
- "rails-sync:/app"
volumes_from:
- data
ports:
- "3000:3000"
- "9292:9292"
depends_on:
- db
tty: true
stdin_open: true
worker:
<<: *app_base
command: ["bundle", "exec", "sidekiq"]
ports: []
depends_on:
- db
- redis
db:
image: "mysql:5.7.20"
environment:
MYSQL_ROOT_PASSWORD: password
volumes_from:
- data
ports:
- "3306:3306"
redis:
image: redis:alpine
networks:
- default
ports:
- '6379:6379'
volumes_from:
- data
data:
image: busybox
volumes:
- "db:/var/lib/mysql"
- "bundle:/usr/local/bundle"
- "redis:/data/redis"
- "node_modules:/app/node_modules"

volumes:
db:
driver: local
bundle:
driver: local
redis:
driver: local
node_modules:
driver: local
rails-sync:
external: true

ポイント:

  • railsコンテナのport 9292

    • config/puma.rb 内でSSL with オレオレ証明書を有効にしているので、ポートを空けています

      1
      2
      3
      4
      5
      6
      if "development" == ENV.fetch("RAILS_ENV") { "development" }
      ssl_bind '0.0.0.0', '9292', {
      key: 'dev-cert/server.key',
      cert: 'dev-cert/server.crt',
      verify_mode: 'none'
      }
  • 各種データストア, bundle, node_modulesの永続化

    • busybox imageを使って、永続化が必要なデータを隔離しています
    • 新しいモジュールを追加しても、bundle installやyarn installの実行は短い時間で済みます
  • springコンテナは不要だった
    • 上記参考サイトにはspringコンテナが別途必要とのことだったのですがbin/railsbin/rake 内にspringを読み込むコードがあれば、docker-compose exec rails rails cでSpring preloader経由で呼び出されました
    • なぜだろう
  • worker / sidekiqコンテナ
    • sidekiqを起動するために、別でコンテナを定義しています
  • 今だったら、version: 3の記法で書いてもよかったかもしれない

docker-syncについて

docker-sync.yml

1
2
3
4
5
6
7
8
9
version: "2"

options:
verbose: true
syncs:
rails-sync: # tip: add -sync and you keep consistent names as a convention
src: '.'
# sync_strategy: 'native_osx' # not needed, this is the default now
# sync_excludes: ['ignored_folder', '.ignored_dot_folder']

Docker for Macにはファイルシステムのパフォーマンス上の問題があるため、workaroundとして、次の2種類が有力です。

  1. cachedオプションの使用 参考
    • volumeマウント時にどのレベルでファイルの一貫性を要求するか、というオプションが設定されました
    • 設定して試してみましたが私の環境では大きな効果はなかったので、docker-syncを採用しました
  2. docker-sync

    • 上記の通り、docker-sync.ymlという設定ファイルにrails-syncという名前でdocker-sync用のvolumeを作成します
    • railsコンテナからは、rails-sync volumeをマウントします
    1
    2
    volumes:
    - "rails-sync:/app"
    • 今回は、開発環境のみでしかdocker-compose.ymlを使用していませんが、もしdocker-composeが本番用にもある場合はこちらのテクニックが参考になります
    • docker-syncの設定ファイルには多数のオプションがありますので、気になる方は公式ページを確認するとよいでしょう。
    • 手元の環境では、docker-syncを使用することでページ表示までの時間が約3倍ほど高速化(2s -> 0.7s)されました。よかったですね。

まとめ

こういう作業をすべてゼロからやっていたと思うと恐ろしいですが、先人の知恵のおかげで1日で快適な開発環境を手に入れることができました。

みなさまよいお年を。