2020-03-03に投稿

docker build中にVolumeの中身は参照することが出来ない

railsの開発中に、ホストのrailsディレクトリをまるごとコンテナ内で参照してgemもここに入れちゃいたい、そしたらコンテナ作成のたびにbundle installし直さなくて済むし、みたいな需要があって。

volumes:
  - ./:/rails

みたいなことを書いて、dockerfile内で

WORKDIR /rails/
RUN bundle install -j4 --retry=3 && \ bundle clean

しようとすると、/rails/Gemfileが見えないからインストールが上手く行かない、というようなことが起こる。

よくわかんないんだけどbashで入って改めてbundle installすればインストールできるからまあいいか、みたいないい加減な運用をしていた。

んで、heroku環境で運用しようとすると、アクセスが来た時点でbundle installが走り出して、当然timeoutするのでコンテナが殺されて、あやふやな理解じゃだめだね、ということに気づいた。

dockerのvolumeはbuild中に中身を参照できない。

Dockerfileの中に書いたコマンドは、CMDとENTRYPOINT以外、buildのときに実行される。で、このタイミングでは、volumeの中身を読み書きすることは出来ない。
volumeはdocker runの時初めてマウントされる。なので、volumeの中のファイルに依存したコマンドを実行したり、volumeの中に書き込むようなコマンドは、docker run以降にしないといけない。

なんでかというと、docker buildコマンドに-vオプションがないから。
https://docs.docker.com/engine/reference/commandline/build/

つまり、volumeはそういう仕様なのだ。

docker build -v作ろうよ、というissueは過去何度か立ち上がっているのだけれど、こんな感じで却下されている

I think you'll find you get a lot of resistance to a build -v option. The docker team has been very opposed to flags which introduce non-reproducability between builds.
(私訳: build -vオプションには多くの抵抗があると思います。 Dockerチームは、ビルド間に再現性のないフラグを導入することに非常に反対しています)
https://github.com/moby/moby/issues/17745#issuecomment-236785473

そんなこと言ったらCOPYだって環境に依存して再現性がなくなる気がするんだけど。
とにかくそんな仕様で、build中に読み書きすることが出来ないので、/railsに読み書きしようとして失敗していたと。ふむ。

どうするのが良いか

開発環境だったら、起動後のbashからbundle installするか、CMDで起動するスクリプト内でbundle installする運用でも別に困らない。

本番だと、上で述べたようにherokuでアクセスが来てからbundle installがはじまる、みたいなことになるのは困るので、ちゃんとbuild時にbundle installを終わらせておく必要がある。
本番環境でソースを書き換えて即時実行なんて需要はないはずなので、ホストのファイルシステムをマウントするのではなくて、COPYでまるごとコピーするのが正解。
これはなぜかdocker build中でも実行できるので、必要なディレクトリを全部COPYしてbundle installすれば、build中でも実行することができる。

Dockerfile.production

WORKDIR /rails/
COPY . /rails/
RUN echo 'gem: --no-rdoc --no-ri' >> "$HOME/.gemrc"
ENV GEM_HOME /usr/local/bundle
ENV PATH $GEM_HOME/bin:$PATH
RUN gem install bundler \
  && bundle config --local path "$GEM_HOME" \
  && bundle config --local bin "$GEM_HOME/bin"
ENV BUNDLE_APP_CONFIG $GEM_HOME
RUN bundle install -j4 --retry=3 && bundle clean
CMD ["rails", "server", "-b", "0.0.0.0"]
ツイッターでシェア
みんなに共有、忘れないようにメモ

daisuke furukawa

おひるねのできるフリーランサー。「モバイラーズオアシス」の中の人でもあります。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント