12
6

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.

DevContainer で Ruby 開発する際に厄介な BUNDLE_APP_CONFIG

Last updated at Posted at 2020-09-29

TL;DR

  • Docker 公式の ruby イメージでセットされている環境変数 BUNDLE_APP_CONFIG は DevContainer 内での開発において邪魔になることがある
  • BUNDLE_APP_CONFIG の影響を無くしたい場合は devcontainer.json に以下を記述すると良い
.devcontainer/devcontainer.json
{
  // ...
  "remoteEnv": {
    "BUNDLE_APP_CONFIG": null
  },
  // ...
}

厄介な BUNDLE_APP_CONFIG

VSCode Remote - Containers での Ruby 開発では大抵の場合、Docker 公式の ruby イメージ をベースイメージにして DevContainer を作ることになる1かと思いますが、このイメージには厄介な問題があります。

それが環境変数 BUNDLE_APP_CONFIG です。
Dockerfile 内で ENV 命令によりデフォルトで /usr/local/bundle に設定されています。

BUNDLE_APP_CONFIG は bundler のアプリケーションローカルな設定 (bundle config set --local した際に作られる設定) をどこに保存するかを指定するための環境変数です。
この環境変数が設定されていない場合、bundler はアプリケーションルートの .bundle/ 以下に設定を作成します。

さて、話を移して VSCode の DevContainer で Ruby 開発をする場合、インストールした gem を永続化しつつコンテナにマウントするには以下のようにワークスペース内の vendor ディレクトリ等に突っ込んでおきたいところです。2

$ bundle config set --local path vendor/bundle

この設定により path 設定が記述された設定ファイルが通常は .bundle/config として保存されるはずです。ローカル設定にしたことでワークスペース内に設定ファイルが作られ、コンテナを破棄しても再設定は不要となります。
しかし、前述のように Docker 公式の ruby イメージでは BUNDLE_APP_CONFIG が設定されてしまっているため、このローカル設定は /usr/local/bundle/config に保存されてしまいます。

bundle config で確認してみると以下のような感じです。

$ bundle config
Settings are listed in order of priority. The top value will be used.
path
Set for your local app (/usr/local/bundle/config): "vendor/bundle"

app_config
Set via BUNDLE_APP_CONFIG: "/usr/local/bundle"

silence_root_warning
Set via BUNDLE_SILENCE_ROOT_WARNING: true

これでは Rebuild などによりコンテナを破棄した際に設定が失われてしまうため、都度 bundle config ... を実行するか、/usr/local/bundle をホストからマウントするようにするなどの工夫が必要になってしまいます。

スマートに解決するには BUNDLE_APP_CONFIG 自体を何とかした方が良いでしょう。

ちなみに公式イメージで BUNDLE_APP_CONFIG が設定されているのは上記の逆の考え方で、「ホスト側の .bundle/config がコンテナ内に影響を与えないようにするため」のようです3

2020/09/30 追記
コンテナ内でしか作業しない (= ホストと .bundle/vendor/ を共有する必要がない) のであれば BUNDLE_APP_CONFIG はそのままにして別途作成した volume を /usr/local/bundle にマウントした方が良さそうです。特に Docker for Mac では bind mount 時の Disk I/O が遅いため、ワークスペース内に gem を入れる設定だと bundle install 等に時間がかかってしまう可能性があります。

対策

× BUNDLE_APP_CONFIG に空文字を設定する

公式ドキュメント に記載されている方法です。

The environment variables we set are canonically listed in the above-linked Dockerfiles, but some of them include GEM_HOME, BUNDLE_PATH, BUNDLE_BIN, BUNDLE_SILENCE_ROOT_WARNING, and BUNDLE_APP_CONFIG.

If these cause issues for your use case (running multiple Ruby applications in a single container, for example), setting them to the empty string should be sufficient for undoing their behavior.

結論から言うとこの方法ではうまくいきません。
bundler は BUNDLE_APP_CONFIG の有無だけを見ている4ので、空文字が設定されている場合はカレントディレクトリ直下に config ファイルが作成されます。

実験してみましょう。
DevCotainer 用の Dockerfile で BUNDLE_APP_CONFIG に空文字をセットします。

devcontainer/Dockerfile
FROM ruby:2.7

# 空文字を設定
ENV BUNDLE_APP_CONFIG=

# 以下略

DevContaier をビルドして中に入ってみます。
ターミナル上ではなぜか BUNDLE_APP_CONFIG が消えて問題なく動作します (理由は不明ですが VSCode の実装の問題かもしれません)。

$ bundle config
Settings are listed in order of priority. The top value will be used.
silence_root_warning
Set via BUNDLE_SILENCE_ROOT_WARNING: true
$ bundle config set --local path vendor/bundle
$ bundle config
Settings are listed in order of priority. The top value will be used.
path
Set for your local app (/workspace/sample_app/.bundle/config): "vendor/bundle"

silence_root_warning
Set via BUNDLE_SILENCE_ROOT_WARNING: true

問題となるのはデバッグ時です。
デバッガ (rdebug-ide) を Gemfile で管理し、bundle install でインストールしているケースを想定します。
以下のような launch.json になるはずです。

.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Local File",
      "type": "Ruby",
      "request": "launch",
      "program": "${workspaceFolder}/main.rb",
      "useBundler": true
    }
  ]
}

デバッグを実行すると DEBUG CONSOLE に以下のように必要な gem が見つからない旨のエラーが出てしまいます。

bundler: failed to load command: rdebug-ide (/usr/local/bundle/bin/rdebug-ide)
Bundler::GemNotFound: Could not find concurrent-ruby-1.1.7 in any of the sources
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:86:in `block in materialize'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `map!'
  /usr/local/lib/ruby/2.7.0/bundler/spec_set.rb:80:in `materialize'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:170:in `specs'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:237:in `specs_for'
  /usr/local/lib/ruby/2.7.0/bundler/definition.rb:226:in `requested_specs'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:101:in `block in definition_method'
  /usr/local/lib/ruby/2.7.0/bundler/runtime.rb:20:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler.rb:149:in `setup'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `block in <top (required)>'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:136:in `with_level'
  /usr/local/lib/ruby/2.7.0/bundler/ui/shell.rb:88:in `silence'
  /usr/local/lib/ruby/2.7.0/bundler/setup.rb:20:in `<top (required)>'

これは、デバッグ用のサブプロセスでは BUNDLE_APP_CONFIG="" が効いているために .bundle/config が参照されず path 設定が読み込まれないためです。

BUNDLE_APP_CONFIG.bundle のパスを設定してしまう (Dockerfile)

例えば以下のように Dockerfile などでローカル設定用の .bundle の絶対パスを指定してしまう方法です。

devcontainer/Dockerfile
ENV BUNDLE_APP_CONFIG=/workspace/.bundle

この方法であれば大体うまくいきますが、モノレポに複数のプロジェクトを突っ込んでいるケース (Gemfile が複数あるケース) には対応できません。

BUNDLE_APP_CONFIG.bundle のパスを設定してしまう (VSCode 設定)

例えば上記のデバッグがうまくいかない問題は以下のように解決できます。

.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Local File",
      "type": "Ruby",
      "request": "launch",
      "program": "${workspaceFolder}/main.rb",
      "useBundler": true,
      "env": {
        "BUNDLE_APP_CONFIG": "${workspaceFolder}/.bundle"
      }
    }
  ]
}

この方法であれば Multi-root Workspace にすることでモノレポのケースにも対応することができます。
ただしこの設定だけではデバッグ用のサブプロセスにしか有効にならないので、必要に応じて settings.json のターミナル設定や tasks.json にも同様の設定を記述する必要があります。
また、何らかの理由で BUNDLE_APP_CONFIG を設定して使っている人と launch.json 等の設定を共有することが難しくなります。

〇 devcontainer.json の設定で BUNDLE_APP_CONFIG を削除する

下記のような設定を devcontainer.json に追加することで、環境変数を空にするのでも上書きするのでもなく削除 (unset) することができます。

.devcontainer/devcontainer.json
{
  // ...
  "remoteEnv": {
    "BUNDLE_APP_CONFIG": null
  },
  // ...
}

この方法であればコンテナ内の VSCode プロセス及びそのサブプロセス全てで BUNDLE_APP_CONFIG が削除されます。
個人的にはこの方法が最も副作用が少なそうなのでおすすめです。

  1. mcr.microsoft.com/vscode/devcontainers/ruby もベースはこのイメージです

  2. 余談ですが bundle install--path オプションは非推奨になったんですね…最近知りました

  3. https://github.com/docker-library/ruby/issues/129

  4. 2020年9月現在の最新 v2.4.1 ではこうなっています

12
6
0

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
12
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?