[2013-05-19-1] にインストールしたUbuntu 12.04はうっかり32bit版でした。。仕方がないのでUbuntu 14.04へのアップグレードがてら、クリーンインストールすることにしました。

管理画面からポチポチしてOSのインストールは完了。CookはあらかじめVirtualBoxとEC2で確認していたので万全です(キリッ

と思いきや、いつまでたってもRecipeが処理されません…。

$ knife solo cook masutaka.net
Running Chef on masutaka.net...
Checking Chef version...
Uploading the kitchen...
Generating solo config...
Running Chef...
Starting Chef Client, version 11.16.2
(ここでダンマリ)

これまた仕方がないので、サーバ側の/opt/chef/embedded/lib/ruby/gems/1.9.1/gems以下をprintfデバッグ(というかpデバッグか)で調べたところ、Chefがサーバ情報を取得するときに使うOhaiのGCEプラグインから返ってこないことがわかりました。GCEとはGoogle Compute Engineです。なんでChefが?

で、結論から言うと、GCEプラグインをOFFにしたら解決しました。

knife solo prepareのあとに/opt/chefが作られるので、/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/ohai-7.4.0/lib/ohai/config.rbを以下のように修正。あとはknife solo cookするだけです。knife solo bootstrapは出来ないことになります。

default :disabled_plugins, []

default :disabled_plugins, [:GCE]

knife soloはchef-soloをリモートから実行するコマンドで、

/opt/chef/embedded/bin/ruby /usr/bin/chef-solo -c /home/masutaka/chef-solo/solo.rb -j /home/masutaka/chef-solo/dna.json
のようなchef-soloコマンドを実行します。root権限だったかな?

この/home/masutaka/chef-solo/solo.rbに

Ohai::Config[:disabled_plugins] = [:GCE]
を書き、前述のchef-soloコマンドを実行すれば/opt/chef以下を修正する必要はありませんが、残念ながらこのファイルはknife solo cook時に自動生成されます。

ローカルの.chef/knife.rbに書いても無視されました。残念。そういうわけで、今はprepareをした後はサーバにログインしてファイルを書き換え、そのあとにcookというイマイチなことをしています。

ではなぜ、さくらのVPSだけダンマリになるのか?

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/ohai-7.4.0/lib/ohai/mixin/gce_metadata.rbのここに違いがありました。

def can_metadata_connect?(addr, port, timeout=2)
  t = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
  saddr = Socket.pack_sockaddr_in(port, addr)
  # さくらはこの行に来る。
  connected = false

portは80、addrはmetadata.google.internalです。

さくら以外はおそらくSocket.pack_sockaddr_inで例外が発生して、それ以降処理されないのでしょう。さくらはそれ以降処理され、無限ループに入り込んでしまいます。

irbでも再現出来ました。さくら以外は例外が発生しています。さくらは壊れた文字列が返ってきます。

# さくら以外(Cookできる)
$ irb -r socket
irb(main):001:0> Socket.pack_sockaddr_in(80, 'metadata.google.internal')
SocketError: getaddrinfo: nodename nor servname provided, or not known
        from (irb):1:in `pack_sockaddr_in'
        from (irb):1
        from /Users/masutaka/.rbenv/versions/2.1.2/bin/irb:11:in `<main>'

# さくら(Cookでダンマリ)
$ irb -r socket
irb(main):001:0> Socket.pack_sockaddr_in(80, 'metadata.google.internal')
=> "\x02\x00\x00PE\xAC\xC9\xD0\x00\x00\x00\x00\x00\x00\x00\x00"

irb(main):002:0> require 'nkf'

irb(main):003:0> NKF.nkf('-w8', Socket.pack_sockaddr_in(80, 'metadata.google.internal'))
=> "\u0002\u0000\u0000PEャノミ\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"

pingでも違いあり。

# さくら以外(Cookできる)
$ ping metadata.google.internal
ping: cannot resolve metadata.google.internal: Unknown host

# さくら(Cookでダンマリ)
$ ping metadata.google.internal
PING metadata.google.internal.net (69.172.201.208) 56(84) bytes of data.

さくらのDNSサーバはさくら奨励の133.242.0.3と133.242.0.4を指定。Google Public DNS (8.8.8.8と8.8.4.4)に変えても変わらず。

ここで調査は行き詰まり。cookが面倒になってしまったのは残念。

ブラジルの人も困っていました。
GCE Plugin infinite loop · Issue #382 · opscode/ohai

追記(2016-07-19):
職場の同僚が EC2 で同じ現象でハマっていて、この記事の方法で回避できたそうです。