Windows11+WSL2+Docker+Rails+MySQL+API-環境構築

Dockerを使ってRailsのAPI環境を構築する機会があり、環境構築でわからない事の調べ直しに時間が掛かっていたので、備忘録として記事にまとめた。

下記の開発環境で実施しました。

##### 開発環境
- Edition: Windows 11 Home, Version: 22H2, OSビルド: 22621.1702
- WSL2
- Docker Desktop for Windows: 4.20.1 (110738)
- Docker Engine: 24.0.2
- Docker Compose: v2.18.1
- Ruby: 3.2.2
- Rails: 7.0.5
- MySQL: 8.0.33

次に、ディレクトリは下記の構成にしました。
ディレクトリ名は適宜変えて下さい。

##### ディレクトリ構成
real_world
├realworld
│├Gemfile
│├Gemfile.lock
│├entrypoint.sh
│└Dockerfile
└docker-compose.yml

各種設定ファイルを作成、記述していきます。
まずは、必要なディレクト・ファイルを作成します。

```terminal:console
mkdir real_world
cd real_world

mkdir realworld
touch docker-compose.yml

touch ./realworld/Gemfile
touch ./realworld/Gemfile.lock
touch ./realworld/entrypoint.sh
touch ./realworld/Dockerfile

```

次に、各ファイルを記述していきます。

##### Gemfile
```text:Gemfile
source 'https://rubygems.org'
gem 'rails', '~>7.0.5'
```
Railsは最新の7.0.5を使用しました。
Railsのバージョンは下記サイトから確認して下さい。

https://rubygems.org

##### Gemfile.lock
```text:Gemfile.lock
```
Gemfile.lockファイルはビルド後に、バージョン等の情報が記載されるので、現状は空ファイルのままで大丈夫です。

##### entrypoint.sh
```shell:entrypoint.sh
#!/bin/bash
set -e
rm -f /api/tmp/pids/server.pid
exec "$@"
```
Railsにはサーバー内にserver.pidというファイルが先に存在していたときに、サーバーが再起動できなくなる問題があります。それを回避するためのスクリプトを作成します。

##### Dockerfile
```dockerfile:Dockerfile
FROM ruby:latest
ARG RUBYGEMS_VERSION=3.4.6
RUN mkdir /api
WORKDIR /api
COPY Gemfile /api/Gemfile
COPY Gemfile.lock /api/Gemfile.lock
RUN gem update --system ${RUBYGEMS_VERSION} && \
    bundle install
COPY . /api
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
CMD ["rails", "server", "-b", "0.0.0.0"]
```
おおまかに内容を説明すると、イメージを指定した後にコンテナ内にapiというディレクトリを作成し、作業ディレクトリを指定しています。
次に、ローカルのGemfileとGemfile.lockをコンテナ内にコピーし、その後に、Gemをインストールします。
最後に、コンテナ起動時に実行するスクリプトをコピーし、実行権限を与えて、エンドポイントを設定。コンテナ起動時にRailsサーバが起動するようにしてあります。

##### docker-compose.yml
```yml:docker-compose.yml
version: '3'
services:
  api:
    build: ./realworld/
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - ./realworld:/api
    ports:
      - 3000:3000
    depends_on:
      - db
    tty: true
    stdin_open: true
  db:
    image: mysql:latest
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    volumes:
      - db-volume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
      TZ: "Asia/Tokyo"
    ports:
      - "3306:3306"
volumes:
  db-volume:
```

おおまかに内容を説明すると、apiというサービス名を指定して、realworld配下のDockerfileを基にイメージをビルドしています。
volumesではローカルのrealworldディレクトリをコンテナ内のapiディレクトリにマウントしています。
depends_onでは、サービスの依存関係を指定しています。今回のように記述した場合、起動時には、db→apiの順に起動します。また、停止時には、api→dbの順に停止します。
また、dbサービスには最新のMySQLイメージを指定しています。
文字化け防止のために、utf8を指定しました。
volumesを記述するとコンテナを作り直したとしてもPCにデータを保存する領域が作成されるので今回はdb-volumesをボリュームしました。

##### docker compose run & build
```text:console
docker compose run api rails new . --force --database=mysql --api
docker compose build
```
apiモードでRailsプロジェクトを作成します。
docker compose runでは引数に指定したサービスのコンテナ内でコマンドを実行します。
api：docker-compose.ymlのservices直下のサービス名
--force：上書きオプション
--database=mysql：使用するデータベース
--api：APIモードの指定

Railsプロジェクトが作成できたら、イメージをビルドします。

##### データベース設定
```yml:./realworld/config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  host: db
```
./realworld/config/database.ymlを編集します。
デフォルトではpasswordが空白、hostがlocalhostとなっているのでdocker上で設定した値に修正します。

##### データベースの作成
```text:console
docker compose up -d
docker compose exec api rails db:create
```
docker compose up -dでコンテナをバックグランドで実行。
docker compose exec api rails db:createでデータベースを作成しています。

##### コンテナ起動確認とRailsロゴの表示確認
```docker:console
docker-compose ps
NAME                IMAGE               COMMAND                  SERVICE             CREATED             STATUS
     PORTS
real_world-api-1    real_world-api      "entrypoint.sh /bin/…"   api                 34 hours ago        Up 10 minutes       0.0.0.0:3000->3000/tcp
real_world-db-1     mysql:latest        "docker-entrypoint.s…"   db                  34 hours ago        Up 10 minutes       0.0.0.0:3306->3306/tcp, 33060/tcp
```
docker-compose psコマンドでコンテナ情報を確認。
apiとdbが起動(up,running)している事が確認出来たら、下記のアドレスにアクセスしてみましょう。
http://localhost:3000

Railsのロゴが表示されたら成功です。

##### CORS設定
忘れないうちにCORS設定をしておきます。
Gemfileに記載されている下記の部分のコメントアウトを解除します。

```text:Gemfile
gem "rack-cors"
```
コメントアウトして、gemが追加されたのでbundle installします。

```docker:console
docker compose exec api bundle install
```

config/initializers/cors.rbのRails.application.config.middleware.insert_before以下をコメントアウトします。
originsの部分はlocalhost:3000に置き換えて下さい。

```ruby:realworld/config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "localhost:3000"

    resource "*",
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```

下記の記事を参考にさせて頂きました。

https://zenn.dev/wakkunn/articles/33c84147608078