Railsアプリの開発環境向けDockerfile + docker-compose.yml

人に説明するときに記事あると便利なので、開発環境向けのDockerfileとdocker-compose.ymlを書いておく。

Dockerfile

FROM ruby:3.0.0

WORKDIR /app

# Using Node.js v14.x(LTS)
RUN curl -fsSL https://deb.nodesource.com/setup_14.x | bash -

# Add packages
RUN apt-get update && apt-get install -y \
      git \
      nodejs \
      vim

# Add yarnpkg for assets:precompile
RUN npm install -g yarn

# Add Chrome
RUN curl -sO https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
    && apt install -y ./google-chrome-stable_current_amd64.deb \
    && rm google-chrome-stable_current_amd64.deb

# Add chromedriver
RUN CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` \
    && curl -sO https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip \
    && unzip chromedriver_linux64.zip \
    && mv chromedriver /usr/bin/chromedriver
  • git: GitHubを参照してgemを入れるときに必要
  • nodejs + yarn: bin/rails assets:precompile で必要
  • vim: bin/rails credentials:edit で使う
  • Chrome + chromedriver: System Test で必要

docker-compose.yml

version: "3.8"
services:
  db:
    image: postgres:12-alpine
    volumes:
      - postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_HOST_AUTH_METHOD: trust

  redis:
    image: redis:5-alpine
    volumes:
      - redis:/data

  web: &web
    build: .
    image: app:1.0.0
    stdin_open: true
    tty: true
    volumes:
      - .:/app:cached
      - bundle:/app/vendor/bundle
      - node_modules:/app/node_modules
      - rails_cache:/app/tmp/cache
      - packs:/app/public/packs
      - packs_test:/app/public/packs-test
    tmpfs:
      - /tmp
    environment:
      BUNDLE_PATH: "/app/vendor/bundle"
      BOOTSNAP_CACHE_DIR: "/app/vendor/bundle"
      WD_INSTALL_DIR: "/usr/local/bin"
      HISTFILE: "/app/log/.bash_history"
      EDITOR: "vi"
      DATABASE_URL: "postgres://postgres:postgres@db:5432"
      REDIS_URL: "redis://redis:6379/"
      RAILS_MASTER_KEY:
    depends_on:
      - db
      - redis
    command: ["bin/rails", "server", "-b", "0.0.0.0"]
    expose: ["3000"]
    ports: ["3000:3000"]
    user: root
    working_dir: /app

  worker:
    <<: *web
    command: ["bundle", "exec", "sidekiq"]
    expose: []
    ports: []

volumes:
  postgres:
  redis:
  bundle:
  node_modules:
  rails_cache:
  packs:
  packs_test:
image: app:1.0.0

imageに名前とバージョンをつけておくことで、バージョンをインクリメントすると docker-compose run のときに自動的にビルドが走る。

Dockerfileを変えたときにバージョンをインクリメントすることで、他の開発者が古いイメージを使い続けてしまう事を防ぐことができる。

stdin_open: true
tty: true

byebug などでデバッグできるようにするため。

volumes:
  - .:/app:cached
  - bundle:/app/vendor/bundle
  - node_modules:/app/node_modules
  - rails_cache:/app/tmp/cache
  - packs:/app/public/packs
  - packs_test:/app/public/packs-test

Docker for Mac遅い問題の対処。

ホストとの同期を減らすことで若干マシになる。遅いけど。

worker:
  <<: *web
  command: ["bundle", "exec", "sidekiq"]
  expose: []
  ports: []

sidekiqを使う場合、基本的な設定はwebと同じなのでエイリアスを使う。

expose, portsはworkerで不要なので上書きする。

使い方

初回の環境構築

Dockerイメージを作成して、 bin/setup を実行する。

$ docker-compose build
$ docker-compose run --rm web bin/setup

サーバの起動

$ docker-compose run --rm --service-ports web

実行すると http://localhost:3000 でアクセスできる。

Railsコマンド類の実行

$ docker-compose run --rm web bin/rails -T

テストの実行

$ docker-compose run --rm -e RAILS_ENV=test web bin/rails db:test:prepare
$ docker-compose run --rm -e RAILS_ENV=test web bin/rails test

コンテナ内で作業する

毎回コンテナの起動をすると遅いので、基本的にコンテナ内で作業した方が楽。

$ docker-compose run --rm web bash
root@b419978e89ec:/app# bin/rails --version
Rails 6.1.3

全てのvolumeを削除する

開発環境を一新したいときに使う。

$ docker-compose down --volumes

追加の説明

Dockerfileでbundle installは不要なのか?

Dockerイメージのビルドでは不要です。

bin/setup の実行で、マウントしてるvolumeにインストールされます。

なぜdocker-compose up webで起動しないのか?

docker-compose up で起動すると標準入力が使えなくなって、byebugでデバッグできなくなるため。

あと、コンテナを止めたときに稀にtmp/pids/server.pidが残ることがある。*1

コマンドが長い

bibendi/dipを使うか、bashエイリアスを使うと楽です。

開発用のイメージが大きい

alpineやmulti stage buildを使えば軽量化できるかもしれないですが、面倒だったので調べてないです。

開発環境用のDockerイメージが少し重くても、大して困らないため。

*1:原因の詳細は調べられていないですが、 docker-compose run では今まで起きていない。