Movatterモバイル変換


[0]ホーム

URL:


Tech Rachoエンジニアの「?」を「!」に。
  1. TOP
  2. Ruby / Rails関連
  3. [旧版]クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(翻訳)
  • Ruby / Rails関連

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

訳注

本記事の原文が全面的に更新されたので、以下の更新版を公開しました。今後は以下をご参照ください。

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

概要

原著者の許諾を得て翻訳・公開いたします。

更新情報

訳注

Ruby on Whalesは、Ruby on Railsの他に、もしかすると「Boy on a Dolphin」にかけているのかもしれないと思いました。Whales(クジラ)はもちろんDockerのシンボルです。

まえがき

本記事は、私がRailsConf 2019で話した「Terraforming legacy Rails applications」↑の、いわばB面に相当します。この記事を読んで、皆さんがアプリケーション開発をDockerに乗り換えるとまでは考えていません(皆さんが以下の動画で若干言及しているのをご覧になっていたとしても)。本記事の狙いは、私が現在のRailsプロジェクトで用いている設定を皆さんと共有することです。それらのRailsプロジェクトは、Evil Martiansのproduction development環境で生まれたものです。どうぞご自由にお使いください。

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

原文免責事項: 英語版記事は最新の推奨事項に合わせて更新を繰り返しています。詳しくは記事末尾のChangelogをご覧ください(参考:原文Changelog)。


私がdevelopment環境でDockerを使い始めたのは、かれこれ3年ほど前の話です(それまで使っていたVagrantは4GB RAMのノートパソコンではあまりに重たかったのでした)。もちろん、最初からバラ色のDocker人生だったわけではありません。自分のみならず、チームにとっても「十分にふさわしい」Docker設定を見つけるまでに2年という月日を費やしました。

私の設定を、(ほぼほぼ)すべての行に解説を付けてご覧に入れたいと思います。「Dockerをわかっている」前提のわかりにくいチュートリアルはもうたくさんですよね。


本記事のソースコードは、GitHubのevilmartians/terraforming-railsでご覧いただけます


本記事の例では以下を用います。

  • Ruby 2.6.3
  • PostgreSQL 13
  • NodeJS 11 & Yarn(Webpackerベースのアセットコンパイル用)

🔗 Evil Martians流Dockerfile

Railsアプリケーションの「環境」は、Dockerfileで定義します。サーバーの実行、コンソール(rails c)、テスト、rakeタスク、開発者としてコードとのインタラクティブなやりとりは、ここで行います。

ARG RUBY_VERSION# 後述FROM ruby:$RUBY_VERSION-slim-busterARG PG_MAJORARG NODE_MAJORARG BUNDLER_VERSIONARG YARN_VERSION# 共通の依存関係RUN apt-get update -qq \  && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \    build-essential \    gnupg2 \    curl \    less \    git \  && apt-get clean \  && rm -rf /var/cache/apt/archives/* \  && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \  && truncate -s 0 /var/log/*log# mimemagic gem用のMIMEタイプデータベースをダウンロードRUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \  DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \    shared-mime-info \  && cp /usr/share/mime/packages/freedesktop.org.xml ./ \  && apt-get remove -y --purge 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 \  && mkdir -p /usr/share/mime/packages \  && cp ./freedesktop.org.xml /usr/share/mime/packages/# PostgreSQLをソースリストに追加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をソースリストに追加RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -# Yarnをソースリストに追加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# アプリケーションの依存関係# 外部のAptfileでやってる(後ほどお楽しみに!)COPY Aptfile /tmp/AptfileRUN 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の設定ENV LANG=C.UTF-8 \  BUNDLE_JOBS=4 \  BUNDLE_RETRY=3# bundler設定をプロジェクトのルートに保存する場合は以下をコメント解除# ENV BUNDLE_APP_CONFIG=.bundle# `bin/`や`bundle exec`を付けずにbinstabを実行したい場合は以下をコメント解除# ENV PATH /app/bin:$PATH# RubyGemsをアップグレードして必要なバージョンのbundlerをインストールRUN gem update --system && \    gem install bundler:$BUNDLER_VERSION# appコードを置くディレクトリを作成RUN mkdir -p /appWORKDIR /app

このDockerfileの設定は必要不可欠なもののみを含んでいますので、これを元にカスタマイズできます。このDockerfileで何が行われるかを解説します。

最初の2行に少々妙なことが書かれています。

ARG RUBY_VERSIONFROM ruby:$RUBY_VERSION-slim-buster

FROM ruby:2.6.3みたいに適当な安定版Rubyのバージョンを書いておけばよさそうなものですよね。ここではDockerfileを一種のテンプレートとして用い、環境を外部から設定可能にしたいのです。

  • ランタイム依存の厳密なバージョンは、docker-compose.ymlの方で指定することにします(後述)。
  • aptコマンドでインストール可能な依存のリストは、これも別ファイルに保存することにします(これも後述)。

また、PostgreSQLなどの他の依存関係に対応する正しいソースを追加するためにDebianのリリース(buster)を明示的に指定しています。

上に続く以下の4行は、PostgreSQL、NodeJS、Yarn、Bundlerのバージョンを定義します。

ARG PG_MAJORARG NODE_MAJORARG BUNDLER_VERSIONARG YARN_VERSION

今どきDockerfileをDocker Composeなしで使う人などいないという前提なので、Dockerfileではデフォルト値を指定しないことにします。

続いて実際のイメージビルドが行われます。Dockerイメージのサイズを削減するためにslimベースのDockerイメージを使うので、最初にシステム共通の依存関係(GitやcURLなど)を手動でインストールします。

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

システム依存のインストールの詳細については、アプリケーション固有のものについて説明するときに後述します。

次に、mimemagic gemで使うMIMEタイプデータベースをダウンロードするためだけにRUNコマンドを実行します(mimemagic gemのライセンス問題はご存知ですか?)。

RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \  DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \    shared-mime-info \  && cp /usr/share/mime/packages/freedesktop.org.xml ./ \  && apt-get remove -y --purge 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 \  && mkdir -p /usr/share/mime/packages \  && cp ./freedesktop.org.xml /usr/share/mime/packages/

mimemagic gemを使わないのであれば、上のブロックは削除して構いません。

PostgreSQL、NodeJS、Yarnをaptコマンドでインストールするために、それらのdebパッケージのリポジトリをソースリストに追加する必要があります。

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

原注: ここが、OSリリースをbusterにしたことを利用している箇所です。

RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -
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

今度は依存関係のインストールです(apt-get installの実行など)。

COPY Aptfile /tmp/AptfileRUN 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 \    $(grep -Ev '^\s*#' /tmp/Aptfile | xargs) && \    apt-get clean && \    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \    truncate -s 0 /var/log/*log

まずAptfileという裏技について解説します。

COPY Aptfile /tmp/AptfileRUN apt-get install \    $(grep -Ev '^\s*#' /tmp/Aptfile | xargs) && \

Aptfileというアイデアはheroku-buildpack-aptから拝借しました。heroku-buildpack-aptは、Herokuに追加パッケージをインストールできます。このbuildpackを使っていれば、同じAptfileをローカルでもproduction環境でも再利用できます(buildpackのAptfileの方が多くの機能を提供していますが)。

私たちのデフォルトAptfileに含まれているのパッケージは、たったひとつです(私たちはRailsのcredentialの編集にVimを使っています)。

vim

私が携わっていた直前のプロジェクトでは、LaTeXやTexLiveを用いてPDFを生成しました。そのときのAptfileは、さしずめ以下のような感じにできたでしょう(当時私はこの技を使っていませんでしたが)。

vimtexlivetexlive-latex-recommendedtexlive-fonts-recommendedtexlive-lang-cyrillic

このようにすることで、タスク固有の依存関係を別ファイルに切り出し、Dockerfileの普遍性を高めています。

DEBIAN_FRONTEND=noninteractiveという行については、「answer on Ask Ubuntu」という記事をご覧になることをおすすめします。

--no-install-recommendsスイッチを指定すると、推奨パッケージのインストールを行わなくなるので容量を節約でき、ひいてはイメージをもっとスリムにできます。詳しくは「Xubuntu Geek: Save disk space with apt-get option "no-install-recommends" in Xubuntu」をご覧ください。

RUNの最後の部分(apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && truncate -s 0 /var/log/*log)も目的は同じです。取得したパッケージファイルのローカルリポジトリ(ここまでで必要なものはすべてインストールできているので、これらはもはや不要です)や、インストール中に作成されたすべての一時ファイルやログをクリーンアップします。 特定のDockerレイヤにごみを残さないようにするため、このクリーンアップ作業は同じRUNステートメントの中で行う必要があります。

最後の部分は、もっぱらBundlerのためのものです。

# bundlerの設定ENV LANG=C.UTF-8 \  BUNDLE_JOBS=4 \  BUNDLE_RETRY=3# bundler設定をプロジェクトのルートに保存する場合は以下をコメント解除# ENV BUNDLE_APP_CONFIG=.bundle# `bin/`や`bundle exec`を付けずにbinstabを実行したい場合は以下をコメント解除# ENV PATH /app/bin:$PATH# RubyGemsをアップグレードして必要なバージョンのbundlerをインストールRUN gem update --system && \    gem install bundler:$BUNDLER_VERSION

LANG=C.UTF-8は、デフォルトロケールをUTF-8に設定します。これを行わないとRubyが文字列でUS-ASCIIを使ってしまうので、かわいいかわいい絵文字たちとおさらば👋になってしまいます。

プロジェクト固有のbundler設定(プライベートなgemで使うcredentialなど)の保存先に<root>/.bundleフォルダを使う場合は、BUNDLE_APP_CONFIGが必要です。デフォルトのRubyイメージではこの変数が定義されていて(#129)、bundlerがローカル設定にフォールバックしないようになっています。

また、bundle execを付けずにコマンドを実行できるよう、PATH変数に<root>/binフォルダを追加することも可能です。これはデフォルトではオフにしてありますが、理由はマルチプロジェクト環境(Railsアプリ内でローカルのgemやエンジンを使う場合など)でコードが動かなくなる可能性があるためです。

🔗 Evil Martians流docker-compose.yml

Docker Composeは、コンテナ化された環境をオーケストレーションするツールで、これを用いてコンテナ同士を接続し、永続化ボリュームやサービスを定義できます。

以下は、データベースにPostgreSQL、バックグラウンドジョブの処理にSidekiqを用いた、Railsアプリケーションの典型的な開発環境のためのdocker-compose.ymlです。

version: '2.4'services:  app: &app    build:      context: .dockerdev      dockerfile: Dockerfile      args:        RUBY_VERSION: '2.6.3'        PG_MAJOR: '13'        NODE_MAJOR: '11'        YARN_VERSION: '1.13.0'        BUNDLER_VERSION: '2.0.2'    image: example-dev:1.0.0    environment: &env      NODE_ENV: ${NODE_ENV:-development}      RAILS_ENV: ${RAILS_ENV:-development}      YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache    tmpfs:      - /tmp  backend: &backend    <<: *app    stdin_open: true    tty: true    volumes:      - .:/app:cached      - rails_cache:/app/tmp/cache      - bundle:/usr/local/bundle      - node_modules:/app/node_modules      - packs:/app/public/packs      - .dockerdev/.psqlrc:/root/.psqlrc:ro    environment:      <<: *env      REDIS_URL: redis://redis:6379/      DATABASE_URL: postgres://postgres:postgres@postgres:5432      BOOTSNAP_CACHE_DIR: /usr/local/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:        condition: service_healthy      redis:        condition: service_healthy  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'  sidekiq:    <<: *backend    command: bundle exec sidekiq -C config/sidekiq.yml  postgres:    image: postgres:13    volumes:      - .dockerdev/.psqlrc:/root/.psqlrc:ro      - postgres:/var/lib/postgresql/data      - ./log:/root/log:cached    environment:      PSQL_HISTFILE: /root/log/.psql_history      POSTGRES_PASSWORD: postgres    ports:      - 5432    healthcheck:      test: pg_isready -U postgres -h 127.0.0.1      interval: 5s  redis:    image: redis:3.2-alpine    volumes:      - redis:/data    ports:      - 6379    healthcheck:      test: redis-cli ping      interval: 1s      timeout: 3s      retries: 30  webpacker:    <<: *app    command: ./bin/webpack-dev-server    ports:      - '3035:3035'    volumes:      - .:/app:cached      - bundle:/usr/local/bundle      - node_modules:/app/node_modules      - packs:/app/public/packs    environment:      <<: *env      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0volumes:  postgres:  redis:  bundle:  node_modules:  rails_cache:  packs:

このdocker-compose.ymlでは8つのサービスを定義しています。サービスを8つも定義しているのを不思議に思うかもしれませんが、その一部は単に他と共有する設定を定義しているだけで(appbackendといった抽象サービス)、残りはアプリケーションコンテナを用いる特定のコマンド(runnerなど)のためのものです。

私たちはDocker Composeのバージョン2を意図的に使っています。開発目的にはバージョン2が適しています。詳しくはDockerのissueをご覧ください(#7593)。

このアプローチでは、アプリケーションをdocker-compose upで実行するのではなく、docker-compose up railsのように、実行したいサービスを常にピンポイントで指定するようにしています。development環境ではWebpackerやSidekiqなどを全部立ち上げる必要はめったにないので、合理的です。

それでは各サービスを詳しく見ていくことにしましょう。

🔗app

appサービスの主な目的は、(上のDockerfileで定義した)アプリケーションコンテナの構築に必要な情報をすべて提供することです。

build:  context: .dockerdev  dockerfile: Dockerfile  args:    RUBY_VERSION: '2.6.3'    PG_MAJOR: '13'    NODE_MAJOR: '11'    YARN_VERSION: '1.13.0'    BUNDLER_VERSION: '2.0.2'

contextディレクトリは、Dockerのbuild contextを定義します。これはビルドプロセスで用いる一種のワーキングディレクトリであり、COPYコマンドなどで用いられます。また、イメージがビルドされるたびにパッケージ化されてDockerデーモンに送信されるので、イメージのサイズはできるだけ小さくしておくのが望まれます。

ログや一時ファイルによってプロジェクトディレクトリのサイズが非常に大きくなるとビルドが遅くなる可能性があります。.dockerignoreファイルでサイズを減らすか、.dockerdevのような小さなディレクトリを指定しましょう。

私たちの設定ではDockerfileへのパスを明示的に指定しています。理由は、私たちはDockerファイルをプロジェクトのルートディレクトリに配置するのではなく、.dockerdevという隠しディレクトリの中に他のすべてのDocker関連ファイルと一緒に配置しているからです。

前述したように、Dockerfileではargsを用いて依存関係の正確なバージョンを指定しています。

ここでひとつ注意すべきは、イメージにタグ付けする方法です。

image: example-dev:1.0.0

Dockerを開発に用いるメリットのひとつは、設定の変更を自動的にチーム全体で同期できることです。これは、ローカルイメージ(引数や、イメージが依存するファイルでもよい)を変更するたびにローカルイメージのバージョン番号を常にアップグレードしておきさえすれば可能です。逆に最悪なのは、ビルドタグにexample-dev:latestを使うことです。

イメージのバージョン番号が正しく管理されていれば、異なる2つの環境同士で余分な追加作業を一切行わずに済むようにもできます。たとえば、長期間実行するchore/upgrade-to-ruby-3ブランチで作業している最中に、いつでもmasterブランチに切り替えて古いイメージや古いRubyを利用できます。しかもリビルド不要で。


重要:docker-compose.yml内のイメージでlatestタグを使うのは最悪です。


次に、共通の環境変数を追加します。この環境変数は、RailsやWebpackerなど複数のサービスで共有されます。

environment: &env  NODE_ENV: ${NODE_ENV:-development}  RAILS_ENV: ${RAILS_ENV:-development}  # 高速化のため、マウントしたボリュームにYarnキャッシュを保存  YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache

ここでもいろんなことが行われていますが、1つ説明しておきたい点があります。

まずX=${X:-smth}という構文についてです。これは「コンテナ内の変数Xで使う値は、ホストマシンに環境変数Xがあればそれを使い、ない場合は指定の値を使う」という意味です。これによって、RAILS_ENV=test docker-compose up railsのようにコマンドで別の環境を指定してサービスを実行できるようになります。

なお、environmentのフィールドがリスト形式(- NODE_ENV=xxx)ではなく辞書形式(NODE_ENV: xxx)になっていることにご注意ください。辞書形式にすることで、共通設定を再利用できるようになります(後述)。

他にも、コンテナ内で/tmpフォルダにDockerのtmpfsマウントを用いるように指定することでスピードアップしています。

tmpfs:  - /tmp

🔗backend

いよいよ本記事で一番美味しい部分にたどり着きました。

このbackendサービスは、あらゆるRubyサービスで共有する振る舞いを定義します。

まずはvolumes:を見てみましょう。

volumes:  - .:/app:cached  - bundle:/usr/local/bundle  - rails_cache:/app/tmp/cache  - node_modules:/app/node_modules  - packs:/app/public/packs  - .dockerdev/.psqlrc:/root/.psqlrc:ro

volumes:リストの最初の項目「- .:/app:cached」では、現在のワーキングディレクトリ(つまりプロジェクトのルートディレクトリ)をコンテナ内の/appフォルダにマウントし、かつcached戦略を用いています。このcachedという修飾子は、MacOSでのDocker環境の効率を高めるうえで重要なポイントです。cachedについては別記事を書いていますので😉、本記事ではこれ以上は深堀りしません。詳しくはこちらの「公式ドキュメント」をご覧ください。

その次の行では、/bundleという名前のボリュームに/urs/local/bundleの内容を保存するようコンテナに指示しています(gemはデフォルトでここに保存されます)。私たちはこのようにして、gemのデータを永続化して複数の実行で使えるようにしています。docker-compose.ymlで定義されたすべてのボリュームは、docker-compose down --volumesを実行するまで持続します。

以下の3行も、「DockerがMacだと遅い」という呪いをお祓いするために書かれています。私たちは、生成されたファイルをすべてDockerボリュームに配置することで、ホストマシンでディスク操作が重くなるのを回避しています。

- rails_cache:/app/tmp/cache- node_modules:/app/node_modules- packs:/app/public/packs

ポイント: macOSでDockerを十分高速に動かすには、ソースファイルを:cachedでマウントし、かつ、生成されたコンテンツ(アセットやbundleなど)の保存にはボリュームを使うこと。


末尾の3行では、特定のpsql設定をコンテナに追加しています。私たちはほとんどの場合、コマンド履歴をアプリのlog/.psql_historyに保存することで永続化する必要があります。psqlをRubyのコンテナに追加している理由は、rails dbconsoleを実行するときに内部で使われるからです。

私たちが追加している.psqlrcファイルには、履歴ファイルを環境変数経由で指定できるようにするために以下の仕掛けが施されています。履歴ファイルへのパスをPSQL_HISTFILE環境変数で指定できるようにし、利用できない場合は$HOME/.psql_historyにフォールバックします。

\set HISTFILE `[[ -z $PSQL_HISTFILE ]] && echo $HOME/.psql_history || echo $PSQL_HISTFILE`

環境変数について説明します。

environment:  <<: *env  REDIS_URL: redis://redis:6379/  DATABASE_URL: postgres://postgres:postgres@postgres:5432  WEBPACKER_DEV_SERVER_HOST: webpacker  BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap  HISTFILE: /app/log/.bash_history  PSQL_HISTFILE: /app/log/.psql_history  EDITOR: vi  MALLOC_ARENA_MAX: 2  WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}

冒頭の<<: *envで、共通の環境変数を「継承」しているのがポイントです。

DATABASE_URL変数、REDIS_URL変数、WEBPACKER_DEV_SERVER_HOST変数は、Rubyアプリケーションを別のサービスに接続しますDATABASE_URL変数はRailsのActive Recordで、WEBPACKER_DEV_SERVER_HOST変数はRailsのWebpackerでいつでもサポートされます。ライブラリによってはREDIS_URL変数もサポートします(Sidekiq)が、どのライブラリでもサポートされているとは限りません(たとえばAction Cableでは明示的に設定が必要です)。

私たちはbootsnapを用いてアプリケーションの読み込みを高速化しています。bootsnapのキャッシュはBudlerのデータと同じ場所に保存しています。理由は、このキャッシュに含まれている内容のほとんどがgemのデータだからです。つまり、たとえばRubyを別のバージョンにアップグレードする場合は、それらを一括廃棄するべきということです。

HISTFILE=/app/log/.bash_historyは、開発者のUXにとって重要な設定です。この設定によってbashの履歴が特定の場所に保管され、永続化されるようになります。

EDITOR=viは、たとえばrails credentials:editコマンドでcredentialファイルを管理するのに用います。

末尾の2つの設定であるMALLOC_ARENA_MAXWEB_CONCURRENCYは、Railsのメモリハンドリングをチェックしやすくするためのものです。

他にbackendサービスで説明すべきは以下の行だけです。

stdin_open: truetty: true

この設定によって、サービスをインタラクティブ(TTYを提供するなどの対話的な操作)にできます。私たちの場合、たとえばRailsコンソールやBashをコンテナ内で実行するのに必要です。

これは、-itオプションを付けてDockerコンテナを実行するのと同じです。

🔗webpacker

webpackerで言及しておきたいのはWEBPACKER_DEV_SERVER_HOST=0.0.0.0という設定だけです。これによって、Webpack dev serverに「外部から」アクセスできるようになります(デフォルトではlocalhostで実行されます)。

🔗runner

このrunnerサービスの目的を説明するために、私がDockerを開発に用いるときの段取りについて説明させてください。

  • 私はDockerデーモンの起動で以下のようなカスタムdocker-startスクリプトを作って実行しています。
#!/bin/shif ! $(docker info > /dev/null 2>&1); then  echo "Docker for Macを開いています..."  open -a /Applications/Docker.app  while ! docker system info > /dev/null 2>&1; do sleep 1; done  echo "Docker準備OK!"else  echo "Dockerは実行中です"fi
  • 次に、コンテナのシェルにログインするために、プロジェクトでdcr runnerを実行します(dcrdocker-compose runのエイリアス)。つまりdcr runnerは以下のエイリアスになります。
$ docker-compose run --rm runner
  • 後はこのコンテナの中でほとんどの作業を行います(テストやマイグレーションやrakeタスクなど何でも構いません)。

以上でおわかりのように、私は何かタスクを1つ実行する必要が生じるたびにいちいちコンテナを1つ立ち上げたりせず、いつも同じ設定でやっています。

つまり私は、なつかしのvagrant sshと同じ感覚でdcr runnerを使っているのです。

私がこれをshellと呼ばずにrunnerと呼んでいる理由はただひとつ、コンテナの中で任意のコマンドをrunするのにも使えるからです。

メモ: このサービスをrunnerと呼ぶかどうかは好みの問題であり、(デフォルトのcommand/bin/bash)は別としても)webサービスと比べて何ひとつ目新しい点はありません。つまり、docker-compose run runnerdocker-compose run web /bin/bashと完全に同じです(ただし短い😉)。

🔗 ヘルスチェック

Railsでdb:migrateなどのコマンドを実行するときには、DBが起動していてコネクションを受け付けられる状態にしておきたいものです。依存するサービスが起動するまで待つようにDocker Composeに指示したい場合は、healthcheckが使えます。

既にお気づきかと思いますが、depends_on定義に書かれているのは単なるサービスリストではありません。

backend:  # ...  depends_on:    postgres:      condition: service_healthy    redis:      condition: service_healthypostgres:  # ...  healthcheck:    test: pg_isready -U postgres -h 127.0.0.1    interval: 5sredis:  # ...  healthcheck:    test: redis-cli ping    interval: 1s    timeout: 3s    retries: 30

メモ: ヘルスチェックはDocker Composeファイルフォーマットv2.1以降でのみサポートされています。私たちがヘルスチェックを開発環境でしか使っていない理由はこれです。

🔗 おまけ: Evil Martians特製のdip.ymlについて

訳注

dipについては以下の記事もどうぞ。

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

Docker Compose式のやり方がまだ難しいとお思いの方に、Dipというツールをご紹介します。これは開発者がスムーズなエクスペリエンスを得られるようにと、Evil Martiansのあるメンバーがこしらえたものです。

dip.ymlは、複数のcomposeファイルを使い分ける場合や、プラットフォームに依存する複数の設定を使い分ける場合に特に便利です。dip.ymlはそれらをまとめて、Dockerでの開発環境を管理する一般的なインターフェイスを提供できるからです。

dip.ymlについては別記事にて詳しく説明しようと思います。どうぞご期待ください!

追伸

本記事のtipsを共有してくれたSergey PonomarevMikhail Merkushinに感謝いたします🤘。

元記事のトップ画像のクレジット: © NASA/JPL-Caltech, 2009

🔗 原文Changelog

1.1.3 (2021-03-30)
minimagic gemのライセンス問題緩和のためDockerfileを更新(#35
docker-compose設定の環境変数を辞書形式に変更(#6
1.1.2 (2021-02-26)
依存関係のバージョンを更新(#28
Aptfileにコメントを書けるようになった(#31
Dockerfile内のAptfileへのパスを修正(#33
1.1.1 (2020-09-15)
.dockerdevディレクトリをプロジェクトディレクトリではなくビルドコンテキストとして使用(#26
1.1.0 (2019-12-10)
Rubyのベースイメージをslimに変更
Rubyバージョン用のDebianリリースを明示的に指定し、busterにアップグレード
bundlerのパスを/bundleからbundler標準の/usr/local/bundleに変更
Docker Composeのファイル形式はv2.4を使用
postgresサービスとredisサービスにヘルスチェックを追加

訳注

以下のスライドも合わせて読むことで、より理解が進むと思います。

関連記事

Rails 6のB面に隠れている地味にうれしい機能たち(翻訳)


この記事を書いた人

hachi8833

X:@hachi8833GitHub:@hachi8833コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。これまでにRuby on Rails チュートリアル第2版のコンテンツ監修、Railsガイドのコンテンツ作成を担当。かと思うと、正規表現の粋を尽くした日本語エラーチェックサービスenno.jpを運営。Claude Codeに夢中になりすぎないための方法を模索中。ブログ:note.com/hachi8833Amazonウィッシュリスト:https://bit.ly/32aAmiI

hachi8833の書いた記事一覧へ

本記事の内容へのお問い合せはTwitterで@techrachoへMentionまたはDMにてご連絡頂くか、運営会社であるBPS株式会社のお問い合せフォームよりお問い合せ下さい。

Our Services

各種サービスのご依頼やお問い合わせなど、お気軽にご相談ください。

Our Products

製品のご利用希望や疑問・質問など、お気軽にご相談ください。

Recruit & Contacts

お問い合わせ、採用へのお申し込みはこちらから。

積極採用中 開発エンジニアCONTACT

関連記事

CONTACT

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

[8]ページ先頭

©2009-2025 Movatter.jp