📅 ⏰  9 分で読めます

Rails7.1で自動生成されるDockerfileを開発環境でも使えるようにする

Rails7.1からDockerfileも生成される Rails7.1からRails newした際にDockerfileも自動生成されるようになりました。 しかし自動生成されるDockerfileは本番環境用で、そのままでは開発環境で使用することはできません。 今回は本番用のDo...

Rails7.1からDockerfileも生成される

Rails7.1からRails newした際にDockerfileも自動生成されるようになりました。 しかし自動生成されるDockerfileは本番環境用で、そのままでは開発環境で使用することはできません。

今回は本番用のDockerfileから開発環境に必要な部分だけ抜き出して、Dockerfile.developmentとして開発環境用のDockerfileを作成したいと思います。

なお、Rails7.1の開発環境の構築はホストのRubyを使ってrails newする記事も見かけますが、本記事で紹介する方法はdockerとdocker composeがあれば可能です。 つまりホストにRubyをインストールする必要がありません。 また今回はdatabaseにpostgresqlを使用します。 (mysqlを使う場合でも大きくやり方は変わらないと思います)

プロジェクトディレクトリの作成

適当にプロジェクトのディレクトリを作成しましょう。 今回はrails_sampleとします。

mkdir rails_sample

作成したディレクトリに移動します。

cd rails_sample

Gemfile

Gemfileを作成します。 このファイルをもとにRailsをinstallします。

$ touch Gemfile

Gemfileが生成されたので以下を記述します。

Gemfile
source 'https://rubygems.org'
gem 'rails', '~>7'

次に空のGemfile.lockを作成します

$ touch Gemfile.lock

docker

Dockerfileを作ります。

Dockerfile
FROM ruby:3.3.1

RUN apt-get update -qq && apt-get install -y nodejs npm postgresql-client
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install

色々と記述が足りないのでは?と思った方もいらっしゃると思いますが、rails newすると上書きされてしまうので、rails newができる最低限の記述にとどめます。 ポイントはnpmをinstallすることです。 npmをインストールしておけば、後でrails newするときにjavascriptが必要なオプションを渡した場合でもエラーを発生させることなく実行することができます。

次にcompose.ymlを作成します。この名前で作成するとdocker composeコマンド時にオプション無しで自動で読み込まれます。 以前はdocker-compose.ymlという名称でしたが現在はcompose.ymlが推奨されているようです。(docker-compose.ymlでもまだ動きます)

compose.yml
services:
  db:
    image: postgres:14-alpine
    volumes:
      - db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes:
  db:

コミットする

rails newの差分を確認するためにこのあたりでgit commitしておくとよいでしょう。

git add .
git commit -m "setup"

プロジェクトのビルド

セットアップが完了したのでプロジェクトをビルドします。

docker-compose run --no-deps web rails new . --force --database=postgresql

完了するとproduction用のDockerfileが生成されます。

Dockerfile
# syntax = docker/dockerfile:1

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.3.1
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"


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build gems
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y build-essential git libpq-dev libvips pkg-config

# 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

# 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


# 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

# 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"]

db接続情報をdocker用に修正

compose.ymlで定義したdbサービスにrailsが接続できるように修正します。 ポイントはhostにcompose.ymlで定義したservice名を指定することです。 service名だけでdockerが名前解決してくれます。

config/database.yml
 default: &default
   adapter: postgresql
   encoding: unicode
+  host: db
+  username: postgres
+  password: password
   # For details on connection pooling, see Rails configuration guide
   # https://guides.rubyonrails.org/configuring.html#database-pooling
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

開発環境用のDockerfile作成

本番環境用のDockerfileから開発環境に必要な部分だけ抜き出し、Dockerfile.developmentを作成します。

Dockerfile.development
# syntax = docker/dockerfile:1

# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
ARG RUBY_VERSION=3.3.1
FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base

WORKDIR /myapp

# 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 && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Install JavaScript dependencies
ARG NODE_VERSION=18.19.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

# Install node modules
COPY package.json yarn.lock ./
RUN yarn install

# Copy application code
COPY . .

yarn.lockがコピーされていない状態なので、作成しておきます。

touch yarn.lock

docker composeでDockerfile.developmentを使用するように修正します。 またgemのキャッシュを利用できるようにvolumeを指定します。

compose.yml
     environment:
       POSTGRES_PASSWORD: password
   web:
-    build: .
+    build:
+      context: .
+      dockerfile: Dockerfile.development
     command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
     volumes:
       - .:/myapp
+      - bundle:/usr/local/bundle
     ports:
       - "3000:3000"
     depends_on:
       - db
 volumes:
   db:
+  bundle:

css,jsをbuildするプロセスも起動する

開発環境でesbuildを使う場合、railsのサーバーのほかにcssとjsをbuildするプロセスも起動するため、compose.ymlで起動コマンドを修正します。 bin/devで起動するように修正します。 またttyを指定してプロセスが落ちないように修正します。

compose.yml
     build:
       context: .
       dockerfile: Dockerfile.development
-    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
+    command: bash -c "rm -f tmp/pids/server.pid && bin/dev"
+    tty: true

bin/devを見てみましょう。 foremanというプロセス管理のgemを使って、Procfile.devを使って起動しています。 Procfile.devを見てみましょう。 js,cssをyarnを使ってbuildしています。 このbuildコマンドはpackage.jsonのscriptsで定義されています。 jsはesbuild,cssはtailwindcssを使っています。

package.json
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets",
    "build:css": "tailwindcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css --minify"
  }

bindingを指定する

Dockerを使っている場合、bindingを0.0.0.0に指定する必要があるので環境変数を指定します。

compose.yml
     command: bash -c "rm -f tmp/pids/server.pid && bin/dev"
+    environment:
+      - BINDING=0.0.0.0

起動する

これで準備がすべて整いました。 ではrailsを起動してみましょう。

docker compose up

データベースを作成してくださいとエラーが発生しますが、create databaseボタンを押してください。

no database error

railsのwelcomeページが表示されれば成功です。

rails welcome

    Share:
    今よりも収入を大幅にアップさせたいならエンジニアになれ!

    今よりも収入を大幅にアップさせたいならエンジニアになれ!

    こんにちは。吉田智哉です。 今日は「今よりも収入を大幅にアップさせたいならエンジニアになれ!」という話をしたいと思います。 エンジニアは安定して稼げる エンジニアって本当に稼げる、稼ぎやすいし、安定して稼げる仕事だと思います。 実際私は、年収200万円台から未経験からエンジニアに...

    地方に住みながらでも稼ぎたかったらWeb系エンジニアになれ!

    地方に住みながらでも稼ぎたかったらWeb系エンジニアになれ!

    こんにちは。吉田智哉です。 地元の岩手県盛岡市に住みながらエンジニアとしてフルリモートワークで東京の開発案件を請けてます。 「地方に住みながらでも稼ぎたかったらWeb系エンジニアになれ!」という話をしたいと思います。 岩手県盛岡市はニューヨークタイムズ紙が選ぶ「世界で訪れるべき5...

    エンジニアとしてフルリモートワークで8年生き残るための技術スタック

    エンジニアとしてフルリモートワークで8年生き残るための技術スタック

    こんにちは。吉田智哉です。 岩手県盛岡市に住みながらエンジニアとしてフルリモートワークで東京の開発案件を請けてます。 気が付くとこの働き方で8年間も経過していました。 8年生き残ることができた技術スタックをシェアしたいと思います。 今までの8年間で、これから先はどうなるかわかりま...