knife-soloをアップデートしたらdockerを使ったサーバCIに失敗した
About
dockerを使ったサーバCI(dockerの使い方としていいか悪いかは別として)をしていますが、最近knife-soloをアップデートしたタイミングでCIがコケるようになりました。原因調査したので、備忘録として残しておきます。
概要
CircleCIを使ってdockerコンテナを2台起動し、その2台に対してcookしています。
$ cat circle.yml machine: timezone: Asia/Tokyo services: - docker checkout: post: - git submodule sync - git submodule update --init # use submodules dependencies: cache_directories: - "~/cache" pre: - cp ./.ssh/ssh-config.circleci ~/.ssh/config - ./script/docker-build.sh - docker run -d --privileged -p 40000:22 docker/centos - docker run -d --privileged -p 40001:22 docker/centos test: pre: - bundle exec knife solo cook -o dstat ci-docker01 - bundle exec knife solo cook -o dstat ci-docker02 override: - bundle exec rake spec:ci01 rake spec:ci02
$ cat $ .ssh/ssh-config.circleci Host ci-docker01 HostName 127.0.0.1 User docker Port 40000 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile ~/.ssh/id_ci.docker IdentitiesOnly yes LogLevel FATAL Host ci-docker02 HostName 127.0.0.1 User docker Port 40001 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile ~/.ssh/id_ci.docker IdentitiesOnly yes LogLevel FATAL
調査内容
エラーログ
エラーログを見ると下記のようにdna.jsonが無いと言われています。
$ be knife solo cook ci-docker02 Running Chef on ci-docker02... Checking Chef version... Uploading the kitchen... Generating solo config... Running Chef: sudo chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json [2015-09-03T14:55:12+00:00] WARN: ***************************************** [2015-09-03T14:55:12+00:00] WARN: Did not find config file: /home/docker/chef-solo/solo.rb, using command line options. [2015-09-03T14:55:12+00:00] WARN: ***************************************** [2015-09-03T14:55:12+00:00] FATAL: Cannot load configuration from /home/docker/chef-solo/dna.json ERROR: RuntimeError: chef-solo failed. See output above.
WARNのログを見ると、~/chef-solo/solo.rbが無いと言われています。
chef-solo -c
から始まるコマンドはsolo.rbからdna.jsonを作るコマンドなので、solo.rbができていないのがそもそものエラーの原因です。
ここで、v0.4.2からv0.5.1までのcommitを切り替えていき、問題を探って行きました。その結果原因らしきものがわかりました。
原因はControlMasterのソケットが重複してしまったから
knife-soloのv0.4.2では、ControlMasterがデフォルトで利用されていませんでしたが、v0.5系からControlMasterを使うことでrsyncの速度向上を計ったようです。 Some small efficiency gains plus controlmaster for rsync by matschaffer · Pull Request #440 · matschaffer/knife-solo · GitHub
具体的なところとしてはsshのオプションに設定が追加されています。
ControlMasterとは
このような説明がありました。
単一のネットワーク接続で、複数のセッションを共有します。これが"yes"に設定されている場合、ssh (1) はControlPathの引数で指定された制御用のソケットを listen します。追加のセッションを開くには、同じソケットをControlPathに指定し、ControlMasterには"no"を指定します (これがデフォルト)。こうすると、そのセッションは新たにネットワーク接続するのではなく、すでにある Master インターフェイスのネットワーク接続の再利用を試みるようになります。ただし制御用ソケットが存在しないか、listen されていない場合は通常の接続になります。
初めて接続したセッションを保持して、2回目以降そのセッションを使いまわす仕組みのようです。毎度セッションを作る必要がないので、その分のオーバヘッドを減らすことができ、効率的に利用することができます。
今回の場合で言うと、ControlPathが ~/.chef/knife-solo-sockets/
にホスト名で作成されますが、DockerCIの場合は同じホストに対しアクセスするようにしていた(ポートを分けて2つのコンテナを起動しています)ので、ソケットファイル名が重複してしまい、通信がうまくいってないようです。
回避策
ControlMasterを利用しない
下記のように--ssh-control-master no
オプションを渡すことで、ControlMasterを無効にすることができます。
$ bundle exec knife solo cook docker02 --ssh-control-master no
ホスト名の指定方法を変える。
今回の問題はホスト名がかぶっているからこそ起きるので、ホスト名を変えれば可能です。
Host ci-docker02 HostName 0.0.0.0 User docker Port 40001 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile ~/.ssh/id_ci.docker IdentitiesOnly yes LogLevel FATAL
上記のように2つ目は0.0.0.0でアクセスさせます。これで一応できます。(イマイチですが)
ControlMasterの仕組みについてはもう少し調査したいです。