AnsibleでのEC2接続時エラーへの対処法

2014.09.16

こんにちは。望月です。
今まで何本かChefに関するブログを書いてきましたが、最近は同じプロビジョニングツールのAnsibleに浮気しております。Ansibleはプロビジョニング対象サーバにツールを追加インストールする必要がなく、記法もシンプルなため習得コストは非常に低いと感じています。
過去にAnsibleの記事も本ブログでいくつか書いています。

さて、ローカルのVagrant等で検証していたのですが、いざEC2にプロビジョニングしようと思ったところエラーが出たので、解決方法を残しておきます。

謎のSSH Connection Error

ansible-playbookコマンド手元のMacからをEC2インスタンスに対して実行したところ、以下のエラーが出ました。

$ ansible-playbook playbook.yml

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
fatal: [ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue

TASK: [create user] ******************************************
FATAL: no hosts matched or all hosts have already failed -- aborting


PLAY RECAP ********************************************************************
           to retry, use: --limit @/Users/mochizukimasao/playbook.retry

ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com : ok=0    changed=0    unreachable=1    failed=0

SSH接続でエラーになっているようです。エラーメッセージに、デバッグのために-vvvvオプションをつけて実行してみて、と書かれているので言われたとおりにしてみます。

$ ansible-playbook playbook.yml -vvvv

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
<ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com> ESTABLISH CONNECTION FOR USER: ec2-user
<ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com> REMOTE_MODULE setup
<ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com> EXEC ['ssh', '-C', '-tt', '-vvv', '-o', 'ControlMaster=auto', '-o', 'ControlPersist=60s', '-o', 'ControlPath=/Users/mochizukimasao/.ansible/cp/ansible-ssh-%h-%p-%r', '-o', 'KbdInteractiveAuthentication=no', '-o', 'PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey', '-o', 'PasswordAuthentication=no', '-o', 'User=ec2-user', '-o', 'ConnectTimeout=10', 'ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com', "/bin/sh -c 'mkdir -p $HOME/.ansible/tmp/ansible-tmp-1410850108.62-240266539733533 && chmod a+rx $HOME/.ansible/tmp/ansible-tmp-1410850108.62-240266539733533 && echo $HOME/.ansible/tmp/ansible-tmp-1410850108.62-240266539733533'"]
fatal: [ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com] => SSH encountered an unknown error. The output was:
OpenSSH_6.2p2, OSSLShim 0.9.8r 8 Dec 2011
debug1: Reading configuration data /Users/mochizukimasao/.ssh/config
debug1: /Users/mochizukimasao/.ssh/config line 1: Applying options for ec2-*
debug1: Reading configuration data /etc/ssh_config
debug1: /etc/ssh_config line 20: Applying options for *
debug1: /etc/ssh_config line 102: Applying options for *
debug1: auto-mux: Trying existing master
ControlPath too long


TASK: [create user] ******************************************
FATAL: no hosts matched or all hosts have already failed -- aborting


PLAY RECAP ********************************************************************
           to retry, use: --limit @/Users/mochizukimasao/playbook.retry

ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com : ok=0    changed=0    unreachable=1    failed=0

ControlPath too longというエラーが出ました。ControlPathというのはSSHのオプションで、ControlMasterという設定と共に利用されます。
単一のTCPコネクションで複数のSSHセッションを貼る機能で、ControlPathにはコネクション共有のためのソケットファイルへのパスを記載する必要があります。

AnsibleからSSHで接続しにいく際にはAnsibleがControlMaster,ControlPathの機能を裏側で使うため、Ansibleデフォルトの設定値が利用されるのですが、AnsibleデフォルトのControlPathの設定値デフォルトが%(directory)s/ansible-ssh-%%h-%%p-%%rとなっており、これがソケットファイルのファイル名の最大長制限に引っかかってしまうようです。中で使われているホスト名変数(%%hの部分)が、EC2ではデフォルトのhostnameが非常に長いため、文字数制限に引っかかりやすいようです。

解決策

Ansibleの設定ファイルであるansible.cfgに、以下のように書いてデフォルト値を上書きしてしまいましょう。

[ssh_connection]
control_path = %(directory)s/%%h-%%r

ちなみに、Ansibleの設定は以下のファイルに書くことができます。上から順番に評価されます。

  • 環境変数ANSIBLE_CONFIGに記載されたファイル
  • カレントディレクトリのansible.cfg
  • $HOME/.ansible.cfg
  • /etc/ansible/ansible.cfg

この設定はよく使うと思われるので、私は$HOME/.ansible.cfgに記載しました。

これでもう一度ansible-playbookコマンドを実行してみます。

$ ansible-playbook playbook.yml

PLAY [all] ********************************************************************

<snipped...>

ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com : ok=8    changed=0    unreachable=0    failed=0

無事、SSH接続に成功し、playbookの適用が完了したようです。 Ansible、非常に気軽に始められますので、是非使ってみてください。

参考資料