🔰
Railsプロジェクトの新規作成からproductionモードでの起動までのまとめ(Docker Compose使用)
Railsプロジェクトの新規作成からproductionモードでの起動までのまとめ(Docker Compose使用)
本記事で扱うバージョン
app | version |
---|---|
Ruby | 3.3.6 |
Rails | 7.2.2 |
MySQL | 8.4.3 |
特記事項
作業の流れ
- 仮のコンテナを作成する
- コンテナ上で
rails new
を実行 - 再度コンテナを作成する
- アプリのコードを書く
- production用のコンテナを作成する
- おまけ:別端末で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
rails new
を実行
2. コンテナ上で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/
へのアクセス結果
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
へのアクセス結果
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
へのアクセス結果
6. おまけ:別端末でproductionを起動するために
-
Dockerfile.prod
、compose.prod.yaml
及びアプリのコードはそのまま移植 -
.env
及びconfig/master.key
は手作業で同期 - コンテナをsudoで起動する場合はMySQLのユーザ情報の変更が必要?(未確認)
別端末で起動したコンテナへのアクセス結果
おわりに
試行錯誤(つまづいたポイント等)は別記事にてまとめる予定。
Discussion