RailsアプリにDockerを導入する手順

Rails6+Webpacker+Postgresql 既存のRailsアプリをDocker化する手順について綴っていこうと思います。誤っている点や改善点などありましたらご指摘いただけると幸いです。

前提条件

~$ gem info rails
rails (7.0.4.3, 7.0.4, 6.1.7.3, 6.1.5, 6.0.6.1, 6.0.3) #今回は6.1.5を使用します
~$ruby -v
ruby 3.1.2
~$ docker -v
Docker version 20.10.16
~$ docker-compose -v
docker-compose version 1.29.2
~$  docker login
Authenticating with existing credentials...
Login Succeeded

事前準備

手順

  • rails _6.1.5_ new <app名 or . > --webpackrails アプリケーションを作成する。(rails new .の場合カレントディレクトリをアプリ名としてrailsアプリが作成されます。)
  • rails generate scaffold task title:stringで簡単なタスクアプリを作成する。
  • rails db:migrateでdb/migrate以下のファイルの変更をdbに反映する
  • config/routes.rbを編集する(rootにアクセスした際にtasksコントローラのindexアクションを用いるように記述する)
Rails.application.routes.draw do
  resources :tasks
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  root 'tasks#index'  # <=この記述を追加
end
  • export NODE_OPTIONS=--openssl-legacy-providerをterminalで実行(openSSL互換エラーを防ぐための環境変数を追加)
  • 1つの目端末(terminalのタブ)でbin/webpacker-dev-serverを実行(コンパイル用のサーバー)
  • 2つ目の端末(terminalのタブ)で'rails server'を実行(アプリケーションサーバー)

事前準備が終わった際のrepository構成は以下のgithubページから確認できます。

github.com

動作確認と簡単な解説 www.loom.com

zenn.dev

Railsアプリのdocker化

手順

  • Dockerfile作成

app名/Dockerfileを以下の内容で作成します。

# nodeイメージに対してasでエイリアスをつけます。エイリアスはCOPYコマンド等で使用することが可能となります
# このDockerfileではrubyのイメージに対して命令を記載しているため、FROM の定義順番もnode,rubyである必要があります
# 下記のnode全体は最終的なイメージには保存されません
FROM node:16.20.0-bullseye as node



FROM ruby:3.1.2
# Install Node.js and Yarn、nodeイメージがcreateされstartしてbashで入った際に以下のフォルダーが確認できた
# /opt/yarn-* , /usr/local/bin/node , /usr/local/lib/node_modules/
# 以下の記述はnodeコンテナで生成されたフォルダーをrubyコンテナにコピーしている
# ln -fs では左のソースを右のターゲット名で呼び出せるようにシンボリックリンクを作成している
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
  && ln -fs /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx \
  && ln -fs /opt/yarn/bin/yarn /usr/local/bin/yarn \
  && ln -fs /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg
# apt-getで必要なパッケージをインストールします
# -y オプションはyes/noのダイアログに対してyesと答えますという意味です、
RUN apt-get update && apt-get install -y \
  build-essential \
  libpq-dev \
  postgresql-client

# WORKDIR 以降の命令は、/myappで実行されます myapp$
WORKDIR /myapp

# build contextの中のファイルをdockerイメージに組み込んでコンテナが起動した際にコンテナのファイルシステムの/myapp/Gemfileに配置をしている
# ADDもCOPYもbuild contextファイルをdockerイメージに組み込んでコンテナのファイルシステムに配置している
# tarの圧縮ファイルをコピーして解凍したい時はADD、単純にファイルやフォルダをコピーする場合はCOPY
ADD Gemfile /myapp/Gemfile
ADD Gemfile.lock /myapp/Gemfile.lock
COPY package.json /myapp/package.json
COPY yarn.lock /myapp/yarn.lock

# bundle install -> Gemfile.lockの内容をもとにインストール
# yarn install -> yarn.lockの内容をもとにインストール
RUN bundle install && yarn install

# build contextに渡してディレクトリをコンテナファイルシステムの/myappに配置する
ADD . /myapp
  • docker-compose.yml作成

app名/docker-compose.ymlを以下の内容で作成します。

version: '3'

# 名前つきボリュームを定義します
# ボリュームはデータを永続化するための機能
# コンテナ上で生成されたファイルはコンテナのライフサイクルと共に消えてしまう。ボリュームはコンテナのライフサイクルとは独立してファイルの管理を行います。
volumes:
  db-data:
    driver: local #driverはAmazon EBS のような外部のストレージ・システムと統合した環境に Docker をデプロイできるようにする際に詳細に設定する
  bundle:
    driver: local

services:
  web:
    # Dockerfileを使ってbuildします。build contextにはdocker-composeが実行された際のカレントディレクトリを渡します
    build: .
    volumes:
      # ホスト上のカレントディレクトリ(.)の内容をコンテナ上の/myappに割り当てます(バインドマウント)
      - '.:/myapp'
      # コンテナ上のgemインストール先(/usr/local/bundle)をbuldleという名前でボリュームに割り当てますs
      - bundle:/usr/local/bundle
    # コンテナ稼働時に実行されるコマンドです
    # RUN -> volumes(マウント) ->CMD
    command: /bin/sh -c "rm -f tmp/pids/server.pid && rails db:create && rails db:migrate && rails s -p 3000 -b '0.0.0.0'"
    # 'ホスト側:コンテナ側'のポートを3000でマッピングしています
    ports:
      - '3000:3000'
    environment:
      # DB_PASSWORDに.envのDB_PASSWORD のバリューを格納してwebホスト上の環境変数として使用できるようにしています
      - 'DB_PASSWORD=${DB_PASSWORD}'
      # WEBPACKER_DEV_SERVER_HOSTでdev-serverの接続先を指定しています
      - 'WEBPACKER_DEV_SERVER_HOST=webpacker'
    # 擬似端末(キーボードによる入力)をコンテナに結びつけます(docker run -itの-tと同じ意味)
    tty: true
    # 標準入出力とエラー出力をコンテナに結びつけます(docker run -itの-iと同じ意味)
    stdin_open: true
    # dbコンテナが起動してからwebを起動します
    depends_on:
      - db

  webpacker:
    build: .
    volumes:
      - .:/myapp
      - bundle:/usr/local/bundle
    command: ./bin/webpack-dev-server
    environment:
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 #webpackerホスト名を指している
    ports:
      - "3035:3035"

  db:
    image: postgres:12
    volumes:
      - 'db-data:/var/lib/postgresql/data'
    environment:
      - 'POSTGRES_USER=${POSTGRES_USER}'
      - 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
  • .env作成

app名/.envを以下の内容で作成します。

DB_PASSWORD=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
  • .gitignore修正

app名/.gitignoreに以下の内容を追記します。

# .gitignoreファイルの最下部
.env
  • .dockerignore作成

app名/.dockerignoreを以下の内容で作成します。

.env
.git
.gitignore
**/.gitkeep
**/Dockerfile
docker-compose.yml
public/packs
log/*
tmp/*
vendor/bundle
node_modules
  • database.yml修正

app名/config/database.ymlを以下の内容に修正します。

default: &default
  adapter: postgresql
  encoding: unicode
  # docker-composeで割り当てられたhostname(db)に変更
  host: db
  user: postgres
  port: 5432
  password:  <%= ENV.fetch("DB_PASSWORD") %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  database: myapp_production
  • Gemfile修正

app名/Gemfileにいかのgemを追記 && bundle install (bundle updateの際にpostgresqlが入ってないとエラーが発生するので、その際はbrew install postgresqlの後にbundle installを実行してください) 以下の記述をGemfileに追加したらbundle installを実行してください。

gem 'pg', '~> 1.1'

stackoverflow.com

  • webpacker.yml修正

app名/config/webpacker.ymlのdevelopmentを以下のように修正してください

development:
  <<: *default
  compile: true

  # Reference: https://webpack.js.org/configuration/dev-server/
  dev_server:
    https: false
    host: localhost #docker-compose.ymlのenvironment: WEBPACKER_DEV_SERVER_HOST: host名 で上書きされる。
    port: 3035
    public: localhost:3035 #ここでのlocalhostはdev_server: host: で指定したホスト名を指す
    hmr: true
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    compress: true
    disable_host_check: true
    use_local_ip: false
    quiet: false
    pretty: false
    headers:
      'Access-Control-Allow-Origin': '*'
    watch_options:
      ignored: '**/node_modules/**'

Railsアプリのdocker化が終わった際のrepository構成は以下のgithubページから確認できます。

github.com

動作確認と簡単な説明 docker-compose up

www.loom.com

points

  • RUN -> volumes -> CMD
  • docker-composeはアプリに対して1つのネットワークを作成します。サービス用の各コンテナはデフォルトのネットワークにservice:セクションで指定されたホスト名で接続し、そのネットワーク上でコンテナは相互に接続可能な状態になります
  • volumesを共有する設定にしないと、Railsが起動しているコンテナで新しいgemをインストールした際に、Webpackerのコンテナでgemがインストールされていないというエラーが発生します。そのエラーを回避するために、webpackerとrailsの両方のサービスでvolumesを指定して共有しています
めっちゃ参考にしたサイト

zenn.dev

blog.furu07yu.com

参考にしたサイト

自己紹介

23歳 | flutter, AWS 少し触ってました | 筋トレ・ランニング・散歩・サウナ・読書・スプラが好き。 2023/4/18~ Happiness chainで勉強中 twitter フォローしてくれると喜ぶかもしれない。