訳注
Ruby on Whalesは、Ruby on Railsの他に、もしかすると「Boy on a Dolphin」にかけているのかもしれないと思いました。Whales(クジラ)はもちろんDockerのシンボルです。
クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(更新翻訳)
本記事では、著者がRuby on Railsプロジェクトの開発に用いているDockerの設定を紹介いたします。この設定はEvil Martiansのproduction development環境で生まれ、さらに進化を遂げたものです。本記事の内容の利用や共有はご自由にどうぞ。存分に楽しみましょう!
原文お知らせ
英語版記事は最新の推奨事項に合わせて更新を繰り返しています。詳しくは本記事末尾のChangelogをご覧ください(参考: 原文Changelog)。
さて、どこからお話を始めましょうか。ここに到達するまでに長い長い旅路をたどりました。かつて私は開発にVagrantを使っていましたが、当時のVMは私の4GB RAMのノートPCでは少々重すぎました。そして2017年にコンテナへの乗り換えを決意したときに、やっとDockerを使い始めました。
しかしDockerで問題がたちまち解決したという気持ちではありません。自分自身やチーム、そしてすべての人々にとって完璧な設定を追求し続けてきましたが、「これでよし」と言える究極の設定はありません。標準的なアプローチを見出すまでにかなりの時間を要しました(2019年に本記事を最初に公開した時点でも相当の時間を費やしていました)。
本記事を最初に公開して私の秘密を隅々までオープンにして以来、多くのRailsチームや開発者が私の手法を採用し、さらに改良や貢献にもご協力をいただきました。
前置きはこのぐらいにして、いよいよ設定そのものをご覧に入れたいと思います。設定のほぼすべての行に解説を付けています(「Dockerをわかっている」前提のわかりにくいチュートリアルにしたくなかったので)。
本記事は元々RailsConf 2019『“Terraforming legacy Rails applications”』での私の発表に合わせて公開されました。
本記事のソースコードは、GitHubのevilmartians/ruby-on-whalesでご覧いただけます。
その前に、この設定例では以下のような最新バージョンのソフトウェアを使っている点にご留意ください。
- Docker Desktop 20.10以降
- Docker Compose v2
- Ruby 3.1.0
- PostgreSQL 14
- etc.
本記事の大半はコメント付きコードと設定例で構成されており、以下のようになっています。
- 基本: Dockerfileとdocker-compose.ymlについて
- Dipツールの紹介
- 開発における(マイクロ)サービス vs Docker
- development環境からproduction環境へ
- 対話的ジェネレータ「Ruby on Whales」の紹介
🔗 基本的な設定
🔗 Dockerfile
Dockerfileは、Rubyアプリケーションの環境を定義します。この環境でサーバーを実行したり、rails c
でコンソールを実行したり、テストやrakeタスクを走らせたり、その他にも開発者としてあらゆる形でのコードとのやりとりを行います。
# syntax=docker/dockerfile:1
ARG RUBY_VERSION
ARG DISTRO_NAME=bullseye
FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME
ARG DISTRO_NAME
# 共通の依存関係
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \
apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
build-essential \
gnupg2 \
curl \
less \
git
# PostgreSQLの依存関係をインストール
ARG PG_MAJOR
RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
gpg --dearmor -o /usr/share/keyrings/postgres-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgres-archive-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/" \
$DISTRO_NAME-pgdg main $PG_MAJOR | tee /etc/apt/sources.list.d/postgres.list > /dev/null
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
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をインストール
ARG NODE_MAJOR
ARG YARN_VERSION=latest
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
apt-get update && \
apt-get install -y curl software-properties-common && \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \
echo "deb https://deb.nodesource.com/node_${NODE_MAJOR}.x $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/nodesource.list && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends nodejs
RUN npm install -g yarn@$YARN_VERSION
# アプリケーションの依存関係をインストール
# 外部のAptfileを利用して行う(後述)
COPY Aptfile /tmp/Aptfile
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
$(grep -Ev '^\s*#' /tmp/Aptfile | xargs)
# Bundlerの設定
ENV LANG=C.UTF-8 \
BUNDLE_JOBS=4 \
BUNDLE_RETRY=3
# Bundlerの設定をプロジェクトのルートフォルダに保存する
ENV BUNDLE_APP_CONFIG=.bundle
# `bin/`や`bundle exec`をプレフィックスせずに
# binstubを実行したい場合は以下のコメントを解除する
# ENV PATH /app/bin:$PATH
# RubyGemをアップグレードして最新のBundlerをインストールする
RUN gem update --system && \
gem install bundler
# アプリケーションコード用のディレクトリを作成する
RUN mkdir -p /app
WORKDIR /app
# ポート3000を公開することを明示する
EXPOSE 3000
# デフォルトコマンドをBashにする
CMD ["/bin/bash"]
この設定には必要なものしか含まれていないので、これを土台にできます。設定内容をもう少し詳しく解説します。
以下の3行は少々見慣れないかもしれません。
ARG RUBY_VERSION
ARG DISTRO_NAME=bullseye
FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME
いつものようにFROM ruby:3.1.0
などでその時点の安定版Rubyを指定すればよさそうなものなのに、そうしない理由がおわかりでしょうか?その答えは、Dockerfileを一種のテンプレートとして利用し、環境をDockerfileの外部で設定できるようにしたいからです。
- 実行時における依存関係の正確なバージョンはdocker-compose.ymlで指定します(後述👇)。
apt
コマンドで追加インストール可能な依存関係のリストは別ファイルに保存します(これも後述👇👇)。
さらに、Debianのリリースもパラメータで指定可能にし(デフォルトはbullseye
)、PostgreSQLなどの他の依存関係と整合する正しい取得元を確実に追加するようにしています。
さて、FROM
ステートメントでは以下のように引数を再度宣言していることにご注目ください。
FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME
ARG DISTRO_NAME
ここがDockerfileのしくみの厄介な点で、FROM
以降では引数がリセットされてしまうので再宣言が必要になります。詳しくはmobyのissue #34129を参照してください。
上に続くファイルの残りの部分で、実際のビルド手順が記述されています。ここではコンテナサイズを小さくするためにslimベースのDockerイメージを使っているので、最初に一般的なシステム依存関係(gitやcurlなど)をいくつか手動でインストールしておく必要があります。
# 共通の依存関係
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
build-essential \
gnupg2 \
curl \
less \
git
システムの依存関係のインストールについて詳しくは、この後アプリケーション固有の依存関係で合わせて説明します。
apt
コマンドでPostgreSQLやNodeJSをインストールするには、それらのdebパッケージをソース一覧に追加しておく必要があります。
以下はPostgreSQLのインストールです(公式ドキュメントに基づいています)。
ARG PG_MAJOR
RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
gpg --dearmor -o /usr/share/keyrings/postgres-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgres-archive-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/" \
$DISTRO_NAME-pgdg main $PG_MAJOR | tee /etc/apt/sources.list.d/postgres.list > /dev/null
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
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
このDockerfileはDocker Composeなしでの利用を想定していないので、PG_MAJOR
引数にはデフォルト値を指定していません(後述のNODE_MAJOR
やYARN_VERSION
も同様)。
また、上のコードではDockerfileの冒頭で定義したDISTRO_NAME
引数が再利用されている点にもご注目ください。
さらに、ここでもapt-get ... apt-get clean
という呪文を再度唱えます。そうする理由は、環境の主要な部分が独立した形で構築されるようにしたいからです(こうしておくと、アップグレード時にDockerのキャッシュレイヤがよく効くようになります)。
以下はNodeJSのインストールです(NodeSourceリポジトリより)。
ARG NODE_MAJOR
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
apt-get update && \
apt-get install -y curl software-properties-common && \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \
echo "deb https://deb.nodesource.com/node_${NODE_MAJOR}.x $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/nodesource.list && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends nodejs
続いてYarnをNPMでインストールします。
ARG YARN_VERSION
RUN npm install -g yarn@$YARN_VERSION
NodeJSとYarnを最初の時点でDockerfile経由でインストールしている理由がおわかりでしょうか?Rails 7ではimport mapsや(プリコンパイル済みバイナリを用いる)tailwindcss-railsを用いることで、NodeJSなしのセットアップが使えるようになりますが、ここでNodeJSとYarnを追加しておけば、従来のレガシーパイプラインのサポートや最新のWebpackerオルタナティブを追加するという選択肢を増やせます。
お次はアプリケーション固有の依存関係をインストールします。
COPY Aptfile /tmp/Aptfile
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
$(grep -Ev '^\s*#' /tmp/Aptfile | xargs)
Aptfileを使った小技について少し補足しておきます。
COPY Aptfile /tmp/Aptfile
RUN apt-get install\
$(grep -Ev '^\s*#' /tmp/Aptfile | xargs)
このアイデアは、Herokuに追加パッケージをインストールできるheroku-buildpack-aptから拝借しました。このビルドパックを使うと、ローカル環境とproduction環境で同じAptfileを再利用することも可能になります。
デフォルトのAptfileには以下のパッケージが1個あるだけです(Railsのcredentialを編集するときにVimを使えるようにするため)。
vim
なお、著者が以前担当したプロジェクトでは、LaTeXとTexLiveでPDFを生成していました。この場合のAptfileは以下のような感じになるでしょう。
vim
# TeXパッケージ
texlive
texlive-latex-recommended
texlive-fonts-recommended
texlive-lang-cyrillic
このように依存関係のリストをAptfileに切り出しておくことで、タスク固有の依存関係を別ファイルに保存できるようになり、Dockerfileの普遍性が高まります。
DEBIAN_FRONTEND=noninteractive
という記述についてはAsk Ubuntuの回答をご覧いただければと思います。
--no-install-recommends
オプションを指定して推奨パッケージのインストールを無効にしておくと容量を節約でき、Dockerイメージのサイズも小さくなります。詳しくは以下の記事をご覧ください。
参考: Save disk space with apt-get option "no-install-recommends" in Xubuntu
どのRUN
ステートメントの冒頭部分もかなり謎な感じに見えますが、これも目的は同じく容量の節約で、取得したパッケージ ファイルのローカルリポジトリを、ビルドとビルドの間に保存されるキャッシュに移動するためのものです。この特定のDockerレイヤにゴミが残らないようにするには、パッケージをインストールするすべてのRUN
ステートメントにこのマジックを含めておく必要があります。これにより、Dockerイメージのビルドも大幅に高速化されます!
RUN --mount
は Dockerの比較的新しい機能です。以前だったら、パッケージのインストール手順には必ずと言ってよいほど、一時ファイルをクリーンアップするための rm
コマンドやtruncate
コマンドだらけの闇の呪文が追加されていたものです。
Dockerfileの残りの部分は、ほぼBundlerのためです。
# Bundlerの設定
ENV LANG=C.UTF-8\
BUNDLE_JOBS=4\
BUNDLE_RETRY=3
# Bundlerの設定をプロジェクトのルートフォルダに保存する
ENV BUNDLE_APP_CONFIG=.bundle
# `bin/`や`bundle exec`をプレフィックスせずに
# binstubを実行したい場合は以下のコメントを解除する
# ENV PATH /app/bin:$PATH
# RubyGemをアップグレードして最新のBundlerをインストールする
RUN gem update --system &&\
gem install bundler
LANG=C.UTF-8
でデフォルトのロケールをUTF-8に設定しています。これは気分を損なわないための設定です。これを指定しておかないとRubyの文字列がUS-ASCIiになってしまい、いとしい絵文字たちとサヨナラしなければならなくなります👋
プロジェクト固有のBundler設定(社内gemで使うcredentialなど)を<root>/.bundle
フォルダに保存するのであれば、BUNDLE_APP_CONFIG
の設定が必要です。Docker Hubにある公式Rubyイメージにはデフォルトでこの変数が定義されているので、Bundlerの設定がローカル設定にフォールバックせずに済みます。
オプションとして、<root>/bin
フォルダをPATH
に追加すればbundle exec
をプレフィックスせずにコマンドを実行できるようになります。私たちはこの振る舞いをデフォルトでは設定していません。理由は、マルチプロジェクト環境で壊れる可能性があるからです(Railsアプリでローカルなgemやエンジンが使われている場合など)。
従来はBundlerのバージョンも指定する必要がありました(システムで拾われるように若干のハックを利用します)。ありがたいことに、Bundler 2.3.0以降はGemfile.lockのBUNDLED_WITH
で定義されているのと同じバージョンのBundlerをわざわざ手動でインストールする必要がなくなりました。コンフリクトの回避はBundlerが代わりにやってくれます(#4076)。
🔗 compose.yml
- リポジトリ: compose.yml
訳注
Docker Compose V2から、compose.ymlがデフォルトのファイル名になりました。なおcompose-specでは従来のdocker-compose.ymlもサポートすることとなっています。
参考: compose-spec/spec.md at master · compose-spec/compose-spec
Docker Composeは、コンテナ環境のオーケストレーションに利用できるツールです。コンテナ同士を連携させることも、永続的なボリュームやサービスを定義することも可能です。
以下は、データベースにPostgreSQL、バックグラウンドジョブにSidekiqを用いる典型的なRailsアプリケーションを開発するときのComposeファイルです。
x-app: &app
build:
context: .
args:
RUBY_VERSION: '3.1.0'
PG_MAJOR: '14'
NODE_MAJOR: '16'
YARN_VERSION: '1.22.17'
image: example-dev:1.0.0
environment: &env
NODE_ENV: ${NODE_ENV:-development}
RAILS_ENV: ${RAILS_ENV:-development}
tmpfs:
- /tmp
- /app/tmp/pids
x-backend: &backend
<<: *app
stdin_open: true
tty: true
volumes:
- ..:/app:cached
- bundle:/usr/local/bundle
- rails_cache:/app/tmp/caches
- node_modules:/app/node_modules
- packs:/app/public/packs
- packs-test:/app/public/packs-test
- history:/usr/local/hist
- ./.psqlrc:/root/.psqlrc:ro
- ./.bashrc:/root/.bashrc:ro
environment: &backend_environment
<<: *env
REDIS_URL: redis://redis:6379/
DATABASE_URL: postgres://postgres:postgres@postgres:5432
WEBPACKER_DEV_SERVER_HOST: webpacker
MALLOC_ARENA_MAX: 2
WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}
BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap
XDG_DATA_HOME: /app/tmp/caches
YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
HISTFILE: /usr/local/hist/.bash_history
PSQL_HISTFILE: /usr/local/hist/.psql_history
IRB_HISTFILE: /usr/local/hist/.irb_history
EDITOR: vi
depends_on: &backend_depends_on
postgres:
condition: service_healthy
redis:
condition: service_healthy
services:
rails:
<<: *backend
command: bundle exec rails
web:
<<: *backend
command: bundle exec rails server -b 0.0.0.0
ports:
- '3000:3000'
depends_on:
webpacker:
condition: service_started
sidekiq:
condition: service_started
sidekiq:
<<: *backend
command: bundle exec sidekiq -C config/sidekiq.yml
postgres:
image: postgres:14
volumes:
- .psqlrc:/root/.psqlrc:ro
- postgres:/var/lib/postgresql/data
- history:/user/local/hist
environment:
PSQL_HISTFILE: /user/local/hist/.psql_history
POSTGRES_PASSWORD: postgres
ports:
- 5432
healthcheck:
test: pg_isready -U postgres -h 127.0.0.1
interval: 5s
redis:
image: redis:6.2-alpine
volumes:
- redis:/data
ports:
- 6379
healthcheck:
test: redis-cli ping
interval: 1s
timeout: 3s
retries: 30
webpacker:
<<: *app
command: bundle exec ./bin/webpack-dev-server
ports:
- '3035:3035'
volumes:
- ..:/app:cached
- bundle:/usr/local/bundle
- node_modules:/app/node_modules
- packs:/app/public/packs
- packs-test:/app/public/packs-test
environment:
<<: *env
WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
volumes:
bundle:
node_modules:
history:
rails_cache:
postgres:
redis:
packs:
packs-test:
このComposeファイルでは、6つのサービスと2つの拡張フィールド(x-app
とx-backend
)を定義しています。拡張フィールドを使うと、設定内で共通で利用できる部分を定義できます。これらの拡張フィールドにYAMLアンカーをアタッチすると、後からComposeファイル内のどの部分にでも埋め込めるようになります。
原注
本記事では、最終的にDocker Composeを使うこともdocker compose up
を実行することもありません。代わりに後述のDipというツールを使うので、このcompose.ymlファイルは単なる「サービスレジストリ」としてのみ振る舞うことになります。もうひとつ重要な点は、このcompose.ymlは(通常と異なり).dockerdev/
フォルダの下に置くようにしていることです。これが、このcompose.ymlファイルでソースコードを.:/app
ではなく..:/app
でマウントしている理由です。Dipツールを使わない設定(本記事ではおすすめしません)を検討する場合は、どうかこの点をお忘れなく。
以上を踏まえたうえで、各サービスを徹底的に見ていくことにしましょう。
🔗 x-app
サービス
x-app
拡張の主な目的は、上記のDockerfileで定義されているアプリケーションコンテナをビルドするのに必要なすべての情報を提供することです。
x-app: &app
build:
context: .
args:
RUBY_VERSION: '3.1.0'
PG_MAJOR: '14'
NODE_MAJOR: '16'
YARN_VERSION: '1.22.17'
さて、このcontext
とは何でしょうか?context
ディレクトリは、Dockerで使うビルドコンテキストを定義するもので、ビルドプロセス(COPY
コマンドを実行するなど)で使われる一種のワーキングディレクトリのようなものです。
context
ディレクトリは、イメージをビルドするたびにパッケージ化されてDockerデーモンに送信されるので、なるべく小さく保つ方がよいでしょう。私たちのコンテキストには.dockerdev
ディレクトリしかないので、これについては問題ありません。
上述したように、次はDockerfileで宣言されているargs
を用いて、依存関係のバージョンを正確に指定します。
イメージにタグ付けする方法にもぜひご注目ください。
image: example-dev:1.0.0
Dockerを開発に使うメリットのひとつは、設定を変更すればチーム内で自動的に同期できることです。つまり、上の1.0.0
のようなローカルイメージのバージョン番号は、イメージそのもの(またはイメージが依存する引数)を更新したときだけ更新が必要です。よくあるexample-dev:latest
のようなうかつな設定は自分の足を撃ち抜くようなものなので、くれぐれも使わないでください。
イメージのバージョン番号をタグで指定して管理しておくことで、異なる環境で作業するときも余分な作業を強いられずに済むようになります。たとえば"chore/upgrade-to-ruby-3"ブランチで長時間作業している最中でも、イメージのバージョン番号を適切に更新しておけば、いつでもmain
ブランチにさっと切り替えて古いイメージやRubyを使えるようになります。これならリビルドは一切不要です。
経験則: Dockerfileやその引数を更新する場合(依存関係を更新する場合など)は、必ずその都度イメージタグのバージョン番号を更新すること。
次に、複数のサービス(RailsとWebpackerなど)で共有される環境変数を追加します。
environment: &env
NODE_ENV: ${NODE_ENV:-development}
RAILS_ENV: ${RAILS_ENV:-development}
ここにはさまざまな工夫が盛り込まれていますが、その中で X=${X:-何ちゃら}
という構文に注目したいと思います。
これは「コンテナ内の変数X
について、ホストマシンに環境変数X
が存在すればその値を使い、存在しない場合は指定の値を使う」という意味です。これを活用すれば、RAILS_ENV=test docker-compose up rails
のようにコマンドで別の環境を指定してサービスを実行できるようになります。
environment
フィールドでは値をリスト形式(- NODE_ENV=xxx
)ではなく、辞書形式(NODE_ENV: xxx
)で記述している点にもご注意ください。この書式にすることで、共通設定を使い回せるようになります(後述)。
また、コンテナ内の/tmp
(およびアプリケーションのtmp/pids
)にtmpfsを利用するようDockerに指示しています。こうすることで、コンテナ終了時にserver.pid
が残らなくなり、いまいましい"A server is already running"エラーとおさらばできます。
tmpfs:
- /tmp
- /app/tmp/pids
🔗 x-backend
サービス
いよいよ本記事で一番オイシイ部分にたどり着きました。
x-backend
サービスは、すべてのRubyサービスに共通する振る舞いを定義します。
まずボリュームについて見ていきましょう。
x-backend: &backend
<<: *app
stdin_open: true
tty: true
volumes:
- ..:/app:cached
- rails_cache:/app/tmp/caches
- bundle:/usr/local/bundle
- history:/usr/local/hist
- node_modules:/app/node_modules
- packs:/app/public/packs
- packs-test:/app/public/packs-test
- ./.psqlrc:/root/.psqlrc:ro
- ./.bashrc:/root/.bashrc:ro
- ./.pryrc:/root/.pryrc:ro
environment: &backend_environment
<<: *env
REDIS_URL: redis://redis:6379/
DATABASE_URL: postgres://postgres:postgres@postgres:5432
WEBPACKER_DEV_SERVER_HOST: webpacker
MALLOC_ARENA_MAX: 2
WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}
BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap
XDG_DATA_HOME: /app/tmp/caches
YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
HISTFILE: /usr/local/hist/.bash_history
PSQL_HISTFILE: /usr/local/hist/.psql_history
IRB_HISTFILE: /usr/local/hist/.irb_history
EDITOR: vi
depends_on: &backend_depends_on
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ..:/app:cached
- bundle:/usr/local/bundle
- rails_cache:/app/tmp/caches
- node_modules:/app/node_modules
- packs:/app/public/packs
- packs-test:/app/public/packs-test
- history:/usr/local/hist
- ./.psqlrc:/root/.psqlrc:ro
- ./.bashrc:/root/.bashrc:ro
さて、ボリュームリストの冒頭にある項目..:/app:cached
では、cached
戦略を用いてコンテナ内の/app
フォルダにプロジェクトディレクトリをマウントしています。このcached
は、macOSでDocker開発を効率化するときの鍵でした。
「え、『でした』ってどういうこと?」
そう、そんな時代もありました。このcached
指定は、gRPC FUSE同期がリリースされて以来不要になりました(#5402)。
しかし、私は以下の2つの理由からcached
を当面残しておくつもりです。
- Docker Desktopのバージョンが古いメンバーがチーム内にいる可能性がある。
- いくつかベンチマークを走らせてみると、古いosxfsファイル共有を使うとパフォーマンスがよくなることがわかった(ただし
:cached
を指定した場合のみ)。
そういうわけで、Dockerが最新であっても「Preference」の「Use gRPC FUSE for file sharing」をオフにしておく意味がある可能性もあります。
Dockerチームは、macOS上でDockerがより高速に動作するよう努力を重ねています。最新リリース(4.6.0以降)では、VirtioFSによるディレクトリ共有の高速化と、virtualization.framework
のサポートが提供されるようになりました。この2つはDocker Desktopの「Preference」の「Experimental Features」タブで設定できます。この設定を有効にしたときのパフォーマンス改善には目をみはることでしょう(おそらく通常操作が最大で2倍は速くなります↓)
Did some benchmarks, and found that VirtioFS makes routine Rails tasks ~2x faster—:not_bad https://t.co/Zy44n9k676 pic.twitter.com/JxTR00xohm
— Vladimir Dementyev (@palkan_tula) March 17, 2022
その次の行のbundle:/usr/local/bundle
では、/usr/local/bundle
ディレクトリの内容をbundle
というボリュームに保存するようコンテナに指示しています(gemはデフォルトでここに保存されます)。こうすることで、gemデータが永続化されてどの実行でも使われるようになります。compose.yml
で定義したすべてのボリュームは、compose down --volumes
を実行するまで維持されます。
以下の行は、「DockerがMacで遅い」という呪いをお祓いするためにわざわざ配置されています。生成されたファイルをすべてDockerボリュームに保存して、ホストマシンのディスク操作が重くならないようにしています。
- rails_cache:/app/tmp/caches
- node_modules:/app/node_modules
- packs:/app/public/packs
- packs-test:/app/public/packs-test
DockerをmacOS上で十分高速化するには、次の2つのルールを守ること。ソースファイルをマウントするときは:cached
を指定し(gRPC FUSEを使わない場合)、生成されたコンテンツ(アセットやバンドルなど)はボリュームに保存すること。
原注
SprocketsやPropshaftを使う場合は、専用ボリューム(assets:/app/public/assets
)を追加してそこにアセットを保存するのをお忘れなく。tailwindcss-railsの場合はassets_builds:/app/assets/builds
のように追加します。
次は、さまざまなコマンドラインツールの設定ファイルや履歴を永続化するボリュームをマウントします。
- history:/usr/local/hist
- ./.psqlrc:/root/.psqlrc:ro
- ./.bashrc:/root/.bashrc:ro
Rubyコンテナなのにpsql
がある理由は、rails dbconsole
の内部で使われるからです。
私たちの.psqlrcファイルには以下の仕掛けがあり、履歴ファイルのパスを環境変数で指定できるようになっています。このおかげで、履歴ファイルのパスにPSQL_HISTFILE
環境変数が使われ、環境変数が指定されていない場合はデフォルトの$HOME/.psql_history
が使われます。
\set HISTFILE `[[ -z $PSQL_HISTFILE ]] && echo $HOME/.psql_history || echo $PSQL_HISTFILE`
.bashrc
ファイルには、コンテナ内でターミナルをカスタマイズする設定を以下のように追加できます。
alias be="bundle exec"
それでは、環境変数について解説します。
environment: &backend_environment
<<: *env
# ----
# サービスの検出
# ----
REDIS_URL: redis://redis:6379/
DATABASE_URL: postgres://postgres:postgres@postgres:5432
WEBPACKER_DEV_SERVER_HOST: webpacker
# ----
# アプリケーションの設定
# ----
MALLOC_ARENA_MAX: 2
WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}
# -----
# キャッシュ
# -----
BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap
# この環境変数はRuboCopなどののツールでキャッシュの保存に使われる
XDG_DATA_HOME: /app/tmp/cache
# マウントしたボリュームyにYarnのキャッシュを保存して高速化を図る
YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
# ----
# 開発ツール
# ----
HISTFILE: /usr/local/hist/.bash_history
PSQL_HISTFILE: /usr/local/hist/.psql_history
IRB_HISTFILE: /usr/local/hist/.irb_history
EDITOR: vi
冒頭では、<<: *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では明示的な設定が必要です。
Rubyのメモリ関連の裏技については以下をご覧ください。
次のグループには、アプリケーションを対象とする設定の一部が含まれています。たとえば、MALLOC_ARENA_MAX
やWEB_CONCURRENCY
はRubyのメモリの扱いをチェックするのに便利です。
また、Dockerボリュームにキャッシュを保存する変数(BOOTSNAP_CACHE_DIR
、XDG_DATA_HOME
、YARN_CACHE_FOLDER
)も用意してあります。
bootsnapはアプリケーションの読み込み時間を短縮するのに使われます。bootsnapのキャッシュはBundlerのデータと同じボリュームに保存しています。このキャッシュにはもっぱらgemのデータが保存されるので、Bundlerのボリュームを削除するとき(Rubyのバージョンアップ時など)にキャッシュも確実にリセットされるようにするためです。
変数の最後のグループは、開発のエクスペリエンスを向上させるためのものです。中でも重要なのはHISTFILE: /usr/local/hist/.bash_history
です。Bashの履歴の保存場所を指定して、履歴が消えないようにします。PSQL_HISTFILE
やIRB_HISTFILE
も同様です。
原注
IRBの履歴を指定の場所に保存するには、IRB設定ファイル(.irbrc)に以下を記述する必要があります。
IRB.conf[:HISTORY_FILE] = ENV["IRB_HISTFILE"] if ENV["IRB_HISTFILE"]
最後のEDITOR: vi
は、rails credentials:edit
コマンドでcredentialファイルを管理するときなどに使われます。
これで、x-backend
の説明は以下を残すだけとなりました。
stdin_open: true
tty: true
上を指定すると、このサービスがインタラクティブになります。つまりTTYが提供されます。これは、コンテナにログインしてRailsコンソールやBashを実行するのに必要です。
これは、-it
オプションを指定してDockerコンテナを実行するのと同じです。
🔗 rails
サービス
rails
はデフォルトのバックエンドサービスです。このサーバーは、実行するコマンドだけを上書きします。
rails:
<<: *backend
command: bundle exec rails
rails
サービスは、開発で必要なすべてのコマンド(rails db:migrate
やrspec
など)を実行するためのものです。
🔗 web
サービス
web
サービスはWebサーバーの起動用です。ここでは、公開するポート番号や、アプリの実行に必要な依存関係を定義します。
🔗 webpacker
サービス
ここではWEBPACKER_DEV_SERVER_HOST: 0.0.0.0
についてだけ説明しておきたいと思います。これを指定することで、webpack-dev-serverが外部からアクセス可能になります(デフォルトではlocalhost
で実行されますが、この場合は外部からアクセスできません)。
🔗 ヘルスチェック
Railsでdb:migrate
などのコマンドを実行するときは、データベースが確実に起動していてコネクションが受け付け可能であることを確認しておきたいと思います。ヘルスチェック機能を利用すれば、依存するサービスの準備が整うまで待機するようDocker Composeに指示できます。
そろそろ皆さんも、設定にあるdepends_on
の定義がサービスの単なるリストではないことにお気づきかもしれませんね。
backend:
# ...
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
postgres:
# ...
healthcheck:
test: pg_isready -U postgres -h 127.0.0.1
interval: 5s
redis:
# ...
healthcheck:
test: redis-cli ping
interval: 1s
timeout: 3s
retries: 30
🔗 Dipツールを導入する
Docker Composeの操作が面倒に感じられるのであれば、開発者のエクスペリエンスを快適にするDipという強い味方があります(Evil Martiansの同僚が開発しました)。
Dipはdocker compose
コマンドの薄いラッパーで、従来のインフラ指向のフローから開発指向のフローに乗り換えられます。Dipを使うと以下のようなメリットを得られます。
- アプリケーション固有の対話型コマンドやサブコマンドを定義する機能
- 開発環境をゼロから一発で構築できる
dip provision
フロー - 複数の
compose.yml
ファイルをサポート(OS固有の設定も含む)
以下の2つのコマンドを実行するだけで、ローカル環境にDipを導入して即座にアプリで作業を開始できるようになります。
# Dockerイメージがなければビルドし、追加コマンドも実行する
$ dip provision
# 定義済みの依存関係を用いてRailsサーバーを起動する
$ dip rails s
=> Booting Puma
=> Rails 7.0.2.2 application starting in development
=> Run `bin/rails server --help` for more startup options
[1] Puma starting in cluster mode...
...
[1] - Worker 0 (PID: 9) booted in 0.0s, phase: 0
以下は典型的なdip.ymlファイルの設定例です。
version: '7.1'
# Docker Composeに渡すデフォルトの環境変数を定義する
environment:
RAILS_ENV: development
compose:
files:
- .dockerdev/compose.yml
project_name: example_demo
interaction:
# 必要な依存関係(データベースなど)とともに
# Railsコンテナを起動し、コンテナ内でターミナルを開く
runner:
description: Open a Bash shell within a Rails container (with dependencies up)
service: rails
command: /bin/bash
# 依存するサービスを起動せずにRailsコンテナを実行する
# (Railsと無関係のスクリプトの実行に便利)
bash:
description: Run an arbitrary script within a container (or open a shell without deps)
service: rails
command: /bin/bash
compose_run_options: [ no-deps ]
# Bundlerコマンドのショートカット
bundle:
description: Run Bundler commands
service: rails
command: bundle
compose_run_options: [ no-deps ]
# RSpecを実行するショートカット(RAILS_ENVを上書きする)
rspec:
description: Run RSpec commands
service: rails
environment:
RAILS_ENV: test
command: bundle exec rspec
rails:
description: Run Rails commands
service: rails
command: bundle exec rails
subcommands:
s:
description: Run Rails server at http://localhost:3000
service: web
compose:
run_options: [service-ports, use-aliases]
yarn:
description: Run Yarn commands
service: rails
command: yarn
compose_run_options: [ no-deps ]
psql:
description: Run Postgres psql console
service: postgres
default_args: anycasts_dev
command: psql -h postgres -U postgres
'redis-cli':
description: Run Redis console
service: redis
command: redis-cli -h redis
provision:
- dip compose down --volumes
- dip compose up -d postgres redis
- dip bash -c bin/setup
いくつかの設定について詳しく説明します。
最初はcompose
セクションについてです。
compose:
files:
- .dockerdev/compose.yml
project_name: example_demo
ここでは私たちのDocker Composeの設定ファイル(.dockerdev/compose.yml
)へのパスを指定します。これで、プロジェクトのルートディレクトリでdip
を実行すれば正しい設定が読み込まれます。
project_name
は重要です。これを指定しておかないと、compose.ymlファイルが置かれているフォルダ(ここでは.dockerdev)がプロジェクト名として認識されてしまい、複数のプロジェクト同士がコンフリクトする可能性があります。
rails
コマンドにも注目したい点がいくつかあります。
rails:
description: Run Rails commands
service: rails
command: bundle exec rails
subcommands:
s:
description: Run Rails server at http://localhost:3000
service: web
compose:
run_options: [service-ports, use-aliases]
デフォルトでは、dip rails
を実行するとRailsコンテナ内でbundle exec rails
が実行されます。しかしここではDipのサブコマンド機能を使ってdip rails s
を別扱いにしています。
- ここでは
rails
サービスではなくweb
サービスを使う(つまり依存関係も起動する) - サービスのポートを公開する(ここでは
3000
) - ネットワークエイリアスも有効にして、他のサービスが
web
というホスト名でコンテナにアクセスできるようにする
dip rails s
の背後では、以下のDocker Composeコマンドを生成します。
docker compose run --rm --service-ports --use-aliases web
上ではup
ではなくrun
が使われている点にご注目ください。run
にすることで、サーバーがターミナルからアクセス可能になります。つまり、たとえばデバッガをアタッチしても問題なく利用できるということです(up
コマンドではターミナルがインタラクティブになりません)。
🔗 対話的なプロビジョニング
Dockerイメージをビルドしてデータベースをセットアップしただけで開発を始められるアプリケーションはめったにありません。ほとんどの場合、その他にも何らかの秘密鍵やcredentialや.env
ファイルなどが必要になります。私たちはDipツールでインタラクティブなデプロイを実現し、プロジェクトの新メンバーが細かい追加設定であくせくせずに短時間で環境構築を完了することに成功しました。
設定を適切に管理する方法を学ぶには、以下の「Terraforming Railsシリーズ」記事をご覧ください。
たとえば、.env.development.local
ファイルに秘密情報をいくつか保存して、Sidekiq Proなどのプライベートレジストリからgemをダウンロードするように設定しなければならないとします。こんなとき、私なら以下のようなプロビジョニングスクリプトを書くでしょう。
# 単独でも実行できるように、このコマンドを切り出してある
configure_bundler:
command: |
(test -f .bundle/config && cat .bundle/config | \
grep BUNDLE_ENTERPRISE__CONTRIBSYS__COM > /dev/null) ||
\
(echo "Sidekiq ent credentials: "; read -r creds; dip bundle config --local enterprise.contribsys.com $creds)
provision:
- (test -f .env.development.local) || (echo "\n\n ⚠️ .env.development.local file is missing\n\n"; exit 1)
- dip compose down --volumes
- dip configure_bundler
- (test -f config/database.yml) || (cp .dockerdev/database.yml.example config/database.yml)
- dip compose up -d postgres redis
- dip bash -c bin/setup
以下は、dip provision
コマンドを実際に実行している様子です。
Dipによる対話的なプロビジョニングの例
🔗 開発における(マイクロ)サービス vs Docker
開発環境を標準化するもうひとつのユースケースは、複数の独立したサービスをローカルで立ち上げられるようにすることです。これをDipで行う簡単なデモをお見せします。
まず、本記事に沿って個別のアプリケーションをDocker化します。次に、アプリ同士を接続する必要があります。これはDocker Composeの外部ネットワーク機能でできます。
今述べたようなことは伝統あるMakefileでもできますが、Dipのような専用ツールであらゆることを宣言的に定義する方がずっと効率がよいこともわかってきました。
以下のdip.yml
ファイルを各アプリに追加します。
# ...
provision:
# 名前付きネットワークが存在していることを確認する
- docker network inspect my_project > /dev/null 2>&1 ||\
docker network create my_project
# ...
最後に、compose.yml
ファイル内のエイリアスを経由してサービスをネットワークにアタッチします。
# サービスAのcompose.yml
service:
ruby:
# ...
networks:
default:
project:
aliases:
- project-a
networks:
project:
external:
name: my_project
# サービスBのcompose.yml
service:
web:
# ...
environment:
# 外部ネットワーク用に定義されたエイリアスを介して
# サービスAにアクセスできる
SERVICE_URL: http://project-a:3000
networks:
project:
external:
name: my_project
🔗 development環境からproduction環境へ
本記事の最初のバージョンを公開して以来最も多く寄せられた質問が「Dockerをproduction環境で使う方法」です。この質問にお答えするにはまったく別の記事を書く必要があります(書きます😉)。
それまでのつなぎとして、現在のdevelopment環境を拡張してproduction環境にも応用する方法を先行紹介いたします。
最初にお断りしておきますが、今はDocker Composeベースのデプロイについては扱いませんので、compose.ymlは無関係です。必要なのは、Dockerイメージを更新してdevelopment環境とproduction環境の違いを反映することだけです。
- セキュリティ上の理由で、rootユーザーではなく一般ユーザーの代理としてコードを実行すること
- 必要な依存関係や成果物はイメージにすべて格納しておくこと
- ソースコードはコンテナ内にコピーする形で保存すること
- イメージがなるべく太らないように作ること
上の要件を満たすために、既存のDockerfileをリファクタリングして複数の「ステージ」を定義します(Dockerのマルチステージビルドもサポートします)。以下はコメント付きの例です。
# syntax=docker/dockerfile:1
ARG RUBY_VERSION
ARG DISTRO_NAME=bullseye
# ここでbaseという名前のステージを追加
FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME AS base
ARG PG_MAJOR
ARG NODE_MAJOR
ARG YARN_VERSION
# 共通の依存関係
# ...
# 以下は上とまったく同じ
# ...
# ...
WORKDIR /app
EXPOSE 3000
CMD ["/usr/bin/bash"]
# 次にbaseからdevelopmentステージを定義する
FROM base AS development
ENV RAILS_ENV=development
# baseイメージとの主な違いは、developmentのみのシステム依存関係を
# 持てる点(Vimやgraphvizなど)。
# これらはAptfile.devファイルから抽出する
COPY Aptfile.dev /tmp/Aptfile.dev
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
$(grep -Ev '^\s*#' /tmp/Aptfile.dev | xargs)
# production-builderイメージは依存関係のインストールや
# アセットのコンパイルを担当する
FROM base as production-builder
# まずアプリケーションを実行する専用のユーザーを作成。設定する
RUN groupadd --gid 1005 my_user \
&& useradd --uid 1005 --gid my_user --shell /bin/bash --create-home my_user
USER my_user
RUN mkdir /home/my_user/app
WORKDIR /home/my_user/app
# 次にBundlerを再設定する
ENV RAILS_ENV=production \
LANG=C.UTF-8 \
BUNDLE_JOBS=4 \
BUNDLE_RETRY=3 \
BUNDLE_APP_CONFIG=/home/my_user/bundle \
BUNDLE_PATH=/home/my_user/bundle \
GEM_HOME=/home/my_user/bundle
# gemをインストール
COPY --chown=my_user:my_user Gemfile Gemfile.lock ./
RUN mkdir $BUNDLE_PATH \
&& bundle config --local deployment 'true' \
&& bundle config --local path "${BUNDLE_PATH}" \
&& bundle config --local without 'development test' \
&& bundle config --local clean 'true' \
&& bundle config --local no-cache 'true' \
&& bundle install --jobs=${BUNDLE_JOBS} \
&& rm -rf $BUNDLE_PATH/ruby/3.1.0/cache/* \
&& rm -rf /home/my_user/.bundle/cache/*
# JavaScriptパッケージをインストール
COPY --chown=my_user:my_user package.json yarn.lock ./
RUN yarn install --check-files
# コードをコピーする
COPY --chown=my_user:my_user . .
# アセットをプリコンパイルする
# 注: credentialを使う場合はSECRET_KEY_BASEなどの
# 環境変数も必要になる可能性がある
RUN bundle exec rails assets:precompile
# 最後にproductionイメージを定義する
# 注: これはbaseイメージを拡張したものではなく新規イメージである
FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME AS production
# productionのみで必要な依存関係
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=tmpfs,target=/var/log \
apt-get update -qq \
&& apt-get dist-upgrade -y \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
curl \
gnupg2 \
less \
tzdata \
time \
locales \
&& update-locale LANG=C.UTF-8 LC_ALL=C.UTF-8
# gemをアップグレードして最新バージョンのBundlerもインストールする
RUN gem update --system &&\
gem install bundler
# production-builderイメージのときと同じ名前の専用ユーザーを
# 作成・設定する
RUN groupadd --gid 1005 my_user \
&& useradd --uid 1005 --gid my_user --shell /bin/bash --create-home my_user
RUN mkdir /home/my_user/app
WORKDIR /home/my_user/app
USER my_user
# Ruby/Railsの環境変数を設定する
ENV RAILS_ENV=production\
BUNDLE_APP_CONFIG=/home/my_user/bundle \
BUNDLE_PATH=/home/my_user/bundle \
GEM_HOME=/home/my_user/bundle \
PATH="/home/my_user/app/bin:${PATH}" \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
EXPOSE 3000
# コードをコピーする
COPY --chown=my_user:my_user . .
# 成果物をコピーする
# 1) インストールされたgem
COPY --from=production-builder $BUNDLE_PATH $BUNDLE_PATH
# 2) コンパイル済みアセット(ここではWebpackerでコンパイル)
COPY --from=production-builder /home/my_user/app/public/packs /home/my_user/app/public/packs
# 3) BootsnapのキャッシュもコピーしてRailsサーバーの起動を高速化する
COPY --chown=my_user:my_user --from=production-builder /home/my_user/app/tmp/cache/bootsnap* /home/my_user/app/tmp/cache/
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
🔗 対話的ジェネレータ「Ruby on Whales」の紹介
本記事のボーナスとして、私たちが公開した Ruby on Whalesをご紹介します。 Ruby on WhalesにはRailsテンプレートが含まれており(Rails Bytesで公開されています)、わずか1個のコマンドを実行していくつかの質問に回答するだけで開発で即座にDockerを導入できるよう支援します。
何はともあれ、以下のデモ動画をご覧ください。
Ruby on Whalesインストーラの対話的操作
以下のコマンドをどうぞお試しください。
rails app:template LOCATION='https://railsbytes.com/script/z5OsoB'
感謝
以下の皆さまにお礼の言葉を申し上げます。
- Sergey Ponomarev: パフォーマンス改善のヒントの共有、および初期のDocker化試行でのバトルテスト。
- Mikhail Merkushin: Dipによる業績。
- Dmitriy Nemykin: V2メジャーアップグレードへの協力。
- Oliver Klee (Brain Gourmets): 継続的プルリクによる設定の改善や実用化。
🔗 原文Changelog
- 2.0.3 (2023-09-21)
- Node.jsのインストールスクリプトを更新
- 2.0.2 (2022-11-30)
- ビルド中のクリーンアップを手動から
RUN --mount
によるパッケージのキャッシュに変更 - 2.0.1 (2022-03-22)
- 非推奨の
apt-key
をgpg
に置き換え - 2.0.0 (2022-03-02)
- 全面的な更新および新章の追加
- 1.1.4 (2021-10-12)
- tmpfsにtmp/pidsを追加(A server is already running”エラー対策)
- 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
サービスにヘルスチェックを追加
概要
原著者の許諾を得て翻訳・公開いたします。
更新情報
参考: 2021/04/20の旧版記事は以下で参照できます。