250
207

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Docker Compose + Railsでイメージ内でbundle installしているはずなのにgemが無いとエラーがでる。

Last updated at Posted at 2019-08-28

背景

docker-composeでrails環境を構築したが起動せず。
gemが無いと言われているのでdocker-compose run 'コンテナ' bundle installの実行で解決。
けどなんでDockerfile内でbundle installしているのに再度bundle installしないといけないんだろうと疑問に思ったので調査してみました。

先に結論

volumeに古い情報が保存されており、最新の情報がvolumeに上書きされてしまう。

エラー再現例

構築時のファイル例

Dockerfile
FROM ruby:2.6.3

RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       libpq-dev \
                       postgresql-client &&\
    rm -rf /var/lib/apt/lists/*

# install nodejs
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get install -y nodejs

# install yarn
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

ENV APP_HOME /my_app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock

RUN bundle install

ADD . $APP_HOME
docker-compose.yml
version: '3'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    command:
      [ "bash", "-c", "rm -f tmp/pids/server.pid; RAILS_ENV=development bundle exec rails s -b 0.0.0.0" ]
    volumes:
      - .:/my_app
      # volumeを使用してbundle installしてきたものを永続化
      - bundle:/usr/local/bundle 
volumes:
  bundle:
Gemfile
source 'https://rubygems.org'
gem 'rails', '6.0.0'

空のGemfile.lock

$ touch Gemfile.lock

よくある構築コマンド例

# railsプロジェクトを作成
$ docker-compose run web bundle exec rails new . --force --skip-bundle

# Dockerfileからイメージを作成
$ docker-compose build

# コンテナ(rails)の起動
$ docker-compose up

を実行すると

web_1  | bundler: failed to load command: rails (/usr/local/bundle/bin/rails)
web_1  | Bundler::GemNotFound: Could not find gem 'sqlite3 (~> 1.4)' in any of the gem sources listed in your Gemfile.
...

と出て起動しない。

コンテナに入ってgemを確認してみると

$ docker-compose run web ls /usr/local/bundle/gems
actioncable-6.0.0      erubi-1.8.0          rack-test-1.1.0
actionmailbox-6.0.0    globalid-0.4.2       rails-6.0.0
actionmailer-6.0.0     i18n-1.6.0           rails-dom-testing-2.0.3
actionpack-6.0.0       loofah-2.2.3         rails-html-sanitizer-1.2.0
actiontext-6.0.0       mail-2.7.1           railties-6.0.0
actionview-6.0.0       marcel-0.3.3         rake-12.3.3
activejob-6.0.0        method_source-0.9.2  sprockets-3.7.2
activemodel-6.0.0      mimemagic-0.3.3      sprockets-rails-3.2.1
activerecord-6.0.0     mini_mime-1.0.2      thor-0.20.3
activestorage-6.0.0    mini_portile2-2.4.0  thread_safe-0.3.6
activesupport-6.0.0    minitest-5.11.3      tzinfo-1.2.5
builder-3.2.3          nio4r-2.5.0          websocket-driver-0.7.1
concurrent-ruby-1.1.5  nokogiri-1.10.4      websocket-extensions-0.1.4
crass-1.0.4            rack-2.0.7           zeitwerk-2.1.9

docker-compose buildのときにbundle installしたはずなのにgem 'rails'でインストールしたgemしか入っていない。(rails newで更新されたGemfileのgemがインストールされていない)

なぜ起きるのか

下記条件で発生します。

  • bundle installしてきた内容をvolumeを使用してデータを永続化をしている。
  • rails newで--skip-bundleしている。

ポイント

  • docker-compose buildではvolumeと紐付かない
  • docker-compose runはvolumeと紐づく。

原理

# 初回のbuildが走る。bundle installで ”railsのみ” がコンテナ内の/usr/local/bundleにインストールされる。
# volumeと紐づくので ”railsのみ” がvolumeに保存される。
$ docker-compose run web bundle exec rails new . --force --skip-bundle

# 新しいGemfileのgemがコンテナ内の/usr/local/bundleにインストールされる。
# volumeと紐づかないのでvolumeは更新されない。
$ docker-compose build

# volumeと紐付いたタイミングで/usr/local/bundleがvolumeに上書かれてしまう。
$ docker-compose up

対応

  • docker-compose run web bundle installでvolumeを更新する。
  • volumeを一回削除し再度作成することによって、volumeの中身を/usr/local/bundleと同じにする。
  • rails newする時に--skip-bundleを使用しない。

疑問

docker-compose run 'コンテナ' bundle installをどうせ走らせるのならDockerfileからbundle installを削除していいのでは。

イメージとdocker-composeを1セットと考えるとそう思いますが、同じイメージを使ってvolumeを使わないパターンもあり得るのでbundle installをDockerfileから削除するのが必ずしも正しいわけではないのかなと感じました。
開発する上で下記を意識すれば問題ないかと思います。

  • gemをvolumeで管理しない:docker-compose buildでgemを管理する。
  • gemをvolumeで管理する:docker-compose run 'コンテナ' bundle installでgemを管理する。(docker-compose buildbundle installが走るのは我慢する?)

さいごに

Qiitaとかにあるチュートリアルをそのまま実行して起動できなくてハマった方もいると思うので、少しでも参考になればと思います。
間違えや不足などあればご指摘頂ければ嬉しいです。

250
207
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
250
207

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?