ansible の公式ドキュメント(英語)を読んで見落としがちなところをメモ


[公式ドキュメント](http://docs.ansible.com/ansible/) を読んで重要な点をメモしておく。

version 1.9 を前提にメモ。

用語の整理
==========

- `facts` : 更新先となるリモートサーバの各種情報。冒頭で取ってくる。
- `inventory` : `hosts` ファイルで定義されるもののこと。
- `handler` : サーバでの変更に応じて他サーバで何かをするためのイベントの仕組み。
- `playbook` : `ansible-playbook` コマンドに渡すメインの yml のこと。設計する上では、構築手順を書くのでなく、何者なのかを書くこと。
- `冪等性(べきとうせい/idempotent)` : 何度動かしても大丈夫なこと。


[hosts](http://docs.ansible.com/ansible/intro_inventory.html#hosts-and-groups)
=======

- host をグループ化できる。グループごとにキーバリューを設定できる:

```yaml:グループごとの値設定
[atlanta]
host1
host2

[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com
```

- グループはネストできる:

```yaml:グループのネスト
[southeast:children]
group1
group2
```



[tasks](http://docs.ansible.com/ansible/playbooks_intro.html#tasks-list)
======

- 書かれている順に実行される。
- １つ `task` を対象サーバすべてに適用してから、次の `task` に移る。


[handlers](http://docs.ansible.com/ansible/playbooks_intro.html#handlers-running-operations-on-change)
=========

```yaml:playbook
tasks:
  - name: template configuration file
    template: src=template.j2 dest=/etc/foo.conf
    notify:
       - restart memcached
       - restart apache
handlers:
    - name: restart memcached
      service: name=memcached state=restarted
    - name: restart apache
      service: name=apache state=restarted
```

と書いておくと、変更があった場合のみ、`handler` の実行できる。

##### 注:

- `notify` の中は書かれている順に実行される。
- `handlers` の中の名前は global スコープ。(同名があれば片方のみ実行される。)
- `include` の中にある `handler` は `notify` できない。
- `pre_tasks`, `tasks`, `post_tasks` の中で `notify` された `handler` は、各セクションの終了時にフラッシュ(≒実際に実行)され、`roles` の中で `notify` された `handler` は、`tasks` セクションの全の `handler` の後にフラッシュされる。
- `meta: flush_handlers` でただちにフラッシュすることも可能:

```yaml:playbook
  tasks:
     - shell: some tasks go here
     - meta: flush_handlers
     - shell: some other tasks
```

[include](http://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse)
=============

- トップ階層に `tasks` ディレクトリを作成し、その中に再利用可能な task の yml を保存しておくことで、playbook 中で再利用できる。
- パラメータを渡すことも可能:

```yaml:パラメータを渡す例
- include: wordpress.yml wp_user=timmy
もしくは
- include: wordpress.yml
  vars:
    wp_user: timmy
```

- 別の playbook を include することも可能。その場合、パラメータは渡せない？

[role](http://docs.ansible.com/ansible/playbooks_roles.html#roles)
===========

#### 次のように振る舞う:

- roles/x/**tasks**/main.yml (あれば)
- roles/x/**handlers**/main.yml (あれば)
- roles/x/**vars**/main.yml (あれば)
- roles/x/**meta**/main.yml (あれば) 内の role dependencies が roles に加わる。
- role 内の `copy`, `script`, `template`, `include` タスクは `roles/x/{files,templates,tasks}/` (ディレクトリはタスクによる) の下を見る。

#### パラメータを渡す role:

```yaml
  roles:
    - common
    - { role: foo_app_instance, dir: '/opt/a',  app_port: 5000 }
    - { role: foo_app_instance, dir: '/opt/b',  app_port: 5001 }
```

#### 条件付きの role:

```yaml
  roles:
    - { role: some_role, when: "ansible_os_family == 'RedHat'" }
```

#### タグ付きの role (role内の全タグを上書くので注意):

```yaml
  roles:
    - { role: foo, tags: ["bar", "baz"] }
```

#### role の前後に task を走らせないなら:

```yaml
  pre_tasks:
    - shell: echo 'hello'
  roles:
    - { role: some_role }
  tasks:
    - shell: echo 'still busy'
  post_tasks:
    - shell: echo 'goodbye'
```

#### 別の role に依存するなら (roles/x/**meta**/main.yml の中で) :

```yaml
dependencies:
  - { role: common, some_parameter: 3 }
  - { role: apache, appache_port: 80 }
  - { role: postgres, dbname: blarg, other_parameter: 12 }
```

- 同じ role は（パラメータが異なっていたとしても）デフォルトでは複数回適用されない。複数適用したいなら呼ばれる側で `allow_duplicates: yes` (複数回の適用を許す) が必要:

```yaml:roles/car/meta/main.yml (呼び出す側)
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
```

```yaml:roles/wheel/meta/main.yml (呼ばれる側)
allow_duplicates: yes
dependencies:
- { role: tire }
```

[変数](http://docs.ansible.com/ansible/playbooks_variables.html#variables)
========

#### 命名ルール

- `foo_port` のようなフォーマットであること。
- 値は yaml で map を書くこともできる。

#### 値の定義場所

inventory:

```yaml:hosts
[atlanta]
host1 http_port=80 maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909
```

playbook:

```yaml:playbook
- hosts: webservers
  vars:
    http_port: 80
```

include に渡す:

```yaml:playbook
tasks:
  - include: foo.yml
    vars:
        aa: timmy
        bb:
          key1: keys/one.txt
          key2: keys/two.txt
```

値を外出し:

```yaml:playbook
- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml
```

```yaml:/vars/external_vars.yml
somevar: somevalue
password: magic
```

コマンドライン:

```bash
ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"
もしくは
ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'
もしくは
ansible-playbook release.yml --extra-vars "@some_file.json"
もしくは
ansible-playbook release.yml --extra-vars "@some_file.yml"
```

#### 優先順位(下にいくほど強い)

1. role defaults
2. inventory の定義
3. facts
4. その他(コマンドラインのスイッチ、playのvars、includeされたvars、roleのvarsなど)
5. コネクション変数(`ansible_user` など)
6. extra vars(コマンドラインの `-e`)

※2.x で変わるらしい。

#### 値を使う

- `{{ aa }}` や `{{ bb.key1 }}` のように使う。
- `foo: {{ bar }}/22` は NG。`foo: "{{ bar }}/22"` とすべき。これは yaml の文法上、`{` がディクショナリの開始と混同されるため。

#### フィルタ

[参照1](http://docs.ansible.com/ansible/playbooks_filters.html)
[参照2](http://jinja.pocoo.org/docs/dev/templates/#builtin-filters)

- `{{ val|default('aaa') }}` - `val` が未定義の場合に `aaa` を採用。
- `{{ val|default('aaa', true) }}` - `val` が `false` として評価される値の場合(`''` 等)に `aaa` を採用。
- `{{ val|escape }}` - `<>&"'` を HTML 用にエスケープ。
- `{{ "%s - %s"|format("Hello?", "Foo!") }}` - フォーマット。
- `{{ val|trim }}` - トリム。
- `- shell: echo {{ string_value | quote }}`
- `{{ (name == "John") | ternary('Mr','Ms') }}` - true なら左。
- `{{ list | join(" ") }}`
- `{{ path | basename }}` - ファイル名
- `{{ path | dirname }}` - ディレクトリ名
- `{{ path | realpath }}`
- `{{ path | relpath('/etc') }}`
- `when: some_string_value | bool` -  bool にキャスト
- `when: url | match("http://example.com/users/.*/resources/.*")` - 正規表現でえ完全一致
- `when: url | search("/users/.*/resources/.*")` - 正規表現で中間一致
- `{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}` - 正規表現で置き換え

#### [register](http://docs.ansible.com/ansible/playbooks_variables.html#registered-variables)

```yaml
  tasks:
    - shell: /usr/bin/foo
      register: foo_result
      ignore_errors: True

    - shell: /usr/bin/bar
      when: foo_result.rc == 5
```

```yaml:結果をフィルタして条件に使う
  - shell: /usr/bin/foo
    register: result
    ignore_errors: True

  - debug: msg="it failed"
    when: result|failed

  # in most cases you'll want a handler, but if you want to do something right now, this is nice
  - debug: msg="it changed"
    when: result|changed

  - debug: msg="it succeeded in Ansible >= 2.1"
    when: result|succeeded

  - debug: msg="it succeeded"
    when: result|success

  - debug: msg="it was skipped"
    when: result|skipped
```

- 結果が失敗でも skip されても `foo_result` は失敗/skipの結果が登録される。登録を避けたいならタグを使えばいい。

#### [予約語](http://docs.ansible.com/ansible/playbooks_variables.html#magic-variables-and-how-to-access-information-about-other-hosts)

- `hostvars` - 別host用の値が入っている。この時点で全くやりとりの無いサーバでも変数値は取れるが、その場合 facts は取れない。使用例: `{{ hostvars['test.example.com']['ansible_distribution'] }}`
- `group_names` - 現在の `host` が属するグループのリスト(配列)。
- `groups` - inventory に存在するすべてのグループとhost。

#### [スコープ](http://docs.ansible.com/ansible/playbooks_variables.html#variable-scopes)

- global - コンフィグ、環境変数、コマンドラインによるもの。
- play - 各playと、含まれる構造体, varsのentries, include_vars, role defaults , vars
- host - hostに直接関連する値（inventory、facts、taskの出力をregisterしたものなど）