読者です 読者をやめる 読者になる 読者になる

今川館

都内勤務の地味OLです

Ansibleのハンドラが起動するのはタスクがchangedを返したときだけ

Ansibleのハンドラとは

Ansibleのタスクには `notify` という属性を追加してコールバック処理を呼ぶよう設定できる。

[roles/nginx/tasks/main.yml]

- name: nginx config ensure
  template: src=my.nginx.conf.j2 dest=/etc/nginx/conf.d/your.conf
  sudo: yes
  notify: restart nginx

[roles/nginx/handlers/main.yml]

- name: restart nginx
  service: name=nginx state=restarted
  sudo: yes

こう書いておくと roles/nginx/templates/my.nginx.conf.j2 から生成した内容が /etc/nginx/conf.d/your.conf の内容を変更したときに、自律的にnginxを再起動してくれる。

この挙動を一般に「nginxの設定を変えたら自動的に再起動される」などと呼ぶ。

ところがわざわざ下線を引いたけれども、Ansibleのプレイブックを実行したタイミングで `nginx config ensure` のタスクが `CHANGED` を返さないとハンドラは起動しない。

だから

  1. プレイブックを起動。(A -> B -> C) の順にタスクを実行。
  2. nginxの設定ファイル配置をBのタイミングで行ったが、Cに指定した別のタスクが失敗した。
  3. ハンドラはすべてのタスクが終わった後に動くのでCが失敗した時点で処理が終わってしまう。
  4. もう一度プレイブックを動かすとBのタスクではもはやCHANGEDとは評価されないのでnginxが再起動されないまま放置される。

このような事態が起きうる。

役に立つのかわからない --force-handlers オプション

そういう事態を想定して、 `ansible-playbook` コマンドには `--force-handlers` というオプションが用意されている。

これを指定してプレイブックを動かすと、たとえタスクが失敗してもハンドラの起動まで進めてくれる。

とはいえ、指定し忘れると結局アウトなのだ。

よくわからないのだが、Ansibleには「ハンドラのタスクを指定して動かす」仕組みが無いので、nginxの設定ファイル配置以降のタスクが予期せず失敗した場合にやり直す方法が無い。

どうしたらいいのか?

仕方が無いのでこんな方法で対処してみた。

Role -- register/changed を利用

[roles/nginx/tasks/main.yml]

- name: 1. nginxの設定ファイルを配置
  template: src=my.nginx.conf.j2 dest=/etc/nginx/conf.d/your.conf
  sudo: yes
  register: nginx_conf
  notify: restart nginx

... (他の雑多なタスク) ...

- name: 2. nginxが起動していること
  service: name=nginx state=started
  sudo: yes

- name: 3. nginxを強制的に再起動
  service: name=nginx state=restarted
  sudo: yes
  when: nginx_restart is defined and not nginx_conf.changed
  tags:
    - deploy
実行コマンド

[通常時]

$ ansible-playbook -i foo.ini some.yml

[やり直しするとき]

$ ansible-playbook -i foo.ini some.yml -t deploy -e 'nginx_restart=yes'

こうしておけば

  • 1のタスクでnginxの設定が変わったらハンドラから再起動される
  • 3のタスクはコマンドラインからnginx_restartを指定したときだけ動く
  • 1から動くハンドラと3のタスクによってnginx再起動が2度実行されることもない
ひとり言

なんかややこしいことをしている気がするのだが・・・
もっと簡潔な方法は無いのか。