ansibleのplaybookをvagrantで確認しようとしてひっかかった話


発端
===

- awsでサーバ構築に使用する用途にansibleを作成中、いちいちインスタンス建てたり落としたりがめんどくさくなった

- vagrant localに建ててそこに流そう

ansible 2.6.2 

構成
---

詳細は公式ドキュメント

https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html

```
.
├── Vagrantfile
├── ansible.cfg
├── inventory
│   └── dev
│       ├── group_vars
│       │   ├── any-group1
│       │   └── any-group2
│       └── hosts.yml
├── playbook
│       ├── any-group1.yml
│       └── any-group2.yml
├── roles
│   └── any-role
└── ssh_config
```

any-group1がapplicationサーバ、any-group2がミドル用のサーバといった感じ

```
$ cat ansible.cfg
[defaults]
hash_behaviour = merge

[ssh_connection]
ssh_args   = -F ssh_config
scp_if_ssh = True
```

`ssh_config` には実際に流すリモートのホスト情報が入っている

```
$ cat inventory/dev/group_vars/any-group1
owner: "me"
group: "me"
public: "any-group1"
$ cat inventory/dev/group_vars/any-group2
middle: "any-group2"
```

この変数の差分が今回の核心

vagrantに流す用のinventoryを用意した
===

構成
---

```
.
├── Vagrantfile
├── ansible.cfg
├── inventory
│   ├── check
│   │   ├── group_vars -> ../dev/group_vars
│   │   └── hosts.yml
│   └── dev
│       ├── group_vars
│       │   ├── any-group1
│       │   └── any-group2
│       └── hosts.yml
├── playbook
│       ├── any-group1.yml
│       └── any-group2.yml
├── roles
│   └── any-role
├── ssh_config
└── ssh_config.vagrant
```

`vagrant ssh-config` をそのまま入れただけの `ssh_config.vagrant`

```
$ cat ssh_config.vagrant
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile <鍵>
  IdentitiesOnly yes
  LogLevel FATAL
```

```
$ cat inventory/check/hosts.yml 
---
any-group1: &check
  hosts:
    default:
any-group2: *check
```

同一ホストを別グループに指定している(今回の核心2)


結果(失敗した、片方だけ)
===

```
ansible-playbook -i inventory/check playbook/any-group1.yml --ssh-common-args='-F ssh_config.vagrant'
```

```
ansible-playbook -i inventory/check playbook/any-group2.yml --ssh-common-args='-F ssh_config.vagrant'
```

any-group1のほうは成功、よしよしと思いながらany-group2を実行すると失敗した

エラーログは取り忘れたけれど内容はディレクトリのグループに存在しないグループをしているぞと言ってた

該当部分は以下の部分で、どちらのグループでも実行されるタスクだった。

```
  - name: Setting directory permission
    file:
      path: /etc/middle
      owner: middle
      group: "{{ group | default('root') }}"
      mode: 02575
      state: directory
```

なにが起きた?
---

いろいろと調査してると `ansible-inventory` というのを見つけた

インベントリの設定内容を出してくれるということだったので早速ためす

```
$ ansible-inventory -i inventory/dev --list
{
    "_meta": {
        "hostvars": {
            "any-dev-group1": {
                "owner": "me",
                "group": "me",
                "public": "any-group1"
            },
            "any-dev-group2": {
                "middle": "any-group2"
            }
        }
    },
    "all": {
        "children": [
            "any-group1",
            "any-group2",
            "ungrouped"
        ]
    },
    "any-group1": {
        "hosts": [
            "any-dev-group1"
        ]
    },
    "any-group2": {
        "hosts": [
            "any-dev-group1"
        ]
    },
    "ungrouped": {}
}
```

```
$ ansible-inventory -i inventory/check --list
{
    "_meta": {
        "hostvars": {
            "default": {
                "owner": "me",
                "group": "me",
                "middle": "any-group2",
                "public": "any-group1"
            }
        }
    },
    "all": {
        "children": [
            "any-group1",
            "any-group2",
            "ungrouped"
        ]
    },
    "any-group1": {
        "hosts": [
            "default"
        ]
    },
    "any-group2": {
        "hosts": [
            "default"
        ]
    },
    "ungrouped": {}
}
```

注目すべきは

```
$ ansible-inventory -i inventory/dev --list | jq ._meta.hostvars
"any-dev-group1": {
    "owner": "me",
    "group": "me",
    "public": "any-group1"
},
"any-dev-group2": {
    "middle": "any-group2"
}

$ ansible-inventory -i inventory/check --list | jq ._meta.hostvars
"default": {
    "owner": "me",
    "group": "me",
    "middle": "any-group2",
    "public": "any-group1"
}
```

groupが別でも同一ホストだとvarsがマージされるよう

group_varsとして持つんじゃなくて、group_varsをもとにそれぞれのhostごとのhost_varsを持つ感じなのか

とにかくそのために、any-group2でも変数 `group` が定義されてしまい、前記のタスク部分でデフォルトが効かず、存在しないグループを指定してしまっていた。

解決
===

```
$ cat ssh_config.vagrant
Host any-check-group1
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile <鍵>
  IdentitiesOnly yes
  LogLevel FATAL
Host any-check-group2
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile <鍵>
  IdentitiesOnly yes
  LogLevel FATAL
```

```
$ cat inventory/check/hosts.yml 
---
any-group1:
  hosts:
    any-check-group1:
any-group2:
  hosts:
    any-check-group2:
```

とりあえずホストの設定を別々にして対応した

```
$ ansible-inventory -i inventory/check --list | jq ._meta.hostvars
"any-check-group1": {
    "owner": "me",
    "group": "me",
    "public": "any-group1"
},
"any-check-group2": {
    "middle": "any-group2"
}
```

期待通りhost_varsが別れて、ansible-playbookも成功した