armとintelのdocker-compose共通化へ向けて(Ruby on Rails編)

docker

問題

M1macでDockerのrubyイメージからを使おうとすると、nokogiriの部分でエラーが出ます。

% docker run --rm -it ruby:3.0.3-buster bash
root@489921d6a766:/# gem i nokogiri
Fetching nokogiri-1.13.4-aarch64-linux.gem
Successfully installed nokogiri-1.13.4-aarch64-linux
1 gem installed
root@489921d6a766:/# irb
irb(main):001:0> require 'nokogiri'

ERROR: It looks like you're trying to use Nokogiri as a precompiled native gem on a system with glibc < 2.17:

  /lib/aarch64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by /usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri/3.0/nokogiri.so) - /usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri/3.0/nokogiri.so

  If that's the case, then please install Nokogiri via the `ruby` platform gem:
      gem install nokogiri --platform=ruby
  or:
      bundle config set force_ruby_platform true

  Please visit https://nokogiri.org/tutorials/installing_nokogiri.html for more help.

/usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri/extension.rb:7:in `require_relative': /lib/aarch64-linux-gnu/libm.so.6: version `GLIBC_2.29' not found (required by /usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri/3.0/nokogiri.so) - /usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri/3.0/nokogiri.so (LoadError)
	from /usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri/extension.rb:7:in `<top (required)>'
	from /usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri.rb:10:in `require_relative'
	from /usr/local/bundle/gems/nokogiri-1.13.4-aarch64-linux/lib/nokogiri.rb:10:in `<top (required)>'
	from <internal:/usr/local/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:160:in `require'
	from <internal:/usr/local/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:160:in `rescue in require'
	from <internal:/usr/local/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:149:in `require'
	from (irb):1:in `<main>'
	from /usr/local/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
	from /usr/local/bin/irb:23:in `load'
	from /usr/local/bin/irb:23:in `<main>'
<internal:/usr/local/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- nokogiri (LoadError)
	from <internal:/usr/local/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
	from (irb):1:in `<main>'
	from /usr/local/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
	from /usr/local/bin/irb:23:in `load'
	from /usr/local/bin/irb:23:in `<main>'
irb(main):002:0>

原因は、Nokogiriが1.11あたりからプリコンパイルされて配布されるようになりました。そのバイナリは、glibc 2.17でコンパイルされています。

しかしながら、arm用にコンパイルされたnokogiriが参照している /lib/aarch64-linux-gnu/libm.so.6が2.29を必要としているっぽくてエラーになります。

解決方法

静的リンクするか、独自でコンパイルするのが良さそうです。静的リンクはバイナリサイズがでかくなりそうなので独自でコンパイルします。

Bundlerを使っている場合

Bundler使っている場合は以下を打って自分でコンパイルするように設定します。

# bundler 2.1以降
% bundle config set force_ruby_platform true

# bundler 2.0以前
% bundle config force_ruby_platform true

そうすると、.bundle/configに以下が入ります。

---
BUNDLE_FORCE_RUBY_PLATFORM: "true"

もし、.bundleディレクトリを.gitignoreで無視するような設定を書いていたり、ユーザ側で設定した.bundleディレクトリを使うようにしている場合はホームディレクトリの設定ファイルに書き込んであげるのがよいです。

bundle config set --global force_ruby_platform true

実行例

% docker run --rm -it ruby:3.0.3-buster bash
root@11a428c7e8da:/# bundle config set force_ruby_platform true
root@11a428c7e8da:/# bundle init
Writing new Gemfile to //Gemfile
root@11a428c7e8da:/# bundle add nokogiri
Fetching gem metadata from https://rubygems.org/.......
Resolving dependencies...
Fetching gem metadata from https://rubygems.org/.......
Resolving dependencies...
Using bundler 2.2.32
Fetching racc 1.6.0
Fetching mini_portile2 2.8.0
Installing racc 1.6.0 with native extensions
Installing mini_portile2 2.8.0
Fetching nokogiri 1.13.4
Installing nokogiri 1.13.4 with native extensions
root@11a428c7e8da:/# bundle exec irb
irb(main):002:0> require 'nokogiri'
=> true

Bundlerを使っていない場合

--platform=ruby オプションをつけてインストールします。

root@b4ca7905b3a5:~# gem install nokogiri --platform=ruby
Fetching nokogiri-1.13.4.gem
Building native extensions. This could take a while...
Successfully installed nokogiri-1.13.4
1 gem installed
root@b4ca7905b3a5:~# irb
irb(main):001:0> require 'nokogiri'
=> true
root@b4ca7905b3a5:/# gem list|grep nokogiri
nokogiri (1.13.4)

手元での変更

結果的に手元のプロジェクトでは以下のような変更をDockerfileに行いました。

+ RUN bundle config set --global force_ruby_platform true

参考資料

詳細はこちらのissueにかかれています。

Feedback and final to-do items for precompiled native gems on Linux and OSX · Issue #2075 · sparklemotion/nokogiri
This issue exists to capture feedback and action items about the precompiled native gems for Linux and OSX that we're shipping in the Nokogiri v1.11 release...

不確実情報

環境によっては、arm用のbinaryがインストールされたりもするので、これが実現すればコンパイルの時間が取られなくて良いのでどういったときにこのような設定ができるのかを検証中です。

diff –git a/Gemfile.lock b/Gemfile.lock
index 09a17e2..e17967d 100644
— a/Gemfile.lock
+++ b/Gemfile.lock
@@ -180,6 +180,8 @@ GEM
net-protocol
timeout
nio4r (2.5.8)
+ nokogiri (1.13.3-aarch64-linux)
+ racc (~> 1.4)
nokogiri (1.13.4-x86_64-linux)
racc (~> 1.4)
parallel (1.22.1)
@@ -318,6 +320,7 @@ GEM
zeitwerk (2.5.4)
PLATFORMS
+ aarch64-linux
x86_64-linux
DEPENDENCIES
diff –git a/docker-compose.yml b/docker-compose.yml
index 2ebf9f0..8a02a5e 100644
— a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,11 +1,12 @@
version: '3'
services:
db:
image: mysql:8.0
+ image: mysql/mysql-server:8.0
volumes:
– mysql_data:/var/lib/mysql:cached
environment:
MYSQL_ROOT_PASSWORD: 0fj4l3muBP7tpKtg
+ MYSQL_ROOT_HOST: '%'
app:
build: .
view raw arm.diff hosted with ❤ by GitHub

コメント

タイトルとURLをコピーしました