🐆

ボリュームマウントを使ってdocker開発を効率化するdocker-compose.yml集(Node.js, Go, Rails)

2022/12/12に公開

対象者

  • docker-compose.ymlの文法がわかる方
  • Node.js, Go, Ruby on Railsをdockerで開発している方
  • 開発者体験に速度面で不満を抱えている方

何かお役に立てれば幸いです。

基本理念

  1. ホストとコンテナ間で同期するファイルを少なくすることによって高速化を試みる
  2. パッケージのインストールやキャッシュは再度インストールする必要がないように積極的にボリュームを使う
  3. コンテナの設定をできる限りdocker-compose.ymlに記述することで再度ビルドをすることなく設定を変更できるようにする

Node.js

docker-compose.yml
volumes:
  node_modules:
  yarn_cache:
services:
  app:
    image: node:19-alpine
    working_dir: /app
    volumes:
      - .:/app
      - node_modules:/app/node_modules
      - yarn_cache:/usr/local/share/.cache/yarn/v6
    # 以下略
  1. node_modulesをボリュームマウントする

サイズが巨大なnode_modulesをバインドマウントするとホストとコンテナ間の同期が足手まといになってyarn installなどが遅くなったり、require()を用いるjsの実行自体が遅くなったりすることがあります。(小規模のプロジェクトではあまり顕在化しないけど)

注意: 上記のように指定するとホストのnode_modulesがコンテナに同期されないので、コンテナでyarn installをしてもホスト側のnode_modulesは空になります。よってコードを書く際はホスト側でもう一度yarn installをしないといけません。これはちょっと面倒ですが、高速化とのトレードオフですね。

  1. yarnのcacheをボリュームマウントする

yarnのcacheを永続化すると再度同じパッケージを入れる場合yarn addが速くなります。

Go

docker-compose.yml
volumes:
  go_packages:
services:
  app:
    image: golang:1.19-alpine
    working_dir: /app
    volumes:
      - .:/app
      - go_packages:/go
    environment:
      GO111MODULE: 'on'
      GOPATH: /go

こちらはgoのパッケージを永続化する設定です。永続化をしない場合、次のどちらかを選択することになります。

  1. Dockerfileでgo mod downloadを記述して、パッケージが追加されるたびにdocker compose buildを行う
    -> ローカルのイメージが無駄に増えてしまう
  2. コンテナを作成するたびにgo.modに記述されたパッケージのうち足りないものをダウンロードする(Goが自動でやってくれる)
    -> 通信量と時間が勿体無い

Ruby on Rails

Dockerfile
FROM ruby:2.6.3-alpine3.10 AS dev
WORKDIR /app
RUN apk update && \
  apk add git gcc make g++ ca-certificates curl mysql-client mysql-dev nodejs tzdata && \
  curl -o- -L https://yarnpkg.com/install.sh | sh && \
  ln -s "$HOME/.yarn/bin/yarn" "/usr/local/bin/yarn"
docker-compose.yml
volumes:
  ruby_gems:
  rails_tmp:
services:
  app:
    build: .
    working_dir: /app
    environment:
      GEM_HOME: /gem
      BUNDLE_BIN: /gem/bin
      PATH: "${PATH}:/gem/bin"
    volumes:
      - .:/app
      - ruby_gems:/gem
      - rails_tmp:/app/tmp
  1. gemを永続化

環境変数GEM_HOME, BUNDLE_PATH, BUNDLE_BINを自ら設定してgemが/gemにインストールされるようし、/gemをボリュームマウントしています。
こうすることでイメージを再度ビルドすることなく、コンテナ内で一度だけbundle installをすればよくなります。

gemを永続化しない場合、Dockerfileでbundle installを記述して、パッケージが追加されるたびにdocker compose buildを行うことになりますから、無駄なイメージを増やすことになってしまいます。

  1. railsのキャッシュを永続化

ここまでする必要はないかとは思いますが、tmpディレクトリは数十MBになったりするので、ホスト-コンテナ間の同期対象から取り除いて損はないと思います。

Discussion