🔰

Railsプロジェクトの新規作成からproductionモードでの起動までのまとめ(Docker Compose使用)

2024/11/18に公開

Railsプロジェクトの新規作成からproductionモードでの起動までのまとめ(Docker Compose使用)

本記事で扱うバージョン

app version
Ruby 3.3.6
Rails 7.2.2
MySQL 8.4.3

特記事項

作業の流れ

  1. 仮のコンテナを作成する
  2. コンテナ上でrails newを実行
  3. 再度コンテナを作成する
  4. アプリのコードを書く
  5. production用のコンテナを作成する
  6. おまけ:別端末でproductionを起動するために

1. 仮のコンテナを作成する

1.1 ファイルの作成

1.1.0 ディレクトリ作成

今回はrails-exampleとする。

> mkdir rails-example
> cd rails-example

以下、任意のエディタでファイルを作成する。

1.1.1 Gemfile
Gemfile
source 'https://rubygems.org'
gem 'rails', '~>7.2.2'
1.1.2 Gemfile.lock
Gemfile.lock (空ファイルを作成)

1.1.3 Dockerfile.dev
Dockerfile.dev
FROM ruby:3.3.6

WORKDIR /app

COPY Gemfile Gemfile.lock .

RUN bundle install
1.1.4 compose.dev.yaml
compose.dev.yaml
services:
  rails:
    container_name: rails-example
    build:
      context: .
      dockerfile: Dockerfile.dev
    # command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app
    env_file:
      - .env
    ports:
      - ${RAILS_PORT}:3000
    tty: true
    stdin_open: true
    depends_on:
      - mysql
  mysql:
    container_name: rails-example_db
    image: mysql:8.4.3
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_DATABASE: ${MYSQL_DB}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      TZ: "Asia/Tokyo"
    ports:
      - ${MYSQL_PORT}:3306

volumes:
  db-data:
1.1.5 .env
.env
RAILS_PORT=3000
MYSQL_DB=rails_example_db
MYSQL_USER=root
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=password

1.2 ビルド

> docker compose -f compose.dev.yaml build
出力
[+] Building 146.0s (10/10) FINISHED                                                                    docker:desktop-linux
 => [rails internal] load build definition from Dockerfile.dev                                                          0.0s
 => => transferring dockerfile: 120B                                                                                    0.0s
 => [rails internal] load metadata for docker.io/library/ruby:3.3.6                                                     2.0s
 => [rails internal] load .dockerignore                                                                                 0.0s
 => => transferring context: 2B                                                                                         0.0s
 => [rails 1/4] FROM docker.io/library/ruby:3.3.6@sha256:9afef279599922a4426c91d0a2a0d0c32c15ef0f65490ae83132a683d58a  60.3s
 => => resolve docker.io/library/ruby:3.3.6@sha256:9afef279599922a4426c91d0a2a0d0c32c15ef0f65490ae83132a683d58ab978     0.0s
 => => sha256:b1afdd91fda31e5c772022a42c968bbc8093faeedefc4e2d373345621ec2eccc 143B / 143B                              0.2s
 => => sha256:3d68f776ee009336fe5b10c46479b9b1b6187319d32617fd1cc8f1ae5c79fd5d 38.01MB / 38.01MB                        7.2s
 => => sha256:604940ee981238b21ac364db82d053d1129907969ea1b118c4cef476cd01bf7e 197B / 197B                              1.0s
 => => sha256:af247aac076473044d24960a352a8ec6f154cf0a28f4fbf35fe5d43b52687ba2 211.29MB / 211.29MB                     42.6s
 => => sha256:2112e5e7c3ff699043b282f1ff24d3ef185c080c28846f1d7acc5ccf650bc13d 64.39MB / 64.39MB                       30.3s
 => => sha256:c3cc7b6f04730c072f8b292917e0d95bb886096a2b2b1781196170965161cd27 24.06MB / 24.06MB                       21.1s
 => => sha256:b2b31b28ee3c96e96195c754f8679f690db4b18e475682d716122016ef056f39 49.58MB / 49.58MB                       12.5s
 => => extracting sha256:b2b31b28ee3c96e96195c754f8679f690db4b18e475682d716122016ef056f39                               3.7s
 => => extracting sha256:c3cc7b6f04730c072f8b292917e0d95bb886096a2b2b1781196170965161cd27                               1.0s
 => => extracting sha256:2112e5e7c3ff699043b282f1ff24d3ef185c080c28846f1d7acc5ccf650bc13d                               5.1s
 => => extracting sha256:af247aac076473044d24960a352a8ec6f154cf0a28f4fbf35fe5d43b52687ba2                              14.9s
 => => extracting sha256:604940ee981238b21ac364db82d053d1129907969ea1b118c4cef476cd01bf7e                               0.0s
 => => extracting sha256:3d68f776ee009336fe5b10c46479b9b1b6187319d32617fd1cc8f1ae5c79fd5d                               2.5s
 => => extracting sha256:b1afdd91fda31e5c772022a42c968bbc8093faeedefc4e2d373345621ec2eccc                               0.0s
 => [rails internal] load build context                                                                                 0.0s
 => => transferring context: 120B                                                                                       0.0s
 => [rails 2/4] WORKDIR /app                                                                                            1.5s
 => [rails 3/4] COPY Gemfile Gemfile.lock .                                                                             0.1s
 => [rails 4/4] RUN bundle install                                                                                     75.2s
 => [rails] exporting to image                                                                                          6.5s 
 => => exporting layers                                                                                                 4.4s 
 => => exporting manifest sha256:c280fa68ef97bc4077f57f8fb1823645e5352b0375aa645a359a70b0f455f4bd                       0.0s 
 => => exporting config sha256:cf2d402b442741112a3f6f2c0d58044efa1296cd943bf7243c489bdf855971c2                         0.0s
 => => exporting attestation manifest sha256:4ba14a817be74f5df63a2a07f962b9efda4a8a916461fdc163f85d6388edf709           0.0s
 => => exporting manifest list sha256:d14e8dd3d7e8851ba772142334dc7b81ff20de453de052f255ae4b99ea0a00e0                  0.0s
 => => naming to docker.io/library/rails-example-rails:latest                                                           0.0s
 => => unpacking to docker.io/library/rails-example-rails:latest                                                        1.9s
 => [rails] resolving provenance for metadata file                                                                      0.1s

2. コンテナ上でrails newを実行

2.1 コンテナ起動

> docker compose -f compose.dev.yaml up -d
出力
[+] Running 11/11
 ✔ mysql Pulled                                                                                                        28.9s 
   ✔ 35c128e29a0d Download complete                                                                                     0.5s 
   ✔ febd18f0a1d7 Download complete                                                                                     0.7s 
   ✔ 3c14bea4ae90 Download complete                                                                                     0.3s 
   ✔ b0f3df846531 Download complete                                                                                     0.9s 
   ✔ f1a9f94fc2db Download complete                                                                                    14.1s 
   ✔ eb1cd7434237 Download complete                                                                                     1.5s 
   ✔ 5ed68a3d6699 Download complete                                                                                     4.3s 
   ✔ 27ea237e9183 Download complete                                                                                    12.9s 
   ✔ 3b413c932fe5 Download complete                                                                                     0.9s 
   ✔ 25c3925a49a0 Download complete                                                                                    19.2s 
[+] Running 4/4
 ✔ Network rails-example_default   Created                                                                              0.2s 
 ✔ Volume "rails-example_db-data"  Created                                                                              0.0s 
 ✔ Container rails-example-db      Started                                                                              1.9s 
 ✔ Container rails-example         Started                                                                              0.9s 

2.2 rails new

> docker compose -f compose.dev.yaml exec rails rails new . -f -d mysql -T
出力
       exist  
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
       force  Gemfile
         run  git init -b main from "."
Initialized empty Git repository in /app/.git/
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/views/pwa/manifest.json.erb
      create  app/views/pwa/service-worker.js
      create  app/assets/images
      create  app/assets/images/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/brakeman
      create  bin/rails
      create  bin/rake
      create  bin/rubocop
      create  bin/setup
      create  Dockerfile
      create  .dockerignore
      create  bin/docker-entrypoint
      create  .rubocop.yml
      create  .github/workflows
      create  .github/workflows/ci.yml
      create  .github/dependabot.yml
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/cable.yml
      create  config/puma.rb
      create  config/storage.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/content_security_policy.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/new_framework_defaults_7_2.rb
      create  config/initializers/permissions_policy.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/master.key
      append  .gitignore
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/406-unsupported-browser.html
      create  public/422.html
      create  public/500.html
      create  public/icon.png
      create  public/icon.svg
      create  public/robots.txt
      create  tmp
      create  tmp/.keep
      create  tmp/pids
      create  tmp/pids/.keep
      create  vendor
      create  vendor/.keep
      create  storage
      create  storage/.keep
      create  tmp/storage
      create  tmp/storage/.keep
      remove  config/initializers/cors.rb
      remove  config/initializers/new_framework_defaults_7_2.rb
         run  bundle install --quiet
WARN: Unresolved or ambiguous specs during Gem::Specification.reset:
      stringio (>= 0)
      Available/installed versions of this gem:
      - 3.1.2
      - 3.1.1
WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
Please report a bug if this causes problems.
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /app/Gemfile.lock
         run  bundle binstubs bundler
       rails  importmap:install
       apply  /usr/local/bundle/gems/importmap-rails-2.0.3/lib/install/install.rb
  Add Importmap include tags in application layout
      insert    app/views/layouts/application.html.erb
  Create application.js module as entrypoint
      create    app/javascript/application.js
  Use vendor/javascript for downloaded pins
      create    vendor/javascript
      create    vendor/javascript/.keep
  Ensure JavaScript files are in the Sprocket manifest
      append    app/assets/config/manifest.js
  Configure importmap paths in config/importmap.rb
      create    config/importmap.rb
  Copying binstub
      create    bin/importmap
         run  bundle install --quiet
       rails  turbo:install stimulus:install
       apply  /usr/local/bundle/gems/turbo-rails-2.0.11/lib/install/turbo_with_importmap.rb
  Import Turbo
      append    app/javascript/application.js
  Pin Turbo
      append    config/importmap.rb
         run  bundle install --quiet
       apply  /usr/local/bundle/gems/stimulus-rails-1.3.4/lib/install/stimulus_with_importmap.rb
  Create controllers directory
      create    app/javascript/controllers
      create    app/javascript/controllers/index.js
      create    app/javascript/controllers/application.js
      create    app/javascript/controllers/hello_controller.js
  Import Stimulus controllers
      append    app/javascript/application.js
  Pin Stimulus
  Appending: pin "@hotwired/stimulus", to: "stimulus.min.js"
      append    config/importmap.rb
  Appending: pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
      append    config/importmap.rb
  Pin all controllers
  Appending: pin_all_from "app/javascript/controllers", under: "controllers"
      append    config/importmap.rb
         run  bundle install --quiet

2.3 config/database.yaml修正

config/database.yaml
 default: &default
   adapter: mysql2
   encoding: utf8mb4
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
-  username: root
+  username: <%= ENV["MYSQL_USER"] %>
-  password:
+  password: <%= ENV["MYSQL_ROOT_PASSWORD"] %>
-  host: <%= ENV.fetch("DB_HOST") { "127.0.0.1" } %>
+  host: mysql

2.4 DB作成

> docker compose -f compose.dev.yaml exec rails rails db:create
出力
Created database 'app_development'
Created database 'app_test'

3. 再度コンテナを作成する

3.1 compose.dev.yaml修正

compose.dev.yaml
-     # 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 && bundle exec rails s -p 3000 -b '0.0.0.0'"

3.2 再ビルド

> docker compose -f compose.dev.yaml build
出力
[+] Building 47.9s (11/11) FINISHED                                                                     docker:desktop-linux
 => [rails internal] load build definition from Dockerfile.dev                                                          0.0s
 => => transferring dockerfile: 120B                                                                                    0.0s
 => [rails internal] load metadata for docker.io/library/ruby:3.3.6                                                     1.8s
 => [rails auth] library/ruby:pull token for registry-1.docker.io                                                       0.0s
 => [rails internal] load .dockerignore                                                                                 0.0s
 => => transferring context: 896B                                                                                       0.0s
 => [rails 1/4] FROM docker.io/library/ruby:3.3.6@sha256:9afef279599922a4426c91d0a2a0d0c32c15ef0f65490ae83132a683d58ab  0.0s
 => => resolve docker.io/library/ruby:3.3.6@sha256:9afef279599922a4426c91d0a2a0d0c32c15ef0f65490ae83132a683d58ab978     0.0s
 => [rails internal] load build context                                                                                 0.0s
 => => transferring context: 8.48kB                                                                                     0.0s
 => CACHED [rails 2/4] WORKDIR /app                                                                                     0.0s
 => [rails 3/4] COPY Gemfile Gemfile.lock .                                                                             0.0s
 => [rails 4/4] RUN bundle install                                                                                     37.5s
 => [rails] exporting to image                                                                                          8.1s 
 => => exporting layers                                                                                                 5.4s 
 => => exporting manifest sha256:f63aabf85fa95fc87e39a83d11b1a9fb3daeca2ed8d7af9f7075dc83908c34b9                       0.0s 
 => => exporting config sha256:885f457a5731296c1b543f5cf35e90035df65d65ac9012faf3809b33b94c565e                         0.0s 
 => => exporting attestation manifest sha256:c2e2d89d8a6960d17d8da22adf2e3e186f9070d357ae6054e1f0fee0b43c5c46           0.0s 
 => => exporting manifest list sha256:384921d83cf4c6c468f117bda47e7be71c6352e40c695aab4c26ae89b0c22dc0                  0.0s 
 => => naming to docker.io/library/rails-example-rails:latest                                                           0.0s
 => => unpacking to docker.io/library/rails-example-rails:latest                                                        2.6s
 => [rails] resolving provenance for metadata file                                                                      0.1s

3.3 コンテナ起動

> docker compose -f compose.dev.yaml up -d
出力
[+] Running 2/2
 ✔ Container rails-example-db  Started                                                                                  1.2s 
 ✔ Container rails-example     Started                                                                                  1.5s 

3.4 起動確認

http://127.0.0.1:3000/

http://127.0.0.1:3000/ へのアクセス結果
http://127.0.0.1:3000/へのアクセス結果

4. アプリのコードを書く

4.1 コンテナ起動

> docker compose -f compose.dev.yaml up -d
出力
[+] Running 2/2
 ✔ Container rails_example_db  Started                                                                                  0.2s 
 ✔ Container rails_example     Started                                                                                  0.5s 

4.2 書く(今回は適当なものをscaffoldで)

> docker compose -f compose.dev.yaml exec rails rails g scaffold example title:string content:text
出力
      invoke  active_record
      create    db/migrate/20241117024947_create_examples.rb
      create    app/models/example.rb
      invoke  resource_route
       route    resources :examples
      invoke  scaffold_controller
      create    app/controllers/examples_controller.rb
      invoke    erb
      create      app/views/examples
      create      app/views/examples/index.html.erb
      create      app/views/examples/edit.html.erb
      create      app/views/examples/show.html.erb
      create      app/views/examples/new.html.erb
      create      app/views/examples/_form.html.erb
      create      app/views/examples/_example.html.erb
      invoke    resource_route
      invoke    helper
      create      app/helpers/examples_helper.rb
      invoke    jbuilder
      create      app/views/examples/index.json.jbuilder
      create      app/views/examples/show.json.jbuilder
      create      app/views/examples/_example.json.jbuilder
> docker compose -f compose.dev.yaml exec rails rails db:migrate
出力
== 20241117024947 CreateExamples: migrating ===================================
-- create_table(:examples)
   -> 0.0234s
== 20241117024947 CreateExamples: migrated (0.0235s) ==========================

4.3 確認

http://127.0.0.1:3000/examples

http://127.0.0.1:3000/examples へのアクセス結果
http://127.0.0.1:3000/examplesへのアクセス結果

5. production用のコンテナを作成する

5.1 ファイル修正

5.1.1 config/database.yaml
config/database.yaml
 production:
   <<: *default
   database: app_production
-  username: app
-  password: <%= ENV["APP_DATABASE_PASSWORD"] %>
5.1.2 config/environment/production.rb
config/environment/production.rb
   # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
   # config.public_file_server.enabled = false
+  config.public_file_server.enabled = true
config/environment/production.rb
   # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
-  config.force_ssl = true
+  config.force_ssl = false
5.1.3 Dockerfile.devをもとにDockerfile.prodを作成
> cp Dockerfile.dev Dockerfile.prod
Dockerfile.prod
-RUN bundle install
+RUN bundle install && bundle exec rails assets:precompile RAILS_ENV=production
5.1.4 compose.dev.yamlをもとにcompose.prod.yamlを作成
> cp compose.dev.yaml compose.prod.yaml
compose.prod.yaml
 services:
   rails:
     container_name: rails-example
     build:
       context: .
-      dockerfile: Dockerfile.dev
+      dockerfile: Dockerfile.prod
     command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
     volumes:
       - .:/app
+    environment:
+      RAILS_ENV: production
     env_file:
       - .env
     ports:
       - ${RAILS_PORT}:3000
     tty: true
     stdin_open: true
     depends_on:
       - mysql
   mysql:
     container_name: rails-example-db
     image: mysql:8.4.3
     volumes:
       - db-data:/var/lib/mysql
     environment:
       MYSQL_DATABASE: ${MYSQL_DB}
       MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
       TZ: "Asia/Tokyo"
     ports:
       - ${MYSQL_PORT}:3306

 volumes:
   db-data:
5.1.5 .env
.env
-RAILS_PORT=3000
+RAILS_PORT=80
 MYSQL_DB=rails_example_db
 MYSQL_USER=root
 MYSQL_PORT=3306
 MYSQL_ROOT_PASSWORD=password

5.2 ビルド

> docker compose -f compose.prod.yaml build
出力
[+] Building 48.9s (11/11) FINISHED                                                                     docker:desktop-linux
 => [rails internal] load build definition from Dockerfile.prod                                                         0.0s
 => => transferring dockerfile: 160B                                                                                    0.0s
 => [rails internal] load metadata for docker.io/library/ruby:3.3.6                                                     2.1s
 => [rails auth] library/ruby:pull token for registry-1.docker.io                                                       0.0s
 => [rails internal] load .dockerignore                                                                                 0.0s
 => => transferring context: 896B                                                                                       0.0s
 => [rails 1/4] FROM docker.io/library/ruby:3.3.6@sha256:9afef279599922a4426c91d0a2a0d0c32c15ef0f65490ae83132a683d58ab  0.0s
 => => resolve docker.io/library/ruby:3.3.6@sha256:9afef279599922a4426c91d0a2a0d0c32c15ef0f65490ae83132a683d58ab978     0.0s
 => [rails internal] load build context                                                                                 0.0s
 => => transferring context: 61B                                                                                        0.0s
 => CACHED [rails 2/4] WORKDIR /app                                                                                     0.0s
 => [rails 3/4] COPY Gemfile Gemfile.lock .                                                                             0.0s
 => [rails 4/4] RUN bundle install && bundle exec rails assets:precompile                                              38.7s
 => [rails] exporting to image                                                                                          7.7s 
 => => exporting layers                                                                                                 5.0s 
 => => exporting manifest sha256:868f6e7ae8153e3407a9563462cbe6193f11d4605435167b9f158898b4441dcd                       0.0s 
 => => exporting config sha256:84d83c60882132495e7478cca60139d086e382df9928819fb81eaf07a637b270                         0.0s 
 => => exporting attestation manifest sha256:d2e782384220bdf7b0d8295be0fdf27ed691b2f2bedd36c459ea44680eed6e38           0.0s 
 => => exporting manifest list sha256:5e5abdf91713d17f55ccc3e31a4b36b98d86a80920947782ff13c06fc4cd2f53                  0.0s 
 => => naming to docker.io/library/rails-example-rails:latest                                                           0.0s
 => => unpacking to docker.io/library/rails-example-rails:latest                                                        2.5s
 => [rails] resolving provenance for metadata file                                                                      0.0s

5.3 コンテナ起動

> docker compose -f compose.prod.yaml up -d
出力
[+] Running 2/2
 ✔ Container rails-example-db  Started                                                                                  0.2s 
 ✔ Container rails-example     Started                                                                                  0.5s 

5.4 DB作成

> docker compose -f compose.prod.yaml exec rails rails db:create
出力
Created database 'app_production'
> docker compose -f compose.prod.yaml exec rails rails db:migrate
出力
I, [2024-11-17T04:05:20.979984 #21]  INFO -- : Migrating to CreateExamples (20241117024947)
== 20241117024947 CreateExamples: migrating ===================================
-- create_table(:examples)
   -> 0.0223s
== 20241117024947 CreateExamples: migrated (0.0224s) ==========================

5.5 動作確認

http://localhost/examples

http://localhost/examples へのアクセス結果
http://localhost/examplesへのアクセス結果

6. おまけ:別端末でproductionを起動するために

  • Dockerfile.prodcompose.prod.yaml及びアプリのコードはそのまま移植
  • .env及びconfig/master.keyは手作業で同期
  • コンテナをsudoで起動する場合はMySQLのユーザ情報の変更が必要?(未確認)

別端末で起動したコンテナへのアクセス結果
別端末で起動したコンテナへのアクセス結果

おわりに

試行錯誤(つまづいたポイント等)は別記事にてまとめる予定。

Discussion