BerkshelfとChefのインストールに苦労した話
例えばberkshelfひとつ入れるのに、膨大な知識がいる。
もしあなたが単にPHPサーバを作りたいだけだったとしても、berkshelfをちゃんとしたモダンなやり方でいれるには膨大な知識がいる。
例えばruby, rbenv, ruby-build, gem, bundlerなどだ。
まあ、rbenvとruby-buildで最新のrubyを入れること自体はそんなに難しくない。Ruby言語の知識も必要ない。
運よくgem install berkshelfが一発で成功したら、別に問題はない。おめでとう。
そういう人はここから下は読む必要はないです。
ところがである。
ひとたびgem installが失敗したら、そこからは茨の道である。
インフラの問題
gem installしたら
"ERROR: Could not find a valid gem 'chef-solo' (>= 0) in any repository"
全然原因わからなくて自分の環境構築スキルの低さを自己嫌悪してたらRoute53障害のせいだったっぽい
— でぃーきゅーねお (@DQNEO) December 23, 2013
まずgemのレポジトリがAWSに依存している。これはつまりAWS障害がおこるとgem installがこけるということだ。だけど、下記のようなエラーを見て、それがAWSのインフラ障害が原因だと気付くだろうか。
すぐには気づかないだろう。
ERROR: While executing gem ... (Gem::Requirement::BadRequirementError) Illformed requirement ["berkshelf"]
"ERROR: Could not find a valid gem 'chef-solo' (>= 0) in any repository"
私はこの原因がわからず数時間悩んだ。様子を詳しく見るためにgem install --verboseしてみた。 そしたら同じレポジトリへのHTTPリクエストを何度も何度も繰り返してるように見えた。
いったいなんだろうと考え込んだ。
rubygemのURLにAmazonS3という文字が含まれていたこと、そのときtwitterのTLがAWSの障害騒ぎで盛り上がっていたこと、などから、これは単に通信障害のせいでないかと思った。
数時間待ってからgem install chefしてみたら成功した。
やはり通信障害のせいだった。
通信障害自体はどのホスティングシステムを使っても起こりうるのだからしょうがないのだけど、問題はエラーメッセージは非常にわかりづらく、ruby初心者は自分の環境構築がどこかで間違っていたのかと考えてしまうということだ。
ひとたびgem installがこけたらgemクライアントとrubygemレポジトリの間のHTTP通信がどうなっているのかに気を配らなければいけない。
例えあなたが単にPHPのサーバを構築したいだけだったとしても。
依存性の問題:C言語コンパイラ
cygwinでgem install knife-soloが失敗するこのつらみよ・・
— でぃーきゅーねお (@DQNEO) December 21, 2013
ERROR: Error installing knife-solo: ERROR: Failed to build gem native extension. Makefile:206: recipe for target `yajl.o' failed
— でぃーきゅーねお (@DQNEO) December 21, 2013
knife-solo がC言語のgem native extensionに依存してる悲しみ。 yajl.c がコンパイルでくなくてmakeがこける。yajl.cってなんぞ?
— でぃーきゅーねお (@DQNEO) December 21, 2013
chefやknife-soloは、yajl-rubyという別のgemに依存していて、このyajl-rubyはC言語のライブラリに依存しているのでgem installしたときにC言語コンパイラが走る。これはつまり、chefはC言語コンパイラに依存しているということだ。
あなたの環境でC言語コンパイルがちゃんと動かない場合、ここでこける。
gem install chef
Building native extensions. This could take a while...
ERROR: Error installing chef:
ERROR: Failed to build gem native extension.
make
gcc -I. -I/usr/include/ruby-1.9.1/i386-cygwin -I/usr/include/ruby-1.9.1/ruby/backward -I/usr/include/ruby-1.9.1 -I. -ggdb -O2 -pipe -fno-strict-aliasing -Wall -funroll-loops -o yajl.o -c yajl.c
Makefile:206: recipe for target `yajl.o' failed
make: *** [yajl.o] Error 1
私の場合はgccが(自分のミスで)壊れていたためにこのようなエラーが出た。makeとMakefileとgccの関係について多少なりとも経験と知識があれば、このエラーをみてもたじろがないかもしれない。
しかしあなたがmakeとMakefileとgccの関係について全く何もしらなかったら、どうだろうか。
knife-soloを使いたいだけなのに気が付いたらgccのコンパイルからやりなおしている自分がいるぽよ~
— でぃーきゅーねお (@DQNEO) December 21, 2013
gccをコンパイルしようとしたら、
configure: error: C compiler cannot create executables
gccをコンパイルするためのコンパイラが動かない!!
— でぃーきゅーねお (@DQNEO) December 21, 2013
もうこれはCygwinもくしはCygwin上のgcc, make, autoconfあたりのバグに違いないと考えて、それらのパッケージをアンインストールしてみた。再インストール中なう。
— でぃーきゅーねお (@DQNEO) December 21, 2013
gccアンインストールできたけど再インストールできなくなって詰んだ・・・ 私のCygwinからgccが消えた・・・
\(人生オワタ)/
— でぃーきゅーねお (@DQNEO) December 21, 2013
もうMac買うしかないのか。今まで意味もなく避けてきたけど。
— でぃーきゅーねお (@DQNEO) December 21, 2013
knife-solo, Berkshelfのインストールをしようと格闘してたらついうっかりgccをアンインストールしてしまい、復帰できなくなったアカウントがこちらになります。
— でぃーきゅーねお (@DQNEO) December 21, 2013
chefを入れるためにはC言語コンパイラがちゃんと動くようにしておかなくてはいけない。例えあなたが単にPHPのサーバを構築したいだけだったとしても。
依存性の問題:ライブラリ間の整合性
さて通信問題も解決してC言語コンパイラもちゃんと入ったとしよう。そしたらgem install chefとかgem install knife-soloあたりは成功するだろう。
でもgem install berkshelfはもっと難しい。
berkshelfはffiという別のgemに依存している。そしてffiを入れるときにC言語コンパイラがコンパイルを始める。
そしてこける笑
linking shared-object ffi_c.so
Call.o: 関数 `call_blocking_function' 内:
/home/DK/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/ffi-1.9.3/ext/ffi_c/Call.c:294: `ffi_call' に対する定義されていない参照です
collect2: エラー: ld はステータス 1 で終了しました
Makefile:232: recipe for target 'ffi_c.so' failed
make: *** [ffi_c.so] Error 1
ERROR: Error installing ffi:
ERROR: Failed to build gem native extension.
Building has failed. See above output for more information on the failure.
make failed, exit code 2
Gem files will remain installed in /home/DK/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/ffi-1.9.3 for inspection.
そしたらあなたはffi_c/Call.cの294行目のffi_call関数を見て首をかしげるわけだ。例えあなたが単にPHPのサーバを構築したいだけだったとしても。
そしてffiについてググりまくったり、ffiのレポジトリを覗いて同様のissue報告があがってないか調べたりしなきゃいけなくなる。
https://github.com/ffi/ffi
ffiについてのスライドシェアを見つけてスライドを読んで、「もしかしたらffiというのはrubygemの名前であって本体はlibffiという別のC言語製ライブラリなのかもしれない」などと考えているうちに、自分がもともと何をしたかったのか忘れてしまうかもしれない。
そしてlibffi本体のレポジトリを見つけてダウンロードする。
コンパイルする。そしてこける笑
libtool: link: gcc -shared src/.libs/prep_cif.o src/.libs/types.o src/.libs/raw_api.o src/.libs/java_raw_api.o src/.libs/closures.o src/x86/.libs/ffi.o src/x86/.libs/win64.o -O3 -march=corei7 -o .libs/cygffi-6.dll -Wl,--enable-auto-image-base -Xlinker --out-implib -Xlinker .libs/libffi.dll.a
src/.libs/prep_cif.o:prep_cif.c:(.text+0x53a): undefined reference to `ffi_prep_closure_loc'
さあ、prep_cif.cファイルのffi_prep_closure_loc関数について調べよう、・・・と思うだろうか?いや思わないだろう。
なぜならあなたは単にBerkshelfを使って便利に鼻血を出したいだけだからだ。
Cygwinでgem install ffi するとビルドが失敗する。libffiをgit cloneしてmakeしても失敗する。
詰んだ・・・
https://t.co/4ua30nYdJf
— でぃーきゅーねお (@DQNEO) December 28, 2013
Berkshelf使いたいだけなのにffiとかlibffiのC言語のコンパイルエラーと戦わなければならないこの悲しみ
— でぃーきゅーねお (@DQNEO) December 28, 2013
Berkshelfはcookbookの依存関係を管理するツールなんだけど、肝心のberkshelfがffiというgemに依存しててfifiはlibffiとかいうC言語ライブラリに依存しててそのライブラリがビルドできなくて結局Bekrshelfのインストールができなくて人生つらい
— でぃーきゅーねお (@DQNEO) December 28, 2013
バージョンの整合性問題
さて深掘りしてきた階段をちょっと逆戻りして上ってみよう。どこまで戻ればいいのか。
そもそもあなたは何がやりかったのか。そうだ、Berkshelfを使いたかったのだ。
とりあえずBerkshelfの公式サイトでも見てみよう。
http://berkshelf.com/
公式サイトを見ると、「依存性のエラーを解決したいならversion 3を使え」と書いてある。
さあ、まさにこれだ。
あなたは、Githubから https://github.com/berkshelf/berkshelf 最新版のbekrshelfをcloneしてきて、ローカルgemをインストールする方法を調べて、ビルドする。
そしてコケる笑
なぜならberkshelfが要求しているrubyのバージョンとあなたがさっきrbenvで入れたrubyのバージョンが一致しないからだ。
この問題を解決するためにあなたはrbenvの動作原理を勉強する。
この記事は何回読んでも素晴らしい。大変勉強になる。 / "rbenvの切り替えの仕組み...と、他言語での実験 - すぎゃーんメモ" http://t.co/hzxNb9V4o4
他29コメント http://t.co/DMMY4rXcnw
— でぃーきゅーねお (@DQNEO) December 28, 2013
そして.ruby-versionというファイルが邪魔しているのだと知り、削除する。
rbenv local --unset
さあ、
gem install berkshelf-3.0.0.beta4.gem
きた!そして
rbenv rehash
そして!
$ berks --version
3.0.0.beta4
やったー!!!berkshelfがついに入ったーーー!!・・・でもちょっと待ってほしい。
本当にそれは便利なの?
あたなはもしかしたら、運よく一発でgem install chef, gem install berkshelfが成功したかもしれない。でもそれはたまたまだ。
たまたま、依存するすべてのライブラリの整合性がとれていたからだ。
これだけ無数のソフトウェア・ライブラリに依存してるのだから、たいていどこかでこける。はまる。
こけたら全部自分で解決しないといけない。
yajl.cがコンパイルできない原因について考えないといけない。
gem ffi とlibffiの関係について考えないといけない。
rbenvと.ruby-versionの関係について考えないといけない。
例えあなたが単にPHPのサーバを構築したいだけだったとしても。
本当にそれは便利なの?
Mac使えば解決するのか?
「Cygwin使ってるお前が悪い。Mac使え」と言われるかもしれない。そうかもしれない。ただし、今回Cygwinのgcc環境が壊れたのは、私がCygwinの64bit化対応をミスったのが原因なのでCygwinのことはどうか責めないでほしい。(32bit → 64bitみたいなアーキテクチャ大移動は数年に1回しか起こらない)
Macを使えばより問題は起こりにくいだろう。ユーザ数もMacの方が多いだろう。
でも大事なことは、Macを使ったからといって依存数が減るわけじゃないということだ。
Macを使ってもC言語コンパイラが走ることに何ら変わりはないし、依存先のライブラリのインストールがこけたら解決しなければいけないことに何ら変わりはない。
Vagrantがgemを捨てたのは賢明な選択だった
私がここまでVagrantをあえてdisらなかったのには理由がある。(別にChefやBerkshelfをdisってるわけでもないけど)Vagrantはver1.1からgem依存をやめてruby処理系そのものをVagrantパッケージに内蔵するようにしたからだ。
この決断は上で説明したような依存性問題からユーザを解放するためにやったことで、とても正しい選択だったと思う。
MITCHELL HASHIMOTO氏のAbandoning RubyGemsというブログ記事の中で、Vagrantがgemを捨てる決断にいたる経緯が詳しく書かれている。
Vagrant 1.1+ no longer supports RubyGems as an installation method. Instead, you must install Vagrant 1.1+ using pre-made packages or installers. For folks used to the gem-based installation, this has caused a mixture of confusion and disdain. In this post, I enumerate my reasons for abandoning RubyGems, and why it is better for the Vagrant community long-term.
[訳] Vagrantは1.1からはインストール方法としてRubyGemsをサポートしません。
その代わりにビルド済みパッケージまたはインストーラを使う必要があります。
gemベースのインストールに慣れた人たちにとって、これは混乱と軽蔑をもたらしてしまいました。
この記事で、VagrantがなぜRubyGemsを捨てたのか、なぜその方がVagrantコミュニティにとって長期的メリットがあるのか、を説明します。
Abandoning RubyGems
まとめ
「高級言語のライブラリを組み合わせて作られたサーバ構築ツール」を手放しで礼賛する風潮について問題提起しました。もしそういうツールを使うなら、トラブルが起きたときにソフトウェアレイヤーの階層をどこまでも深掘りしていくだけの覚悟と知識を持ちましょう。
Happy Yak Shaving!