tjinjin's blog

さらなる高みに1mmずつ近づくブログ

knife-soloをアップデートしたらdockerを使ったサーバCIに失敗した

About

dockerを使ったサーバCI(dockerの使い方としていいか悪いかは別として)をしていますが、最近knife-soloをアップデートしたタイミングでCIがコケるようになりました。原因調査したので、備忘録として残しておきます。

概要

CircleCIを使ってdockerコンテナを2台起動し、その2台に対してcookしています。

github.com

$ 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のオプションに設定が追加されています。

Some small efficiency gains plus controlmaster for rsync by matschaffer · Pull Request #440 · matschaffer/knife-solo · GitHub

ControlMasterとは

このような説明がありました。

単一のネットワーク接続で、複数のセッションを共有します。これが"yes"に設定されている場合、ssh (1) はControlPathの引数で指定された制御用のソケットを listen します。追加のセッションを開くには、同じソケットをControlPathに指定し、ControlMasterには"no"を指定します (これがデフォルト)。こうすると、そのセッションは新たにネットワーク接続するのではなく、すでにある Master インターフェイスのネットワーク接続の再利用を試みるようになります。ただし制御用ソケットが存在しないか、listen されていない場合は通常の接続になります。

SSH_CONFIG (5)

初めて接続したセッションを保持して、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の仕組みについてはもう少し調査したいです。