すでに稼働しているRailsアプリをDocker化する手順を紹介します。
Dockerの公式ガイドだけでは手順が不足している部分があったので、細かな部分も含め解説を加えています。
- 環境構成
- Dockerfile
- docker-compose.yml
- .dockerignore
- entrypoint.sh
- config/database.yml
- config/initializers/sidekiq.rb
- 起動確認
- よく使うdocker & docker-composeコマンド
環境構成
バックエンド - Rails 6.0 - Ruby 2.7.4 フロントエンド - Webpacker 5.4.3 - NodeJS 12.22.0 DB - PostgreSQL 11.14 Worker - Sidekiq - Redis
Dockerfile
webサーバー(ruby)の起動をDockerfileで定義します。 コメントで各コマンドの内容を説明しています。
# FROMにはnodeとrubyのイメージを指定します # FROMはDockerfileの最初に記載する必要があり、以降の命令はFROMで指定したイメージに対して行われます # このDockerfileではrubyのイメージに対して命令を記載しているため、FROMの定義順番もnode、rubyである必要があります。 # nodeイメージに対してはASでエイリアスを付けます。エイリアスはCOPYコマンド等で使用することが可能となります FROM node:12.22.0 as node FROM ruby:2.7.4 # タイムゾーンに東京を指定します ENV TZ Asia/Tokyo # db:migrateなどで使用されるpostgresql-clientをイメージ上にインストールします RUN apt-get update -qq && apt-get install -y postgresql-client # WORKDIRでアプリをインストールするディレクトリを指定します。 # 以降の命令は、移動せずともそのディレクトリに対して実行することが出来ます # ディレクトリが存在しない場合は作成されます WORKDIR /your-appname # ホスト上にあるGemfileとGemfile.lockをイメージのWORKDIRに転送します COPY Gemfile Gemfile COPY Gemfile.lock Gemfile.lock # イメージ上でbundle installを実行します RUN bundle install # entrypoint.shをイメージ上の/usr/bin/に配置します # ENTRYPOINTで指定したコマンドはコンテナ稼働に必ず実行されます # entrypoint.shはRailsアプリ特有の問題を対処します # サーバー内にserver.pidというファイルが先に存在していたときに、サーバーが再起動できなくなる問題を回避するために使用されます COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] # nodeイメージ上にインストールされているnode関連モジュールを、rubyイメージでも実行できるように転送します # nodejsとnpmの各種コマンドが実行できるようにシンボリックリンクを貼ります COPY --from=node /usr/local/bin/node /usr/local/bin/node COPY --from=node /usr/local/include/node /usr/local/include/node COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules RUN ln -s /usr/local/bin/node /usr/local/bin/nodejs && \ ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm # ホスト上にあるpackage.json yarn.lock .postcssrc.ymlをWORKDIRに転送します COPY package.json yarn.lock .postcssrc.yml ./ # npmでyarnモジュールをイメージ上にインストールして、yarn installを実行します RUN npm install --global yarn RUN yarn install # ホストのモジュールを全てWORKDIRに転送します # .dockerignoreに記載されたモジュールは転送されません(node_modulesなど) COPY . ./ # ポート番号3000で公開します EXPOSE 3000 # コンテナ稼働時に以下のrailsコマンドが実行されます CMD ["rails", "server", "-b", "0.0.0.0"]
docker-compose.yml
web、db、webpacker、worker、redisのコンテナを定義します。 コメントで各コマンドの内容を説明しています。
version: "3.9" services: web: # Dockerfileを使ってbuildします build: . # コンテナ稼働時に実行されるコマンドです command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" # 擬似端末(キーボードによる入力)をコンテナに結びつけます(docker run -itの-tと同じ意味) tty: true # 標準入出力とエラー出力をコンテナに結びつけます(docker run -itの-iと同じ意味) stdin_open: true # ホスト側とコンテナ側のポートを3000でマッピングしています ports: - "3000:3000" environment: # WEBPACKER_DEV_SERVER_HOSTでdev-serverの接続先を指定しています WEBPACKER_DEV_SERVER_HOST: webpacker volumes: # ホスト上のカレントディレクトリ(.)の内容をコンテナ上の/your-appnameに割り当てます(バインドマウント) - .:/your-appname:delegated # コンテナ上のgemインストール先(/usr/local/bundle)をbundleという名前でボリュームに割り当てます - bundle:/usr/local/bundle depends_on: # dbコンテナが起動してからwebを起動します - db db: # postgresのイメージを使用します image: postgres:11.14 volumes: # コンテナ上のデータ保存先(/var/lib/postgresql/data)をpostgresという名前でボリュームに割り当てます - postgres:/var/lib/postgresql/data # ホスト側とコンテナ側のポートを5432でマッピングしています ports: - "5432:5432" environment: # Postgreのユーザー名、パスワード、DB名を指定します POSTGRES_USER: app_dp POSTGRES_PASSWORD: password POSTGRES_DB: your_appname_dev # タイムゾーンに東京を指定します TZ: "Asia/Tokyo" # エンコーディングとlocaleを指定します POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C" webpacker: # Dockerfileを使ってbuildします build: . # コンテナ稼働時に実行されるコマンドです command: bash -c 'rm -rf public/packs/* || true && bin/webpack-dev-server' volumes: # ホスト上のカレントディレクトリ(.)の内容をコンテナ上の/your-appnameに割り当てます(バインドマウント) - .:/your-appname # コンテナ上のnode_modulesを使用するためボリュームを割り当てます - node_modules:/your-appname/node_modules ports: # ホスト側とコンテナ側のポートを3035でマッピングしています - '3035:3035' environment: # IPアドレスに0.0.0.0を指定します WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 worker: # Dockerfileを使ってbuildします build: . # コンテナ稼働時に実行されるコマンドです command: bundle exec sidekiq -C config/sidekiq.yml volumes: # ホスト上のカレントディレクトリ(.)の内容をコンテナ上の/your-appnameに割り当てます(バインドマウント) - .:/your-appname # webコンテナと同じ名前のbundleボリュームを作成してgemを共有できるようにします - bundle:/usr/local/bundle environment: # redisのURLを指定します REDIS_URL: redis://redis:6379 depends_on: # redisコンテナが起動してからwebを起動します - redis redis: # redisのイメージを使用します image: redis # コンテナ稼働時に実行されるコマンドです command: redis-server --appendonly yes ports: # ホスト側とコンテナ側のポートを6379でマッピングしています - "6379:6379" volumes: # コンテナ上のデータ保存先(/var/lib/redis/data)をredisという名前でボリュームに割り当てます - redis:/var/lib/redis/data # 名前付きボリュームを定義します volumes: bundle: postgres: redis: node_modules:
.dockerignore
dockerをビルドする際に必要のないファイルを.dockerignoreに記載します。 記載することでCOPYコマンドやADDコマンドでの転送時に対象外となります。
vendorやnode_modules等のコンテナで管理したいファイル群、転送の必要がないファイル群を除外しています。
/.bundle /db/*.sqlite3 /db/*.sqlite3-journal /log/* /tmp/* /storage/* /node_modules /yarn-error.log /public/assets /.byebug_history /config/master.key /vendor/* /.idea/* /config/master-staging.key /public/packs /public/packs-test /node_modules /yarn-debug.log* /.yarn-integrity
entrypoint.sh
entrypoint.shはRailsアプリ特有の問題を対処します。 サーバー内にserver.pidというファイルが先に存在していたときに、サーバーが再起動できなくなる問題を回避するために使用されます。
#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"
config/database.yml
development: <<: *default # docker-composeで割り当てられたhostname(db)に変更します host: db # ・・・(以下省略)
config/initializers/sidekiq.rb
Sidekiq.configure_server do |config| if Rails.env.production? if ENV['REDIS_URL'] config.redis = {url: ENV['REDIS_URL'], namespace: "dp_sidekiq_#{Rails.env}"} end else # docker-composeで割り当てられたhostname(redis)に変更します # config.redis = {url: 'redis://localhost:6379', namespace: "dp_sidekiq_#{Rails.env}"} config.redis = {url: 'redis://redis:6379', namespace: "dp_sidekiq_#{Rails.env}"} end end Sidekiq.configure_client do |config| if Rails.env.production? if ENV['REDIS_URL'] config.redis = {url: ENV['REDIS_URL'], namespace: "dp_sidekiq_#{Rails.env}"} end else # docker-composeで割り当てられたhostname(redis)に変更します # config.redis = {url: 'redis://localhost:6379', namespace: "dp_sidekiq_#{Rails.env}"} config.redis = {url: 'redis://redis:6379', namespace: "dp_sidekiq_#{Rails.env}"} end end
起動確認
1. イメージを作成する
docker-compose build --no-cache
2. イメージを元にコンテナを作成し、起動する
docker-compose up -d
3. migrationを流す
docker-compose run web rails db:migrate
4. アプリにアクセスする
よく使うdocker & docker-composeコマンド
最後によく使用するコマンドをまとめました。
# イメージを作成する docker-compose build --no-cache # コンテナを作成して起動する docker-compose up # コンテナを停止する control + c # コンテナの起動(バックグラウンド) docker-compose up -d # コンテナを停止する(バックグラウンド) docker-compose stop # コンテナを停止して削除する docker-compose down # webコンテナにログインする docker exec -it your-appname_web_1 bash # コマンドを実行する docker-compose run web rails db:migrate # イメージの一覧を出力する docker images # コンテナの一覧を出力する docker ps -a