koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

Ansible の変数のスコープ

いつの間にか Ansible の変数にスコープの概念が入っていてプロビジョンに失敗するようになっていたという話。

正直、自分はあんまりテクニカルな方面のアンテナを張っていないので、ソフトウェアのアップデートで仕様が変わっていたりすると即死してしまう。

今回は、よくわかっていないまま Ansible の Playbook をいろんなところからのコピペで作っていたら、いつの間にか動かないものになっていた。

Variables — Ansible Documentation

Ansible has 3 main scopes:

  • Global: this is set by config, environment variables and the command line
  • Play: each play and contained structures, vars entries, include_vars, role defaults and vars.
  • Host: variables directly associated to a host, like inventory, facts or registered task outputs

どうやら、Ansibleには変数のスコープが3種類あるようで、このうち Play (要は一回の実行単位)スコープで保持してほしい変数を Host スコープに入れていたので、スコープから外れてしまったようだ。

失敗するようになっていた定義

どこからパクったのかすら忘れてしまったが site.yml に以下のように書いてやって、インベントリファイルで stage 変数を設定することで、環境毎の差分を吸収するようにしていた。

site.yml(```yaml:site.yml って書けないの?)

- hosts: all
  vars_files:
    - vars/{{ stage }}.yml
  tasks:
    - name: dump on site.yml
      debug: var=_stage
- include: allservers.yml
- include: webservers.yml
- include: appservers.yml
- include: memservers.yml
- include: dbservers.yml

が、こうすると include しているファイルの中にも hosts があるので site.ymlhosts で設定した変数はスコープから外れてしまうようだった。

インターネットをさまよった結果、どうやら変数にスコープの概念が入った結果未定義になってしまったような気がしただけなので、あまり確信はない。

# site.yml
- hosts: all
  vars_files:
    - vars/{{ stage }}.yml
  tasks:
    - name: dump on site.yml
      debug: var=_stage
- include: allservers.yml

# allservers.yml
- hosts: allservers
  gather_facts: no
  become: yes
  become_method: sudo
  roles:
    - {role: common,  tags: [always]}

# common ロールで実行される task
- name: dump variables.
  debug: var=_stage

# vars/{{ stage }}.yml
_stage:
  mariadb:
    root_password:
    db:
# ~~長々と設定値定義

上記のような定義で実行すると、以下のように未定義になってしまっていた。

TASK [dump on site.yml] ********************************************************
ok: [192.168.33.74] => {
    "_stage": {
        "mariadb": {
            "db": [
# ~~長々と変数内容

TASK [common : dump variables.] ************************************************
ok: [192.168.33.74] => {
    "_stage": "VARIABLE IS NOT DEFINED!"
}

Play スコープで任意の変数ファイルを読む

で、本来これを記事の先頭に書けよ、という気がしなくもないが、記事を書きだしたタイミングではどうすればいいのかわからなかったのでケツにある。

  • Play: each play and contained structures, vars entries, include_vars, role defaults and vars.

とあるので include_vars タスクで読んだら Play スコープになるようなので、単に以下のように変えてみた。

# site.yml
- hosts: all
  tasks:
    - name: includes stage variable file.
      include_vars: vars/{{ stage }}.yml
    - name: dump on site.yml
      debug: var=_stage
- include: allservers.yml

# allservers.yml
- hosts: allservers
  gather_facts: no
  become: yes
  become_method: sudo
  roles:
    - {role: common,  tags: [always]}

# common ロールで実行される task
- name: dump variables.
  debug: var=_stage

# vars/{{ stage }}.yml
_stage:
  mariadb:
    root_password:
    db:
# ~~長々と設定値定義

結果、各ホストでも変数が見えるようになったので、さしあたってはこれでいいかなぁ。と、深堀をやめたのであった。

TASK [includes stage variable file.] *******************************************
ok: [192.168.33.74]

TASK [dump on site.yml] ********************************************************
ok: [192.168.33.74] => {
    "_stage": {
        "mariadb": {
            "db": [
# ~~長々

TASK [common : dump variables.] ************************************************
ok: [192.168.33.74] => {
    "_stage": {
        "mariadb": {
            "db": [
                {
# ~~長々

Ansible のバージョン

こんな感じでした。

$ yum list ansible
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.tsukuba.wide.ad.jp
 * extras: ftp.tsukuba.wide.ad.jp
 * remi-safe: remi.kazukioishi.net
 * updates: ftp.tsukuba.wide.ad.jp
Installed Packages
ansible.noarch                                                   2.0.1.0-2.el7                                                   @epel
$