はじめに
前回は、Dockerfile
の自動生成そのものについて、rails new
を実行する際のオプションや実行環境による違いを確認してみましたが、
今回は、実際に生成されたファイルの中身を見ていきたいと思います。
というか、前回の記事は、もともと書くつもりのなかった( 単一の記事にする予定ではなかった )ものなので、実は、ここからが本番です。
Dockerfile
の生成方法について
詳しくは、上の記事を見てみて欲しいのですが、今回は、次のようなコマンドで生成された Dockerfile
を確認していきます。
rails new . --database=postgresql --css=bootstrap
つまり、データベースに PostgreSQL を、CSS フレームワークとして Bootstrap を、それぞれオプションで指定した場合に生成される Dockerfile
が対象となります。1
ちなみに、前回の記事で説明した通り、Node と Yarn のバージョンについては、実行環境に依存するようですが、Dockerfile
としては、単に環境変数の値が変わるだけですね。
syntax
の確認
さて、まずは、1行目から見ていきましょう。
と言っても、もちろん、丁寧に説明しすぎてもアレなので、ものによっては、簡単に触れるだけに留めておきます。
# syntax = docker/dockerfile:1
これは、Dockerfile
の構文の「 バージョン1 」の、最新の安定リリースを、常に指すようになっているそうです。2
もし「 バージョン2 」がリリースされたりしたら、この辺の事情も変わってくるかもしれませんが、とりあえずは、そっとしておけばいいでしょう。
ベース環境の確認
次に、後で説明するビルド用の環境と、最終的に利用することとなるイメージを作成するためのファイナルステージ環境の、両方で base
となる環境を構築しているのが、3~14行目になります。
まぁ、そんなに大袈裟なものでもなくて、親イメージ( すなわち Ruby 3 のバージョン )や、その他、共通する設定などを指定しているだけですね。
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.2.2
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
# Rails app lives here
WORKDIR /rails
# Set production environment
ENV RAILS_ENV="production" \
BUNDLE_DEPLOYMENT="1" \
BUNDLE_PATH="/usr/local/bundle" \
BUNDLE_WITHOUT="development"
ただ、ここで、作業ディレクトリ( WORKDIR
)が /rails
に設定されているのは、最低限、覚えておいた方がいいでしょう。
また、本番環境用に、いくつか Bundler 関連の環境変数も指定されています。4
-
BUNDLE_DEPLOYMENT
:Gemfile
の変更を禁止に -
BUNDLE_PATH
:gem が配置される場所 -
BUNDLE_WITHOUT
:インストールしない gem( グループで指定 )
そのため、この Dockerfile
を参考にして、開発環境も作ろうと思っている場合などには、ご注意ください。
ビルド環境の確認
ここからは、最終的に生成される Docker イメージのサイズを小さくするために切り離されている、build
環境を見ていきましょう。5
まずは、17~22行目ですが、base
環境を元に、後でビルドに必要となるパッケージを、色々とインストールしていますね。
# Throw-away build stage to reduce size of final image
FROM base as build
# Install packages needed to build gems and node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential curl git libpq-dev libvips node-gyp pkg-config python-is-python3
というか、主に gem と Node のインストールに必要な環境を整えてくれていて、以降、24~41行目で、実際にそれらのインストールが行われることとなります。
# Install JavaScript dependencies
ARG NODE_VERSION=18.15.0
ARG YARN_VERSION=latest
ENV PATH=/usr/local/node/bin:$PATH
RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \
/tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \
npm install -g yarn@$YARN_VERSION && \
rm -rf /tmp/node-build-master
# Install application gems
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile
# Install node modules
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
ちなみに、Node モジュールのインストール手順が含まれているのは、rails new
する際に css
オプションを加えたからです。6
一応、本番環境らしいものとしては、yarn install
時に --frozen-lockfile
オプションが指定されていることが挙げられますかね。7
ともかく、これで、ビルドの実行環境は整ったので、43~50行目では、ローカルのファイルをコピーしてきて、Bootsnap 8 とアセットのプリコンパイルを実行しています。
# Copy application code
COPY . .
# Precompile bootsnap code for faster boot times
RUN bundle exec bootsnap precompile app/ lib/
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
特に、本番環境のためのアセットのプリコンパイルについては、重要ですね。
詳細は、ぜひ Rails ガイドなどを参照してください。
ファイナルステージ環境の確認
それでは、いよいよファイナルステージに突入です。
まず、53~59行目ですが、ビルド環境の時と同じ流れで、base
環境を元に、必要なパッケージをインストールしています。
# Final stage for app image
FROM base
# Install packages needed for deployment
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y curl libvips postgresql-client && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
ここで、postgresql-client
パッケージがインストールされているのは、rails new
を実行する際に、database
オプションで postgresql
を選択したからですね。
そして、61行目から最終行までで、上で構築した build
環境の中から、必要なファイルをコピーしてきて、non-root ユーザーを作成するなど、起動のための準備を行います。
# Copy built artifacts: gems, application
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails
# Run and own only the runtime files as a non-root user for security
RUN useradd rails --create-home --shell /bin/bash && \
chown -R rails:rails db log storage tmp
USER rails:rails
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD ["./bin/rails", "server"]
以上、自動生成された Dockerfile
の中身でした。
ちなみに、データベースの準備をしてくれるという /rails/bin/docker-entrypoint
ファイルの中身は、こんな感じです。
#!/bin/bash -e
# If running the rails server then create or migrate existing database
if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then
./bin/rails db:prepare
fi
exec "${@}"
おわりに
果たして、何かしら参考になりましたでしょうか?
正直な感想を言えば「 思ったより普通だったな 」という感じかもしれませんが、
まぁ、デフォルトとして自動生成してくれるだけで、なんとなく安心感があるというか、
少なくとも、どこからコピペしてきたのか分からないようなものと比べれば、断然マシですし、これだけ簡潔な内容だというのは、むしろ、評価すべきところかもしれません。
また、具体例をもとに、Dockerfile
のベストプラクティスについて学ぶのにも、ちょうど良い機会となるのではないでしょうか?
とは言え、今回は Dockerfile
の内容を確認してみただけなので、これで、実際にコンテナが起動できるかどうかについては、実は、未知数です。
そして、とりあえず、次は、開発環境についても、Docker Compose の利用も含めて、考えてみたいところですね。9