DockerでRailsの開発環境構築するときに、今のところ安定している設定(M1 Mac)

最近、RailsのDocker開発環境をチューニングしたので、備忘録です。

# 構成
## 基本
- M1 Mac
- Ruby 3.1.2
- Rails 7.0.4
- Node 16系
- Postgresql

## その他
- webpacker(Rails7から引退したみたいなので、そのうち剥がしたい）
- solargraphもdocker
- rubocopもdockerにしたいけどしてない。


## 説明
こちらの記事を見て、volume に gem をインストールするよう bundler に設定しました。
https://zenn.dev/aldagram_tech/articles/110bc79925d41b

bundle install前に設定
`bundle config set --local path /usr/local/bundle`

docker-compose.yml
`bundle:/usr/local/bundle`


## ファイル

### Dockerfile

```dockerfile: Dockerfile
FROM ruby:3.1.2-alpine

ENV LANG=ja_JP.UTF-8
ENV TZ=Asia/Tokyo
ENV RAILS_ENV="development"
ENV NODE_ENV="development"
ENV ROOT=/app

WORKDIR $ROOT

COPY Gemfile Gemfile.lock $ROOT

RUN apk --update --no-cache add --no-cache -t .build-dependencies \
  build-base \
  libxml2-dev\
  libxslt-dev \
  && apk add --no-cache python3 make g++\
  # M1 Macで必要
  gcompat \
  bash \
  file \
  libpq \
  libxml2 \
  libxslt \
  nodejs \
  postgresql-dev \
  tini \
  tzdata \
  yarn \
  graphviz \
  msttcorefonts-installer \
  fontconfig \
  font-bitstream-type1 \
  ghostscript-fonts \
  ttf-freefont\
  imagemagick \
  git \
  && gem install bundler \
  && bundle config set --local path /usr/local/bundle \
  && bundle install -j4
RUN  update-ms-fonts \
  && fc-cache -f \
  && rm -rf /var/cache/*

COPY . $ROOT

RUN yarn install --check-files \
  && bundle exec rails webpacker:compile

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh

ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
```

### docker-compose.yml
webpackerを別で作ることで、ホットリロードできるのでこのような構成にしました。
（Rails7では標準っぽいので、Webpacker使ってるプロジェクト向きです。）

```yml: docker-compose.yml
version: "3.7"

services:
  db:
    image: postgres:latest
    container_name: db
    ports:
      - '5432:5432'
    expose:
      - 5432
    environment:
      POSTGRES_DB: api
      POSTGRES_USER: postgresql
      POSTGRES_PASSWORD: postgresql
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
      TZ: "Asia/Tokyo"
    volumes:
      - db:/var/lib/postgresql/data:cached
  app:
    container_name: app
    command: ash -c "rm -f tmp/pids/server.pid && ./bin/rails s -p 3000 -b '0.0.0.0'"
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 3000:3000
    volumes:
      - .:/app:delegated
      - node_modules:/app/node_modules
      - tmp:/app/tmp
      - bundle:/usr/local/bundle
    tmpfs:
      - /tmp
    environment:
      RAILS_ENV: development
      NODE_ENV: development
      DB_HOST: db
      DB_NAME: rails-app
      DB_USER: postgresql
      DB_PASSWORD: postgresql
      WEBPACKER_DEV_SERVER_HOST: webpacker
    tty: true
    stdin_open: true
    depends_on:
      - db
      - webpacker
      - solargraph
  webpacker:
    container_name: wds
    build: .
    command: ./bin/webpack-dev-server
    volumes:
      - .:/app:delegated
      - node_modules:/app/node_modules
    environment:
      RAILS_ENV: development
      NODE_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    ports:
      - "3035:3035"
  solargraph:
    container_name: solargraph
    build: .
    command: bundle exec solargraph socket --host=0.0.0.0 --port=7658
    volumes:
      - .:/app:delegated
      - bundle:/usr/local/bundle
    ports:
      - "8091:7658"

volumes:
  db:
  bundle:
  node_modules:
  tmp:
```


### entrypoint.sh
`bundle check || bundle install --jobs 4`
とすることで、docker-compose up時に、bundle installが必要なら、自動でしてくれます。

```sh: entrypoint.sh
#!/bin/bash
set -e

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

echo "bundle install..."
bundle config set --local path /usr/local/bundle \
  && bundle check || bundle install --jobs 4

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


### Makefile
毎回docker-compose up とか入力するの面倒なので、Makefile使ってます。

個人では、.zhsrcのalias使って、`dc`で実行できるようにしてますが、チーム開発用にMakefileです。
```sh: .zhsrc
alias dc='docker-compose'
```



`make up`でrailsを起動するようにしてますが、合わせてattachして、byebugも使えるようにしてます。

※docker-compose.ymlのappのところで、以下を定義する必要あり。
```yml: docker-compose.yml(抜粋)
tty: true
stdin_open: true
```

```sh: Makefile
.PHONY: init up bundle migrate seed rspec lint-all rubocop slim-lint brakeman rails_best_practices

rubocop=bundle exec rubocop -A
brakeman=bundle exec brakeman --rails7  -q -o brakeman.html
rails_best_practices=bundle exec rails_best_practices
slim-lint=bundle exec slim-lint app/views/

init:
	docker-compose build --no-cache
	docker-compose run --rm app bin/setup
	make seed

up:
	docker-compose up -d && docker attach $$(docker-compose ps -q app | sed -n 1p)

bundle:
	docker-compose run --rm app bundle install

migrate:
	docker-compose run --rm app bin/rails db:apply

seed:
	docker-compose run --rm app bin/rails db:seed

rspec:
	docker-compose run --rm app bundle exec rspec && open coverage/index.html

lint-all:
	docker-compose run --rm app bash -c '${rubocop} && ${brakeman} && ${rails_best_practices}'

rubocop:
	docker-compose run --rm app $(rubocop)

slim-lint:
	docker-compose run --rm app $(slim-lint)

brakeman:
	docker-compose run --rm app $(brakeman)

rails_best_practices:
	docker-compose run --rm app ${rails_best_practices}
```

# 終わりに
もう少しチューニングできるような気はしてますが、今の所これで落ち着いてます。
改善点などあれば、ぜひ教えていただきたいです！