localhostでsshをポートフォワードしたサーバとの synchronize が失敗する [解決]

# 概要
## やりたいこと

- ローカルの ~/ansible/work/ にチェックアウトした myapp のソースコードを、サーバの /var/ma2saka/myapp に転送したい
- サーバは vagrant でローカルに立ててる Cent OS 6.5
- もともとスクリプトは AWS サーバ用に書いていて動作しているもの

## 先に解決策

ポート番号が違ってもホスト名部分が一緒だと誤動作する。

インベントリファイルで直接IP書かずに、`host1 ansible_ssh_host=127.0.0.1` みたいに指定してホスト名称部分がかぶらないようにするとよい。

# 失敗してたパターン
 
## 書いた定義

```main.yml
- name: sync source code
  synchronize:> 
    dest=/var/ma2saka/myapp
    src=/Users/ma2saka/ansible/work/myapp/ 
    recursive=yes
    links=yes
    rsync_opts="--exclude='.git'"

```

## インベントリには

```inventory.ini
[dev]
127.0.0.1

[dev:vars]
ansible_ssh_user=vagrant
ansible_ssh_port=2222
```

vagrant で立てたサーバがローカルの 2222 で待ち受けてる。

以下のようにしてあらかじめ公開鍵は登録している。

```bash
ssh-add -D
ssh-add ~/.ssh/id_rsa
ssh-copy-id -p 2222 vagrant@127.0.0.1
```

## エラーになる

```
TASK: [deploy | sync source code] *********************************************
failed: [127.0.0.1 -> 127.0.0.1] => {"cmd": "rsync --delay-updates -FF --compress --archive --rsh 'ssh  -S none -o StrictHostKeyChecking=no -o Port=2222' --exclude='.git' --out-format='<<CHANGED>>%i %n%L' \"/Users/ma2saka/ansible/work/myapp/\" \"/var/ma2saka/myapp\"", "failed": true, "rc": 23}
msg: rsync: change_dir "/Users/ma2saka/ansible/work/myapp/" failed: No such file or directory (2)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1039) [sender=3.0.6]


FATAL: all hosts have already failed -- aborting
```

いや、`msg: rsync: change_dir "/Users/ma2saka/ansible/work/myapp/" failed: No such file or directory (2)`とか言われてもさ。あるからねそのディレクトリ。どう見てもさ。

## brew で入れてる 1.8.2 だからかと疑って pip で入れなおしてみるもアカン

1.9.1 でも特に変化なし。

## amazonとか別のサーバだとうまくいく

```inventory.ini
[dev]
myapp1.amazon.example.com
myapp2.amazon.example.com
myapp3.amazon.example.com

[dev:vars]
ansible_ssh_user=ec2-user
ansible_ssh_port=22
```

問題なく期待通り動作する。
 
## 127.0.0.1 に /etc/hosts で別名つけたら動作した

```/etc/hosts
127.0.0.1	localhost
255.255.255.255	broadcasthost
::1             localhost
127.0.0.1 this.is.it
```

```inventory.ini
[dev]
this.is.it

[dev:vars]
ansible_ssh_user=vagrant
ansible_ssh_port=2222
```

これで実行すると。

```
 .. 略 ..

TASK: [deploy | sync source code] *********************************************
changed: [this.is.it -> 127.0.0.1]

 .. 略 ..
```


動くし・・・。
期待通り動くのでこれはこれでいいんだけど釈然としないものが残るな。


## ホスト名を localhost にするとそもそも繋がらない

IP指定してやるのがダメなら localhost だとどうよと思ったら、

```
TASK: [deploy | sync source code] *********************************************
failed: [localhost -> 127.0.0.1] => {"cmd": "rsync --delay-updates -FF --compress --archive --rsh 'ssh  -S none -o StrictHostKeyChecking=no -o Port=2222' --exclude='.git' --out-format='<<CHANGED>>%i %n%L' \"/Users/ma2saka/ansible/work/myapp/\" \"/var/ma2saka/myapp\"", "failed": true, "rc": 12}
msg: ssh: connect to host localhost port 2222: Connection refused
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(600) [sender=3.0.6]
```

えーーー。

つまり実際のところ、ホストの「名前」が問題なわけ? と遅ればせながら気がつく。

# ansible_ssh_host の設定で解決

ひっかかっていたのはまさにこの現象でした。

- [Ansible multiple hosts with port forwarding - Stack Over Flow](http://stackoverflow.com/questions/26527458/ansible-multiple-hosts-with-port-forwarding)

ローカルホストでポートフォワードしているホストに ansible で接続する場合、インベントリファイルで適当な別名を付与するのがよいやり方のようです。

```
[dev]
# lvh.me とか this.is.it とか書かなくていい
host1 ansible_ssh_host=127.0.0.1

[dev:vars]
ansible_ssh_user=vagrant
ansible_ssh_port=2222
```

だいたい synchronize モジュールのコードとか実行時に転送されてるスクリプトも確認したので挙動は理解したけど、これは罠だなー。だいたいエラーメッセージが不親切すぎる。という愚痴をいいつつ。解決したので気分よし。

## 反省

`-vvvv` してよく見ると、「そのスクリプトがどこで実行されてるか」がわかるんですね。127.0.0.1 を書いて失敗してたとき、実際にスクリプトはリモートサーバで実行されていたんです。それなのに source の指定はローカルマシンの相対パスが書かれているからディレクトリが見つからない。

というところに気がつけば、これ特定モジュールのバグじゃなくて ansible のホスト名解決ロジックの側だろうとすぐ察せられたはずなのですが、見当違いのところをトレースしていて時間を使ってしまいました。なかなか難しいものです。