少しハマったので備忘録。公式のRubyのDockerイメージを使うとbundler経由でインストールしたものをボリューム用コンテナで永続化しても二回目以降の起動時でうまく反映されない事象が起きました。
具体的には以下の手順でエラーが発生しました。
- コンテナを立ち上げてコンテナ内に入り、
bundle install –path=vendor/bundle
で依存ライブラリをインストール。vendor/bundleはボリュームコンテナで永続化している。 bin/rails s
でサーバ起動できることを確認- コンテナを再起動し再度コンテナ内に入り、
bin/rails s
でサーバ起動する。vendor/bundleはボリュームコンテナで永続化しているはずなのでbin/rails s
を叩けば起動できるはず… - が、エラーで落ちる
# Dockerfile
FROM ruby:2.5.0
ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential nodejs
RUN mkdir /app
WORKDIR /app
CMD ["/bin/bash"]
# docker-compose.yml
version: '3'
services:
app:
build: .
volumes:
- .:/app
- bundle_install:/app/vendor/bundle
ports:
- "3000:3000"
stdin_open: true
tty: true
volumes:
bundle_install:
driver: local
原因
公式のDockerfilleでは環境変数のBUNDLE_APP_CONFIGが/usr/local/bundle
に設定されています。
BUNDLE_APP_CONFIGはbundlerの設定ファイルが格納されるディレクトリを指定する変数でBUNDLE_APP_CONFIGが未設定の場合は ${PWD}/.bundle/config
がbundlerの設定ファイルになります。
そのため、設定ファイルの /usr/local/bundle/config
が初期では作成されず、永続化・ホスト共有もしていないのでコンテナを立ち上げる度に設定ファイルが無い状態になります。設定ファイルが無い状態だと、bundlerはBUNDLE_PATH=/usr/local/bundleを見に行き、vendor/bundleを見に行きません。
対策がいくつかあるので以下に紹介していきます。
対策
1. BUNDLE_APP_CONFIG環境変数を変更する
BUNDLE_APP_CONFIGに永続化・ホスト共有しているパスを指定します。docker-compose.ymlに書くとこんな感じです
version: '3'
services:
app:
# ...
environment:
BUNDLE_APP_CONFIG: /app/.bundle
2. BUNDLE_PATH環境変数を変更する
デフォルトで見るbundlerのパスを/app/vendor/bundle/ruby/{version} に書き換えればconfigが無くても指定したパスを参照してくれます。
上記のようにdocker-compose.ymlに書いても良いし、railsの起動時に変数を設定しても良いです。
BUNDLE_PATH=/app/vendor/bundle/ruby/2.5.0 bin/rails s
が、docker-compose.ymlやDockerfileに書いておいた方がいちいち指定する手間が省けて良いです。
3. /usr/local/bundleをボリュームコンテナにする
そもそも環境が隔離されているコンテナなのでgemのインストールパスにvendor/bundleを指定する必要は無く、コンテナ内のシステム(グローバル)にインストールしても良いはずです。docker-compose.ymlに書くとこんな感じ
version: '3'
services:
app:
build: .
volumes:
- .:/app
- bundle_install:/usr/local/bundle
ports:
- "3000:3000"
stdin_open: true
tty: true
volumes:
bundle_install:
driver: local
この状態でパス指定なしの bundle install
をすればちゃんと動きます。