Webpackerを外したRailsをDockerで開発できるようにする
前回、無事にホットリロードまで使えるようになり、フロントエンド開発環境としてはほぼ問題ない状態になったと思うものの、Railsサーバーとwebpack-dev-serverのプロセスを2つ立ち上げないといけないのはとても面倒なので、今回はその部分をなんとかしていく
ひと昔前ならForeman gemとか使ってやっていたが、今時はdocker-composeを使って2つコンテナを立ち上げて対応すると思うのでその方向でやっていく
正直そこまでDockerの知見がないので、かなり良いのではないかと言われているEvilMartins流の構成を最大限参考にさせていただき、ついでにDockerの知見も得ていこうと思う
(リポジトリはこちら)
EvilMartins流の構成をとりあえずブッ込む
リポジトリが↓にあるので、細かいことはわからないけど、とりあえずぶっ込んで動くところまでやって、そのあと1つ1つ調べていく
https://github.com/evilmartians/terraforming-rails/tree/master/examples/dockerdev
今回docker化するサンプルアプリでは不要なものもあるので、そのあたりを除外した構成で入れていく
具体的には以下の箇所を削除
- redisは使ってないので削除
- sidekiqは使ってないので削除
- bootsnapは使ってないので削除
- webpackerではなく素のwebpackなのでそのあたりを調整
- mimemagicを使ってないのでDockerfileから該当部分を削除
- runnerコンテナは欲しい人がいれば復活させることにして削除
M1 Mac環境でyarnの導入に失敗する
今回M1 Macの環境で構築したんだけど、yarnをソースリストに入れる以下の箇所がエラーになった
https://github.com/evilmartians/terraforming-rails/blob/6d4a2c2297b2ebd686430ddbf107d80f0d006644/examples/dockerdev/.dockerdev/Dockerfile#L42-L44
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo 'deb http://dl.yarnpkg.com/debian/ stable main' > /etc/apt/sources.list.d/yarn.list
ググると同じ症状の人がいたのでコードを拝借するが、どうやらwgetが入っていないようで再びエラーになったので
ここにwgetのインストールも含めてあげると無事にインストールできた
Railsとwebpack-dev-serverが連携できるようにする
動作環境がDocker化したことにより、webpack-dev-serverがlocalhostで待ち受けてもコンテナ内に他に誰もいないので正しく動作しなくなる
従って webpack.config.js
の devServer
の host
設定で 0.0.0.0
を指定して、コンテナ外部からアクセスできるようにして、Railsと連携できるようにする
ということで無事にDocker Composeで動くようになった(ここまでの差分はこちら)
アプリルート超絶荒れがちなので .dockerdev
にまとめるのいいと思った
EvilMartins流Docker化が何をやっているのか見ていく
無事動くようになったので、どんなことをやっているのか見ていく
まずはdocker-compose.ymlの内容をチェックして、そこから参照されているファイルを掘っていく
x-appブロック
まずx-appの部分を見ていく
x-app: &app
build:
context: .dockerdev
dockerfile: Dockerfile
args:
RUBY_VERSION: '2.7.3'
PG_MAJOR: '14'
NODE_MAJOR: '14'
YARN_VERSION: '1.22.15'
BUNDLER_VERSION: '2.2.29'
environment: &env
NODE_ENV: ${NODE_ENV:-development}
RAILS_ENV: ${RAILS_ENV:-development}
YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
image: remove_webpacker_sample:1.0.0
tmpfs:
- /tmp
-
&app
- x-appの横にある
&app
はRailsでもお馴染みのやつで、定義を使いまわせるようにするためのもの - どこか必要な箇所で
<<: *app
と呼べば↑の定義をまるまる追加することができる
- x-appの横にある
-
build
- Docはここ
- コンテナイメージのビルドの要件を定義する箇所
- contextはDockerfileがあるディレクトリへのパス、またはgitリポジトリを指定するところ
- dockerfileはDockerfileを別名で用意した場合に、その名前を指定するためのところ
- 今回は普通にDockerfileという名前で使っているので指定しなくても良さそう
- argsはDockerfileのARG値を設定するところ
- Dockerfile内部で参照していることがわかる
- コンテナ内部で参照できる環境変数とは別なので注意
-
environment
-
image
- Docはここ
- コンテナを起動するためのイメージを指定するところ
- 指定されたイメージがローカルになければdocker hubとかからpullしてくるという動作をする
- ただし、
image
とbuild
が両方指定されている場合はbuild
が優先して実行され、image
のpullは発生しない (ここではこの動き)
- ただし、
- 今回
image: remove_webpacker_sample:1.0.0
という感じでimageを指定しているので
こんな感じにイメージが1つだけ作成されるが、もしimageを指定しなかった場合は$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE remove_webpacker_sample 1.0.0 74b11c553c66 3 hours ago 573MB
といった感じで作成しようとしているコンテナの数($ docker images REPOSITORY TAG IMAGE ID CREATED SIZE remove_webpacker_sample_webpack latest 74b11c553c66 3 hours ago 573MB remove_webpacker_sample_rails latest 74b11c553c66 3 hours ago 573MB
_webpack
と_rails
)だけイメージが作成されてしまうようで、とてもリソース効率が悪い
例えdocker hubなどで配布しないとしても、セマンティックバージョニングしないとしても、とりあえず指定だけはしておいた方が良さそう
あと、ここで最悪と言われているlatest
が勝手に使われるのも本来良くないっぽい
-
tmpfs
- こっちのDocは説明がしょぼいので、こっちを見ると良さそう
- Dockerでは通常コンテナを破棄するとデータは消滅してしまうので、必要なデータに対しては
volume
やbind mount
を使ってホストマシンとコンテナでファイルを共有し、データを保持するようにするのが一般的 tmpfs
は上記2つとは異なりホストのメモリ上に永続化され、コンテナが停止するとtmpfs
は削除される- 要するに消えちゃうけど読み書きが高速なので、
/tmp
みたいなディレクトリをマウントするのにうってつけ - また、ホストにもコンテナにも残したくないような一時データにも最適なので、セキュリティ目的か今回のようにパフォーマンス目的で使うと良い
x-backendブロック
続いて x-backend
ブロックも見ていく
関連しているので volumes
も載せている
x-backend: &backend
<<: *app
stdin_open: true
tty: true
volumes:
- .:/app:cached
- rails_cache:/app/tmp/cache
- bundle:/usr/local/bundle
- node_modules:/app/node_modules
- packs:/app/public/packs
- .dockerdev/.psqlrc:/root/.psqlrc:ro
- .dockerdev/.bashrc:/root/.bashrc:ro
environment:
<<: *env
DATABASE_URL: postgres://postgres:postgres@postgres:5432
WEBPACK_DEV_SERVER_HOST: webpack
WEB_CONCURRENCY: 0
HISTFILE: /app/log/.bash_history
PSQL_HISTFILE: /app/log/.psql_history
EDITOR: vi
depends_on:
postgres:
condition: service_healthy
volumes:
postgres:
bundle:
node_modules:
rails_cache:
packs:
<<: *app
- ↑で説明した通り、x-appの定義を全てmixinしている
stdin_open
とtty
- こちらの記事が超詳しいので、こちらから引用させていただくと
stdin_open
はdocker run
コマンドの-i
オプションに相当するもので、これをつけないと(trueにしないと)terminalからの入力を受け付けなくなるtty
はdocker run
コマンドの-t
オプションに相当するもので、これをつけないと(trueにしないと)標準入出力がterminalに繋がらず、インタラクティブな操作ができない
- こちらの記事が超詳しいので、こちらから引用させていただくと
volumes
- Dockerコンテナ内部にデータを作成しても、コンテナを破棄するとデータも一緒に消えてしまうため、コンテナの外にボリュームと呼ばれるデータ領域を用意し、そこを利用することで永続化することができる
- そのボリュームを指定するためのところが
volumes
- いくらコンテナを破棄しても、明示的に破棄されない限りvolumeの中のデータは残るということ
- そのボリュームを指定するためのところが
- ボリュームは特定のコンテナ専用にも、複数のコンテナから参照できるようにもでき、ここではその両方が使われている
.:/app:cached
のcached
はここに説明がある通り、ホスト上のファイルの更新がコンテナ上に反映されるまで、多少の遅延が発生することを許可する代わりに、パフォーマンスを高めている
- Dockerコンテナ内部にデータを作成しても、コンテナを破棄するとデータも一緒に消えてしまうため、コンテナの外にボリュームと呼ばれるデータ領域を用意し、そこを利用することで永続化することができる
depends_on
- Docはここ
- コンテナ間の起動と停止の依存関係を支持するところ
- ここでの場合「x-backendをmixinしたサービスは、コンテナ作成時にpostgresコンテナが先に作成される。また、コンテナが破棄される時はpostgresコンテナが先に破棄される」ように指定されている
- 要するに常に先に作成・破棄されるようになる
- 上記Docにも書かれているが、docker-composeではサービスが開始する前に、依存するサービスが開始されていることを待機することができる
- ここではpostgresコンテナのヘルスチェックがOKとなったあと、x-backendがmixinされたコンテナが作成される
- railsのコンテナはDBが先に動作していないとエラーになるのでこれが必要
servicesブロック
rails, postgres, webpackの3つのコンテナを作成しているので、1つ1つ見ていく
まずはrailsコンテナ
rails:
<<: *backend
command: bundle exec rails server -b 0.0.0.0
ports:
- '3000:3000'
- 上述した
x-backend
の設定が全て含まれるようになっている- すでに説明したが
image
の指定はあるが、build
も指定されているので、用意したDockerfileからコンテナが作成される
- すでに説明したが
command
はコンテナで実行するプロセスのコマンドで、Railsサーバーを起動し、外部からのアクセスを許可している(0.0.0.0
はその機器にある全てのIPアドレスで待ち受けるという意味)ports
はコンテナの3000番ポートをホストの3000番ポートにマッピングしている(Railsは3000番ポートを使うのでこの指定が必要)- ちなみに
ホストのポート:コンテナのポート
となる
- ちなみに
続いてpostgresコンテナ
postgres:
image: postgres:14.0
volumes:
- .dockerdev/.psqlrc:/root/.psqlrc:ro
- postgres:/var/lib/postgresql/data
- ./log:/root/log:cached
environment:
PSQL_HISTFILE: /root/log/.psql_history
POSTGRES_PASSWORD: postgres
ports:
- 5432
healthcheck:
test: pg_isready -U postgres -h 127.0.0.1
interval: 5s
- こいつは
x-backend
やapp
をmixinしていないので、明示的にimage
が指定されている volume
は↑で説明した通りだが、それぞれ何なのか見ていくports
は単体で5432
と書かれているが、これは「コンテナの5432番ポートを公開し、ホストのポートは使える番号をDockerが適当に選択する」という挙動になるhealthcheck
は↑のx-backendブロックのところでちょっとだけ触れたが、このpostgresコンテナが「健全」であるかどうかを判断するために使われるtest
はコンテナの健全性をチェックするために使われるコマンドで、pg_isreadyコマンドで接続確認をしているinterval
はそのまんま5秒毎に実行される
たまに起動が待てなくて云々という話が出るけど、こうやってヘルスチェックやって待機してあげればいいのか〜
これは勉強になった
最後にwebpackコンテナ
といってもここは僕が独自に書き換えているので、EvilMartins流ではない
webpack:
<<: *app
command: yarn dev-server
ports:
- '8080:8080'
volumes:
- .:/app:cached
- bundle:/usr/local/bundle
- node_modules:/app/node_modules
- packs:/app/public/packs
environment:
<<: *env
x-backend
ではなくapp
の設定を使っているのはそのままcommand
はWebpaker使っていないのでpackage.json
に定義したnpm scriptを指定ports
もwebpack-dev-serverデフォルトのポートをマッピングしているvolumes
もgemとかnpmパッケージとかビルドファイルをrailsコンテナと使いまわせるようにしているだけ
終わりに
ということで、不要と思われる箇所を削除したコミットはこちら
これで(多分)必要なところだけを残したymlになったと思われる