🐳

🐳 Dockerを甚いたRailsずMySQL開発環境構築ガむド

2024/07/31に公開

はじめに

こんにちは、Takeです。郜内の自瀟開発䌁業で゚ンゞニアずしお働いおいたす。

今回やるこず

docker, docker composeを甚いお以䞋の環境を䜜成したす。

ruby 3.3.3
rails 7.1.3.4
mysql 8.4.0

芁件

  1. railsずmysqlはコンテナを分けるこず
  2. 以䞋のgemをdevelopmentおよび test環境でのみ䜿甚できるよう導入するこず
pry-rails
factory_bot
rubocop
  1. minitestを䜿甚するためrspecは䞍芁
  2. makeコマンドの䜜成しお以䞋を実行できるようにするため
    • Docker環境でRubocopを実行
    • テストの実行

動䜜確認

  • docker compose upで以䞋のコンテナが起動する
rails
mysql
  • binding.pryが動䜜する
  • factory_botが動䜜する
  • rubocopが動䜜する
    • コンテナの䞭でrubocopが動䜜するようにmakeコマンドを䜜成しおおく

git initから First Commitたで

こちらを参考に

https://qiita.com/aisplay/items/5a77e259ea2feeeb48c3

Dockerfileの䜜成

以䞋のリンクを参考に必芁なファむルを䜜成したす。
https://docs.docker.jp/compose/rails.html

https://qiita.com/croquette0212/items/7b99d9339fd773ddf20b

https://musclecoding.com/rails7-mysql8-docker/

以䞋のファむルを䜜成したす。DockerでRailsを動かすため

  • Dockerfile
  • docker-compose.yml
  • Gemfile
  • Gemfile.lock
  • entrypoint.sh
terminal
touch Dockerfile docker-compose.yml Gemfile Gemfile.lock entrypoint.sh

Dockerfileの内容

Dockerfile
FROM ruby:3.3.3
ARG RUBYGEMS_VERSION=3.3.3

# 䜜業ディレクトリを指定
WORKDIR /app

# ホストのGemfileずGemfile.lockをコンテナにコピヌ
COPY Gemfile Gemfile.lock /app/

# bundle installを実行
RUN bundle install

# ホストのファむルをコンテナ内の䜜業ディレクトリにコピヌ
COPY . /app

# entrypoint.shをコンテナ内の䜜業ディレクトリにコピヌ
COPY entrypoint.sh /app

# entrypoint.shを実行可胜にする
RUN chmod u+x entrypoint.sh

# コンテナ起動時にentrypoint.shを実行
ENTRYPOINT ["/app/entrypoint.sh"]

# コンテナ起動時にrails serverを実行
CMD ["rails", "server", "-b", "0.0.0.0"]

Ruby 3.3.3をベヌスにしたコンテナを䜜成し、Railsアプリケヌションを実行するためのものです。

䟝存関係をむンストヌルし、アプリケヌションコヌドず゚ントリヌポむントスクリプトをコンテナにコピヌしたす。起動時に゚ントリヌポむントスクリプトを実行し、Railsサヌバヌを起動する蚭定が含たれおいたす。

docker-compose.ymlの内容

docker-compose.yml
version: "3.9"
services:
  db:
    image: mysql:8.4.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: database
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    volumes:
      - mysql_volume:/var/lib/mysql
    ports:
      - "3307:3306"
    command: --default-authentication-plugin=mysql_native_password

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    stdin_open: true
    tty: true
    depends_on:
      - db
volumes:
  mysql_volume:

database.ymlの内容

database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  host: db

development:
  <<: *default
  database: app_development

test:
  <<: *default
  database: app_test

production:
  <<: *default
  database: app_production
  username: app
  password: <%= ENV["APP_DATABASE_PASSWORD"] %>

この蚭定ファむルでは、dbサヌビスずしおMySQLコンテナの起動蚭定を行い、webサヌビスずしおRailsアプリケヌションの起動蚭定を行っおいたす。dbずいう名前で蚭定されたMySQLコンテナに、Railsアプリケヌションからアクセスできるようにしおいたす。

https://zenn.dev/take_tech/articles/4ad36b7ed78ad7

Gemfileの内容

Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 7.1.3.4'

Railsアプリケヌションの䟝存関係を管理したす。具䜓的には、RubyGemsの゜ヌスからRails 7.1.3.4を含む7系のバヌゞョンを䜿甚するこずを指定しおいたす。

entrypoint.shの内容

entrypoint.sh
#!/bin/ash

set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /app/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

Railsサヌバヌが既に存圚する堎合に叀いPIDファむルを削陀し、その埌にDockerfileで指定されたメむンプロセスを実行したす。スクリプトぱラヌが発生するず終了するように蚭定されおいたす。

Railsプロゞェクトの䜜成コマンド

 docker compose run web rails new . --force --no-deps --database=mysql
  • --force: 既存のファむルを䞊曞きしたす
  • --no-deps: サヌビスの䟝存関係を起動せずにコマンドを実行したす
  • --database=mysql: MySQLデヌタベヌスを䜿甚したす

Railsアプリケヌション内でデヌタベヌスを䜜成

docker compose run --rm web rails db:create

MySQLが䜜成されたす

$ docker compose run --rm web rails db:create
[+] Creating 1/0
 ✔ Container auto-registration-db-1  Running                                                                                    0.0s 
Created database 'app_development'
Created database 'app_test'

サヌバヌを起動

docker compose run --rm web rails s

成功🙌

gemをdevelopment, test環境でのみ䜿甚できる状態にする

group :development, :test do
  gem 'pry-rails'
  gem 'pry-byebug'
  gem 'factory_bot_rails'
  gem 'rubocop', require: false
  gem 'rubocop-rails', require: false
  gem 'rubocop-performance', require: false
end

䞻に以䞋のgemを远加

  • pry-rails
  • factory_bot
  • rubocop
    • minitestを䜿甚するためrspecは䞍芁

require: falseを付ける理由は、これらのgemが自動で読み蟌たれないようにするためです。これにより、アプリケヌションの起動時に䜿甚されるメモリ量を枛らし、起動速床を向䞊させるこずができたす。

You may pass an array with multiple files or true if the file you want required has the same name as gem or false to prevent any file from being autorequired.

https://bundler.io/v2.5/man/gemfile.5.html#REQUIRE-AS

https://docs.rubocop.org/rubocop-performance/usage.html

そしお、以䞋のコマンドを実行しおGemfileに蚘茉のgemをダりンロヌドしたす。

docker compose run web bundle install

Makefileの䜜成

芁件
4. makeコマンドの䜜成

  • Docker環境でRubocopを実行できるようにするため
  • さらに、テストも実行できるようにしおいただけるず尚良し

https://www.gnu.org/software/make/manual/make.html#Introduction

https://quruli.ivory.ne.jp/document/make_3.79.1/make-jp_toc.html

Makefile誕生前の背景

1980幎代からCPUのハヌドりェアや゜フトりェアが急速に発展するこずにより、倧芏暡なプログラムも増加したした。倧芏暡プログラムには、倚くの゜ヌスファむルやヘッダファむルが含たれおおり、耇雑な䟝存関係が存圚したす。

あるファむルを修正・倉曎した際、その倉曎に䟝存する党おのファむルを再コンパむルする必芁があるため、手動での管理が困難か぀コンパむルに時間がかかる課題がありたした。

Makefile誕生

Stuart FeldmanによっおMakeが䜜成されたした。Makeは、プログラムのビルドプロセス(プログラムの゜ヌスコヌドをCPUが実行できる圢に倉曎する䜜業)を自動化するためのツヌルで、その具䜓的な蚭定内容を蚘茉したものがMakefileです。

具䜓的な蚭定内容ずしおは、ファむル同士の䟝存関係やコマンドがありたす。Makeは、このMakefileを読み蟌み、必芁なファむルだけを再コンパむルするこずで効率的なビルドプロセスを実珟したした。

※ビルドプロセスずコンパむルの違いはこちら

Makefileのメリット

  1. 効率的なコンパむル倉曎されたファむルだけを再コンパむルするため、コンパむルに芁する時間を削枛。
  2. 䟝存関係の管理Makefileに䟝存関係を蚘述するこずで管理。
  3. ビルドプロセスの自動化手動の操䜜を枛らすこずで、ヒュヌマン゚ラヌも枛少。

Makefileの䞭身に぀いお

Makefile
# Dockerの蚭定ファむル
DOCKER_COMPOSE = docker compose
DOCKER_RUN = $(DOCKER_COMPOSE) run --rm

# デフォルトで実行されるサヌビス名
SERVICE = web

# Railsコマンド
RAILS = $(DOCKER_RUN) $(SERVICE) bundle exec rails

# Rubocopコマンド
RUBOCOP = $(DOCKER_RUN) $(SERVICE) bundle exec rubocop

# Docker
.PHONY: build up stop restart down-volumes logs clean

build:
	$(DOCKER_COMPOSE) build --no-cache

up:
	$(DOCKER_COMPOSE) up -d

restart:
	$(DOCKER_COMPOSE) restart

down:
	$(DOCKER_COMPOSE) down

down-volumes:
	$(DOCKER_COMPOSE) down -v

logs:
	$(DOCKER_COMPOSE) logs -f

# Rails
.PHONY: setup db-create db-drop db-migrate reset seed console annotate routes shell test

create:
	$(RAILS) db:create

drop:
	$(RAILS) db:drop

migrate:
	$(RAILS) db:migrate

reset:
	$(RAILS) db:reset

seed:
	$(RAILS) db:seed

console:
	$(RAILS) console

annotate:
	$(RAILS) annotate

routes:
	$(RAILS) routes

shell:
	$(DOCKER_RUN) $(SERVICE) bash

test:
	$(RAILS) test

# Rubocop
.PHONY: rubocop rubocop-auto-correct rubocop-auto-unsafe
rubocop:
	$(RUBOCOP)

rubocop-auto-correct:
	$(RUBOCOP) -a

rubocop-auto-correct-unsafe:
	$(RUBOCOP) -A

# Bundle
.PHONY: bundle-install update
bundle-install:
	$(DOCKER_RUN) $(SERVICE) bundle install

bundle-update:
	$(DOCKER_RUN) $(SERVICE) bundle update

# Assets
.PHONY: assets-precompile
assets-precompile:
	$(DOCKER_RUN) $(SERVICE) bundle exec rails assets:precompile

# key
.PHONY: key
key:
	$(DOCKER_RUN) --rm -e EDITOR=vim $(SERVICE) bundle exec rails credentials:edit

䜜業䞭に発生した゚ラヌ集

docker-compose: command not found

bash: docker-compose: command not found

docker-compose をダりンロヌドしおむンストヌルする必芁がありそう。

sudo apt-get install docker-compose

ダメ

source ~/.zshrc

䞊蚘を実行埌に再床以䞋を実行するもダメ

sudo apt-get install docker-compose

切り替えお以䞋を実行

docker compose up -d

゚ラヌ発生

Error response from daemon: driver failed programming
external connectivity on endpoint registration-db-1 (urytp95eiypw48tupryjo86uwqa5o8uypt9r9d2b4730141681bfas4aw4rhj53):
Bind for 0.0.0.0:3306 failed: port is already allocated

以䞋の゚ラヌメッセヌゞに着目

Error response from daemon:driver failed programming
external connectivity 〜. failed: port is already allocated.

https://qiita.com/Senritsu420/items/65e0da5696040a5a17c4

Error response from daemon:
failed to create task for container: failed to create shim task:
OCI runtime create failed: runc create failed:
unable to start container process: exec: "entrypoint.sh":
executable file not found in $PATH: unknown

芁するに、entrypoint.shを適圓に䜍眮しおくださいず蚀われたPath名が違う

Dockerfile
# コンテナ起動時にentrypoint.shを実行
- ENTRYPOINT ["/entrypoint.sh"]

+ ENTRYPOINT ["/app/entrypoint.sh"]

docker compose up実行時の゚ラヌメッセヌゞ

※䞀郚抜粋

[ERROR] [MY-013909] [Server] A message intended for a client
cannot be sent there as no client-session is attached.
Therefore, we're sending the information to the error-log instead:
MY-001146 - Table 'mysql.component' doesn't exist

クラむアントに関するメッセヌゞが、クラむアントのセッションが接続しおいないため送信できたせん。
そのため、この情報を゚ラヌログに送信したす。mysql componentテヌブルが存圚しないず。

䞀床ボリュヌムを削陀しおみる

docker compose down -v
docker compose up

正垞に起動🙌

[ERROR] [MY-0100343] [Server] unknown variable 'default-authentication-plugin=mysql_native_password'.

default-authentication-plugin=mysql_native_passwordずいう䞍明な倉数がありたす。

Important Change: The deprecated mysql_native_password authentication plugin is now disabled by default. It can be enabled by starting MySQL with the new --mysql-native-password=ON server option, or by adding mysql_native_password=ON to the [mysqld] section of your MySQL configuration file.

MySQL8.4.0では、mysql_native_passwordの認蚌プラグむンがデフォルトで無効になったため゚ラヌが発生。

https://dev.mysql.com/doc/relnotes/mysql/8.4/en/news-8-4-0.html#mysqld-8-4-0-deprecation-removal

MySQL5.7.0たではmysql_native_passwordはデフォルトの認蚌プラグむンであり、ナヌザヌパスワヌドのハッシュ化にSHA-1が䜿甚されおいた。

In MySQL 8.4, caching_sha2_password is the default authentication plugin rather than mysql_native_password (deprecated).

その䞀方で、MySQL8.0からは曎にセキュアなSHA-256を䜿甚した caching_sha2_password 認蚌プラグむンが远加され、デフォルトの認蚌プラグむンも caching_sha2_passwordに倉曎された経緯がある。

よっお、以䞋のようにdocker-compose.ymlファむルを修正

修正前

docker-compose.yml
version: "3.9"
省略
    volumes:
      - mysql_volume:/var/lib/mysql
    ports:
      - "3307:3306"
        command: --default-authentication-plugin=mysql_native_password

修正埌

docker-compose.yml
version: "3.9"
省略
    volumes:
      - mysql_volume:/var/lib/mysql
    ports:
      - "3307:3306"
    # 削陀デフォルトでcaching_sha2_passwordが蚭定されおいるため

珟段階では採甚しなかったもの

ridgepole

Railsにおスキヌマ管理を行う䟿利なツヌル 🛠
https://github.com/ridgepole/ridgepole

その背景

1. 今回はチヌム人数が比范的少ないため 🌟

ridgepoleはスキヌマ倉曎を䞀括管理するため、マむグレヌションファむルのコンフリクトが起こりにくいメリットがありたすが、そもそも今回はチヌム人数が比范的少ないため、この恩恵を倧きく掻かせないのではないかず考えおおりたす。

2. 远加での蚭定を芁するため

rails g modelを実行する堎合にマむグレヌションファむルを無効にする蚭定などが必芁なため。

3. 開発途䞭でも導入できるため

Rails暙準ではないずいうこずもありたすが、ridgepoleは容易にプロゞェクト途䞭から導入できるため、開発者が増えるなどの理由からマむグレヌションファむルの管理が難しくなっおきた時に導入を再怜蚎いたしたす。

最埌に

ここたで読んでいただきありがずうございたした
今回の蚘事が良かったず思ったらぜひ「いいね」を抌しおいただけるず嬉しいです 🎉

noteでも蚘事を執筆しおいたすので、ぜひチェックしおみおください。
https://note.com/take_lifelog/n/n58df7ce7af6f

他にもこのようなこずに぀いお蚘茉しおいるのでお読みいただければ幞いです。

https://zenn.dev/take_tech/articles/275e5f4242973d

https://zenn.dev/take_tech/articles/374817f256ec9d

最埌たでお読みいただき、誠にありがずうございたした

Discussion