はじめに
AlmaLinux 上で動いている,とある Rails プロジェクトにて。
Nokogiri のバージョンを 1.18.0 にしたところ,動かなくなった。
たとえば
rails -T
しただけで
/lib64/libm.so.6: version `GLIBC_2.29' not found (required by /usr/local/rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/nokogiri-1.18.0-x86_64-linux-gnu/lib/nokogiri/3.4/nokogiri.so) - /usr/local/rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/nokogiri-1.18.0-x86_64-linux-gnu/lib/nokogiri/3.4/nokogiri.so
とエラーが出る。
2022 年にも(各種バージョンは違うが)ほぼ同じ問題によって,あちこちで記事が書かれたようだ。
環境
- OS ver. AlmaLinux release 8.10 (Cerulean Leopard) Kernel 4.18.0-553.33.1.el8_10.x86_64
- ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
- Bundler version 2.6.2
原因
nokogiri は C で書かれた部分を含む拡張ライブラリーだが,主要なプラットフォーム向けに,C 部分のコンパイル済みパッケージも用意されている。
(このあたり,正確な用語を知らないので,間違ってたら教えてください)
そのため,対応 OS ではインストール時にコンパイラーが走らなくてよい。
ところが,今回の問題は,そのコンパイル済みのやつがシステムの環境と合ってないために起きたようだ。
AlmaLinux 8.10 は glibc 2.28 がインストールされているらしい。
このことは dnf info glibc
で確認できる。
一方,インストールした最新の nokogiri は
$ gem list nokogiri
*** LOCAL GEMS ***
nokogiri (1.18.0 x86_64-linux-gnu)
なのだが,こいつは glibc 2.29 を要求するらしい。惜しい,ぎりぎりダメじゃん。
Rails プロジェクトじゃなくても,単に
ruby -r nokogiri -e "puts Nokogiri::VERSION"
とやっただけで冒頭に掲げたエラーが出る。
(エラーメッセージの全体は長いが省略)
対策
エラーメッセージの中には,対処法がちゃんと書かれている。
私のように英語が苦手な人も,「あー,ゴチャゴチャなに言ってるかわかんねー,まーいーや,エラーメッセージでググっちゃえー」などと思わず,分からないなりに読もうとしてみるのがいいと思うよ。
件の「`GLIBC_2.29' not found」の直後に
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
とあった。
要するに「platform を ruby
として nokogiri をインストールしなはれ」と言っている。
これが何を意味するかというと,C 部分がコンパイル済みのやつをインストールするんじゃなくて,手元でコンパイルするようにインストールする,と。
(それがなんで「platform が ruby」なのかよく分からんが)
やり方としては
gem install nokogiri --platform=ruby
と
bundle config set force_ruby_platform true
と二つある,と。親切に具体的なコマンドまで書いてくれている。
まずは --platform=ruby
で
これは今までにも使ったことがあるオプションだ。
ではまず
gem uninstall nokogiri
でアンインストールしておいて,書かれているとおり
gem install nokogiri --platform=ruby
でインストール。
コンパイラーが走るので,約 1 分かかる。たるい。
この状態で Nokogiri が読めるか確認すると……
$ ruby -r nokogiri -e "puts Nokogiri::VERSION"
1.18.0
よっしゃー,解決!
……となれば話は簡単なのだが,Rails アプリ(に限らずなんらかのプロジェクト)でうっかり
bundle update
しようものなら,nokogiri 1.18.0 が入っているにもかかわらず,同バージョンのコンパイル済み版(nokogiri-1.18.0-x86_64-linux-gnu)が新たにインストールされてしまう。
ひ〜
force_ruby_platform 設定を試す
では次に
bundle config set force_ruby_platform true
を検討しよう。
よく分からんが,いかにも「platform=ruby
を強制する」感じがする。
上記のコマンドを実行すると,Bundler の設定が変わるようだ。
この「設定」というのは,プロジェクトディレクトリー(Gemfile
を置くところ)直下の .bundle/config
と,ユーザーのホームディレクトリー直下の .bundle/config
に書かれている(というか,そこから読み取るようになっている)。
これを,件の Rails アプリのディレクトリーで実行すると,その直下の .bundle/config
に
BUNDLE_FORCE_RUBY_PLATFORM: "true"
が追加された。
オプションなしでも,グローバルじゃなくてローカルの設定の変更になるのね。
(ちなみに,Gemfile の存在しないディレクトリーでやってみたら,.bundle/config
は作られなかった。エラーも出なかった)
この状態なら
bundle update
しても,「コンパイル済み版」がインストールされたりはしなかった。
めでたしめでたし。
しかし,これをやると,nokogiri だけでなく全部の gem で「自前コンパイル」になるんだよな? マジかよ。
glibc をバージョンアップするのは?
2022 年に多数書かれた記事の情報によると,glibc のバージョンを上げよう,という方向の解決方法はダメぽい(OS にがっつり噛んでいるので)。
AlmaLinux 8 はメンテナンスサポートが 2029 年 3 月 1 日までと長く使えるので安心していたが,こういうところでトラブルがいろいろ生じるのだろうなあ。
ちなみに AlmaLinux 9.5 で確認したところ,glibc のバージョンは 2.34 なので,こちらは余裕で大丈夫。
感想
- 疲れた。
- gem 1 個のインストールに 1 分もかかる(nokogiri の場合)のは嫌だな。
- この設定で全ての gem についてコンパイル済みが利用できなくなるのは嫌だな。
- gem のインストール時には何も起こらず,
require
で初めて問題が起きるのは嫌だな。 - AlmaLinux のサポート期間が長いのはいいけど,こういう問題を回避するためにアップデートを検討しなければならないとは……。
追記 2025-01-07
Nokogiri のリポジトリーに本件の issue が立っていた(すでにクローズされている):
[bug] v1.18.x requiring glibc 2.29 (Rocky 8.10 ships with 2.28) · Issue #3399 · sparklemotion/nokogiri
直接の原因は Nokogiri が C のプリコンパイルに使っている rake-compiler-dock において glibc の最小サポートバージョンが引き上げられたことであるようだ。
なんで引き上げたのかは,どうも CentOS のサポート終了が原因だったようなのだが1,ええと,AlmaLinux 8 も Rocky Linux 8 も 2029 年まで生きてますよ?
rake-compiler-dock の History.md を見ると,この変更は 2024-12-13 の v1.6.0 で行われたようだ。
それはともかく,issue 主さんの記述を見ていて,私が本記事で書いたのとは別の対策があることを知った。
つまり,Gemfile に
gem "nokogiri", force_ruby_platform: true
のように書けばよい。
gem
メソッドの force_ruby_platform
なんてオプション知らなかったぞ。
このやり方であれば,他の gem のインストールに影響を与えることなく,nokogiri だけ自前コンパイルにできる。
これは .bundle/config
で Bundler の動作を変える方法に比べて大きな利点だ。
しかし,短所もある。
Gemfile に書いちゃうわけなので,「本番サーバーでは自前コンパイルになってほしいが,手元の開発環境ではコンパイル済み版で」ということができない(しにくい)。
-
私の英語力では,そういう解釈で正しいのかよく分からん。 ↩