Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 6.1のDocker開発環境構築をEvil Martians流にやってみた(更新)

更新情報

  • 2019/11/20: 初版公開
  • 2021/03/25: Rails 6.1.3.1に合わせて更新
  • 2021/04/06: Ruby 3.0.1に更新

先々月に公開したこちらの翻訳記事の実践編ということで。試行錯誤しているうちにRailsが6.0.1になりました。

クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(翻訳)

Docker Desktop for Macについて

これまではピュアな環境を求めてParallels Desktop for MacのUbuntu VM上でDockerを使っていたのですが、久しぶりにDocker Desktop for Macを使ってみると速度や使い勝手が随分よくなっていて驚きました。

  • Docker Desktop for Macの方がUbuntu VMのDockerよりビルドが速い(体感ですが)
    • ただしEvil Martiansの記事にもあるように、docker-composeでボリュームに:cachedを記述しておかないとDocker Desktop for Macで遅くなります
  • 今はdmgファイルをダウンロードしてアプリケーションフォルダにドロップするだけでインストール完了できる

Ubuntuだと基本的にsudoを打たないとDockerが使えないのと(回避方法は一応ありますが)、macOSとの間のレイヤが増えるつらみの方が大きくなってきたので、Docker Desktop for Macでやることにしました😅。

環境

  • macOS: 11.2.3(Big Sur)
  • Docker Desktop for Mac: 3.2.2(61853)
    • Docker: 20.10.5
    • Docker-compose: 1.28.5
    • Dockerhubにログインしておく
    • (既存のDockerがあれば削除しておく)
  • Rails: 6.1.3.1
  • Ruby: 3.0.1
  • Git: 2.31.0
  • dipをインストールする

本記事でのDocker操作は全面的にEvil Martians謹製のdipを使っています。dipの説明は割愛しますが、とてもよかったので別記事にしたいと思います。

追記(2019/12/04): 書きました↓

docker-composeを便利にするツール「dip」を使ってみた

リポジトリ

今回のサンプルを以下のリポジトリに置きました。

プロジェクトディレクトリ
├── .dockerdev
│   ├── .bashrc
│   ├── .psqlrc
│   ├── Aptfile
│   └── Dockerfile
├── .env(ダミー)
├── dip.yml
└── docker-compose.yml

変更の方針

  • Evil Martiansの設定からの変更は最小限とし、できるだけ汎用性を高める
    • docker-compose内のimage名は自分のプロジェクトに合わせてリネームし、:1.0.0は残す
  • Evil Martians流でやるとイメージのサイズが1GBになるので軽量化する
    • その代わり基本的なツールは自分で補わないといけない😅
  • SidekiqとRedisは使わないのでdocker-compose.ymlから削除
  • dip.ymlは多少カスタマイズする
  • .psqlrcも入れておく
  • BundlerはRubyに同梱されるようになったのでDockerfileのBundlerのインストールスクリプトは含めなかった。
  • 9bd609でDockerfileに追加されたfreedesktop.org.xmlのダウンロードスクリプトは含めなかった。詳しくは以下をご覧ください。

週刊Railsウォッチ(20210329前編)特集: Rails更新版の臨時リリースとmimemagic gemのGPL問題

変更後のDockerfile

ARG RUBY_VERSION
# 後述
FROM ruby:$RUBY_VERSION-slim-buster

ARG PG_MAJOR
ARG NODE_MAJOR
ARG YARN_VERSION

# 共通の依存関係
RUN apt-get update -qq \
  && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
    build-essential \
    gnupg2 \
    curl \
    less \
    git \
    shared-mime-info \
  && apt-get clean \
  && rm -rf /var/cache/apt/archives/* \
  && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
  && truncate -s 0 /var/log/*log

# PostgreSQLをsources listに追加
RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
  && echo 'deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main' $PG_MAJOR > /etc/apt/sources.list.d/pgdg.list

# NodeJSをsources listに追加
RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -

# Yarnをsources listに追加
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  && echo 'deb http://dl.yarnpkg.com/debian/ stable main' > /etc/apt/sources.list.d/yarn.list

# 依存関係をインストール
COPY ./.dockerdev/Aptfile /tmp/Aptfile
RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
  DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
    libpq-dev \
    postgresql-client-$PG_MAJOR \
    nodejs \
    yarn=$YARN_VERSION-1 \
    $(grep -Ev '^\s*#' /tmp/Aptfile | xargs) && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
    truncate -s 0 /var/log/*log

# bundlerとPATHを設定
ENV LANG=C.UTF-8\
  GEM_HOME=/bundle\
  BUNDLE_JOBS=4\
  BUNDLE_RETRY=3
ENV BUNDLE_PATH $GEM_HOME
ENV BUNDLE_APP_CONFIG=$BUNDLE_PATH\
  BUNDLE_BIN=$BUNDLE_PATH/bin
ENV PATH /app/bin:$BUNDLE_BIN:$PATH

# RubyGemsをアップグレードして必要なバージョンのbundlerをインストール
RUN gem update --system &&\
    gem install rails &&\
    mkdir -p /app

WORKDIR /app

Dockerfileの変更のポイント

  • イメージをruby:2.6.5からruby:3.0.1-slim-busterに変更
    • イメージが軽くなる代わりに、そのままだとcurlpingなどの基本的なパッケージも入らないので、apt-getでインストールするかAptfileに書いておきます
  • curlを実行するRUNコマンドをまとめてイメージの肥大化を防ぐ
  • gem install railsを追加

これでイメージは1GBから671MBに半減しました。もっと減らしたいならAlpine Linuxベースのイメージを使う手もあります。

Dockerのマルチステージビルドもやってみたかったのですが、手間の割にあまり報われない気がしたのと、Evil Martians流からあまり離れたくなかったので見送りました。

変更後のdocker-compose.yml

version: "3.8"

services:
  app: &app
    build:
      context: .
      dockerfile: ./.dockerdev/Dockerfile
      args:
        RUBY_VERSION: "3.0.1"
        PG_MAJOR: "13"
        NODE_MAJOR: "12"
        YARN_VERSION: "1.22.4"
    image: yourapp_docker:1.0.0
    tmpfs:
      - /tmp

  backend: &backend
    <<: *app
    stdin_open: true
    tty: true
    volumes:
      - .:/app:cached
      - rails_cache:/app/tmp/cache
      - bundle:/bundle
      - node_modules:/app/node_modules
      - packs:/app/public/packs
      - .dockerdev/.psqlrc:/root/.psqlrc:ro
      - .dockerdev/.bashrc:/root/.bashrc:ro
    environment:
      - NODE_ENV=development
      - RAILS_ENV=${RAILS_ENV:-development}
      - DATABASE_URL=postgres://postgres:postgres@postgres:5432
      - BOOTSNAP_CACHE_DIR=/bundle/bootsnap
      - WEBPACKER_DEV_SERVER_HOST=webpacker
      - WEB_CONCURRENCY=1
      - HISTFILE=/app/log/.bash_history
      - PSQL_HISTFILE=/app/log/.psql_history
      - EDITOR=vi
    depends_on:
      - postgres
      - webpacker
    env_file: .env

  runner:
    <<: *backend
    command: /bin/bash
    ports:
      - "3000:3000"
      - "3002:3002"

  rails:
    <<: *backend
    command: bundle exec rails server -b 0.0.0.0
    ports:
      - "3000:3000"

  postgres:
    image: postgres:13
    volumes:
      - .psqlrc:/root/.psqlrc:ro
      - postgres:/var/lib/postgresql/data
      - ./log:/root/log:cached
    environment:
      - PSQL_HISTFILE=/root/log/.psql_history
      - POSTGRES_HOST_AUTH_METHOD=trust
    ports:
      - 5432
    env_file: .env

  webpacker:
    <<: *app
    command: ./bin/webpack-dev-server
    ports:
      - "3035:3035"
    volumes:
      - .:/app:cached
      - bundle:/bundle
      - node_modules:/app/node_modules
      - packs:/app/public/packs
    environment:
      - NODE_ENV=${NODE_ENV:-development}
      - RAILS_ENV=${RAILS_ENV:-development}
      - WEBPACKER_DEV_SERVER_HOST=0.0.0.0

volumes:
  postgres:
  bundle:
  node_modules:
  rails_cache:
  packs:

docker-compose.ymlの変更のポイント

  • redisなど、自分の使わないサービスを削除した
  • 認証情報は.envファイルに含め、.gitignoreに追加した
    • そのためenv_file: .envをyamlに2箇所追加した
    • (リポジトリの.envは空にしてあります)
  • credentialの暗号化機能はまだ使っていません

たとえ空であっても本当は.envをリポジトリに入れたくありませんでしたが、すぐ試せるようにしたかったのでした。

補足

このdocker-compose.ymlはローカル開発環境の構築のみを想定しています。Evil Martiansもそのように作っています。

PostgreSQLの接続文字列はdocker-compose.ymlに以下のように丸ごと埋め込まれています↓ので、db/database.ymlを開発用に設定する必要はありませんし、db/database.ymlにdevelopment環境用の設定を加えても効きません(ハマりました😅)。db/database.ymlを設定するのはCIやproductionぐらいになると思います。

  • DATABASE_URL=postgres://postgres:postgres@postgres:5432

docker-compose.ymlのimage名のyourapp_dockerの部分は適宜自分のプロジェクト名に変えてください。

追記(2021/03/27)

PostgreSQLを13にアップグレードしたところ、従来のパスワードなしログインが効かなくなったため、docker-compose.ymlに以下を追加しました。この設定はローカルのdevelopment以外では使わないでください。

- POSTGRES_HOST_AUTH_METHOD=trust

参考: PostgreSQL(Docker)にRails(Docker)が接続できなくなったから調べてみた。(could not translate host name "db" to address: Name or service not known) - Qiita

セットアップ

  • dipをインストールする(homebrewでもできます)
gem install dip
  • git clone git@github.com:hachi8833/rails_docker_like_evilmartians.gitでリポジトリをcloneしてディレクトリに移動する

  • docker-compose.ymlのyourapp_dockerは自分のプロジェクト名に変更する(本記事では変更していません)

  • git checkinしてコミット

  • dip compose buildでビルド

$ dip compose build
# 略
Successfully tagged yourapp_docker:1.0.0
  • dip shでコンテナにログインする
$ dip sh
Creating network "rails_docker_like_evilmartians_default" with the default driver
Creating volume "rails_docker_like_evilmartians_postgres" with default driver
Creating volume "rails_docker_like_evilmartians_bundle" with default driver
Creating volume "rails_docker_like_evilmartians_node_modules" with default driver
Creating volume "rails_docker_like_evilmartians_rails_cache" with default driver
Creating volume "rails_docker_like_evilmartians_packs" with default driver
root@7b09aea79b9f:/app#

以下はDockerコンテナ内部での作業です。

  • yarn -vruby -vbundle -vwhich railsが実行できることを確認
  • rails newに好みのオプションを付けて実行

なおbundle execは不要です。

今回は以下のオプションにしました。アプリ名はappで固定されます。Webpackerもまとめてセットアップされます。

rails new . --database=postgresql --skip-active-storage --skip-action-mailer --skip-active-job --skip-action-cable --skip-action-mailbox --skip-action-text --skip-turbolinks --skip-sprockets --skip-spring --skip-bootsnap --webpacker --webpack=vue

root@7b09aea79b9f:/app# rails new . --database=postgresql --skip-active-storage --skip-action-mailer --skip-active-job --skip-action-cable --skip-action-mailbox --skip-action-text --skip-turbolinks --skip-sprockets --skip-spring --skip-bootsnap --webpacker --webpack=vue
# 略
success Saved lockfile.
success Saved 12 new dependencies.
info Direct dependencies
├─ vue-loader@15.9.6
├─ vue-template-compiler@2.6.12
└─ vue@2.6.12
info All dependencies
├─ @vue/component-compiler-utils@3.2.0
├─ consolidate@0.15.1
├─ de-indent@1.0.2
├─ he@1.2.0
├─ merge-source-map@1.1.0
├─ prettier@1.19.1
├─ vue-hot-reload-api@2.3.4
├─ vue-loader@15.9.6
├─ vue-style-loader@4.1.3
├─ vue-template-compiler@2.6.12
├─ vue-template-es2015-compiler@1.9.1
└─ vue@2.6.12
Done in 6.40s.
Webpacker now supports Vue.js 🎉
  • 環境によっては以下も実行して、bundlerの余分なメッセージを抑制する
bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java
  • 使われていないvendorディレクトリをrm -rf vendorで削除
  • exitでDockerをいったん抜ける
  • git checkinする

起動前の設定

  • config/environments/development.rbに以下を追加する
config.hosts << "localhost"
config.web_console.whitelisted_ips = '0.0.0.0/0'

Rails 6からはconfig.hostを明示的に指定する必要があります(ウォッチ20190401)。

また、Dockerで開発する場合はおそらくwhitelisted_ipsの制約も解除が必要になります。

追記(2021/03/27): dip provisionを実行する前にGemfileのtestブロックに以下を追記し、dip bundle installを実行してrexml gemをインストールしてください。

gem 'rexml', '~> 3.2', '>= 3.2.4'

参考: Rails 6.1, Ruby 3.0.0: tests error as they cannot load rexml - Stack Overflow


  • dip provisionを実行してdevelopmentとtestの空データベースを作成
    • (dip.yamlをそれ用にカスタマイズしています)
    • データベース名はapp_developmentapp_testで固定
$ dip provision
# (略)
== Preparing database ==
Created database 'app_development'
== Installing dependencies ==
The Gemfile's dependencies are satisfied

== Preparing database ==

== Removing old logs and tempfiles ==

== Restarting application server ==
== Installing dependencies ==
The Gemfile's dependencies are satisfied

== Preparing database ==
Created database 'app_test'

== Removing old logs and tempfiles ==

== Restarting application server ==
  • 念のためdip minitestでminitestを実行
$ dip minitest
Starting rails_docker_like_evilmartians_postgres_1 ... done
Run options: --seed 59169

# Running:



Finished in 0.000523s, 0.0000 runs/s, 0.0000 assertions/s.
0 runs, 0 assertions, 0 failures, 0 errors, 0 skips
  • git checkinでコミット

いよいよ起動

できた!

$ dip rails s
Starting rails_docker_like_evilmartians_postgres_1 ... done
=> Booting Puma
=> Rails 6.0.1 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.0 (ruby 2.6.5-p114), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

後は好きに進めます。dip lsすればdipのサブコマンド一覧が表示されます。

作業が終わったらdocker-compose downでコンテナを終了します。

参考: 後からプロジェクトに参加する人の作業

  • Git、Docker for Mac、git-flow、dipなどをセットアップ
  • リポジトリをgit cloneする
  • dip compose build
  • dip bundle install
  • dip yarn install --check-files
  • .envに秘密鍵を入れるのであれば、.gitignoreに.envを追加する(必須)
  • データベースにseedデータを入れる(略)

Dockerfileを改定した場合は、docker-compose.ymlのimage名のタグにあるバージョン番号を適宜更新します。

おたより発掘

関連記事

クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。