少しハマったので備忘録。公式のRubyのDockerイメージを使うとbundler経由でインストールしたものをボリューム用コンテナで永続化しても二回目以降の起動時でうまく反映されない事象が起きました。

具体的には以下の手順でエラーが発生しました。

  1. コンテナを立ち上げてコンテナ内に入り、bundle install --path=vendor/bundle で依存ライブラリをインストール。vendor/bundleはボリュームコンテナで永続化している。
  2. bin/rails sでサーバ起動できることを確認
  3. コンテナを再起動し再度コンテナ内に入り、bin/rails sでサーバ起動する。vendor/bundleはボリュームコンテナで永続化しているはずなのでbin/rails sを叩けば起動できるはず…
  4. が、エラーで落ちる

Dockerfileとdocker-compose.ymlはこんな感じです(最小構成で書いているので実際はもう少し書いてます)

原因

公式の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に書くとこんな感じです

2. BUNDLE_PATH環境変数を変更する

デフォルトで見るbundlerのパスを/app/vendor/bundle/ruby/{version} に書き換えればconfigが無くても指定したパスを参照してくれます。

上記のようにdocker-compose.ymlに書いても良いし、railsの起動時に変数を設定しても良いです。

が、docker-compose.ymlやDockerfileに書いておいた方がいちいち指定する手間が省けて良いです。

3. /usr/local/bundleをボリュームコンテナにする

そもそも環境が隔離されているコンテナなのでgemのインストールパスにvendor/bundleを指定する必要は無く、コンテナ内のシステム(グローバル)にインストールしても良いはずです。docker-compose.ymlに書くとこんな感じ

この状態でパス指定なしの bundle installをすればちゃんと動きます。