📅 ⏰  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:
    エンジニアとしてフルリモートワークで8年生き残るための技術スタック

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

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

    zoomで自分の画面共有をしながら他の画面共有を見る方法

    zoomで自分の画面共有をしながら他の画面共有を見る方法

    プログラミングブートキャンプをオンラインで運営しているのですが、 zoomを使っています。 運営側、つまり私の画面を共有して実際の操作の様子を見ていただきながらやってます。 テキストだけよりもライブでやったほうが圧倒的にわかりやすいですからね! 最近はこちらの様子だけでなく、キャ...