Edited at
AnsibleDay 14

使えばきっと一目置かれるAnsibleのちょっと高度な機能

こちらは Ansible Advent Calendar 2018 の14日目の記事です。


はじめに

普段から構成管理にはAnsibleを使用していて、他にも作業の自動化でもガッツリ使っていますが、その際に使用して重宝したちょっと高度な機能と簡単な例を紹介したいと思います。


概要

以下の機能を紹介します。そんなの知ってるぜというかたはすっ飛ばしてください。

1. debuggerでデバッグ

2. block,rescueでエラーハンドリング

3. untilでリトライ機能の実装

4. run_once,delegate_toでローカル実行


debuggerでタスクのデバッグ

debuggerで任意のタスクをデバッグすることができます。(Ansible2.5以降)

---

- name: debug test
hosts: localhost
connection: local
debugger: on_failed
tasks:
- name: task1
vars:
var1: value1
shell: |
true

- name: task2
vars:
var2: value2
shell: |
false

実際に実行した結果がこちら。

PLAY [debug test] **********************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************************
Sunday 09 December 2018 20:18:57 +0900 (0:00:00.148) 0:00:00.148 *******
ok: [localhost]

TASK [task1] ***************************************************************************************************************************************************************************
Sunday 09 December 2018 20:18:58 +0900 (0:00:01.064) 0:00:01.213 *******
changed: [localhost]

TASK [task2] ***************************************************************************************************************************************************************************
Sunday 09 December 2018 20:18:59 +0900 (0:00:00.435) 0:00:01.649 *******
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "false", "delta": "0:00:00.010900", "end": "2018-12-09 20:18:59.635267", "msg": "non-zero return code", "rc": 1, "start": "2018-12-09 20:18:59.624367", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
[localhost] TASK: task2 (debug)> p task
TASK: task2
[localhost] TASK: task2 (debug)> p task.args
{'_ansible_check_mode': False,
'_ansible_debug': False,
'_ansible_diff': False,
'_ansible_keep_remote_files': False,
'_ansible_module_name': 'command',
'_ansible_no_log': False,
'_ansible_remote_tmp': u'~/.ansible/tmp',
'_ansible_selinux_special_fs': ['fuse', 'nfs', 'vboxsf', 'ramfs', '9p'],
'_ansible_shell_executable': u'/bin/sh',
'_ansible_socket': None,
'_ansible_syslog_facility': u'LOG_USER',
'_ansible_tmpdir': None,
'_ansible_verbosity': 0,
'_ansible_version': '2.7.4',
u'_raw_params': u'false',
'_uses_shell': True,
'warn': True}
[localhost] TASK: task2 (debug)> p task_vars['var2']
u'value2'
[localhost] TASK: task2 (debug)> c

PLAY RECAP *****************************************************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=1

Sunday 09 December 2018 20:19:41 +0900 (0:00:42.096) 0:00:43.745 *******
===============================================================================
shell ------------------------------------------------------------------ 42.53s
setup ------------------------------------------------------------------- 1.06s
total ------------------------------------------------------------------ 43.60s

task2でfailedになったところでdebuggerが起動しています。

またdebuggerには以下の機能があります。



  • p taskでデバッグ対象のタスクを出力


  • p task_argsで引数を出力


  • p task_vars[]で変数の値を出力


  • rでtaskを再実行


  • cで後続タスクを続行


  • qで終了


block,rescueディレクティブでエラーハンドリング

通常作業用のtaskをblockで定義し、その中で失敗した場合にのみ実行したいtaskをrescueで定義します。

この場合、migrate task1,migrate task2のいずれかが失敗した場合にのみfailback taskが実行されます。

すべて成功した場合はfailback taskは実行されません。

alwaysディレクティブを使えば結果にかかわらず実行させるタスクを定義できます。

またblock単位でbecomewhenができるのでコード全体の見通しも良くなります。

---

- name: example block,rescue
block:
- name: migrate task1
shell: |
echo "exec migrate task1"

- name: migrate task2
shell: |
echo "exec migrate task2"
become: yes
when: ansible_distribution == "centos"

rescue:
- name: failback task
shell: |
echo "exec failback task"

always:
- name: always task
shell: |
echo "exec always task"


promptモジュールで入力待ち

作業は自動化したい。でも作業途中の確認は目視で確認したい、実行のタイミングを計りたい場合ってありますよね。問題なければyesを入力、みたいな。

promptモジュールを使えばプロンプトを出して標準入力を受け取れます。

---

- name: wait for prompt
pause: prompt="please input y"
register: yn
- name: exit, if not y
fail: msg="Aborted!!"
when: yn.user_input != 'y'
run_once: true


untilでリトライ機能の実装

untilで条件をつければ条件がTrueになるまでリトライさせることが出来ます。retriesはリトライする回数、delayはリトライのインターバルです。

またregisterで取得した実行結果をそのまま条件に使えます。

この場合はshow slave statusの結果でレプリケーションの遅延やエラーがなくなるまで1秒おき3回までリトライします。

---

- name: show slave status
mysql_replication:
mode: getslave
login_host: 127.0.0.1
login_port: 3306
login_user: root
register: result_getslave_before
until:
- result_getslave_before.Slave_IO_Running == "Yes"
- result_getslave_before.Slave_SQL_Running == "Yes"
- result_getslave_before.Last_IO_Errno == 0
- result_getslave_before.Last_SQL_Errno == 0
- result_getslave_before.Slave_IO_State == "Waiting for master to send event"
- '"Slave has read all relay log; waiting for" in result_getslave_before.Slave_SQL_Running_State'
retries: 3
delay: 1


run_once,delegate_toでローカル実行

hostsで複数のサーバやグループを指定しているけどローカルで1回だけ実行したいタスクがある場合に有効です。

この場合は複数のアプリケーションサーバでDBへのログインチェックを実行しますが、その前のvaultからのパスワード取得はローカルで1回のみ実行しています。

注意点としてbecomeも継承されるので必要に応じて変更する必要があります。

---

- name: db login check
hosts: "{{ env }}-app-servers"
become: yes
vars_files: ["./{{ env }}-vars.yml"]
tasks:
- name: get password form hashi_vault
debug:
msg: "{{ lookup('hashi_vault', 'secret=secret/{{vault_secret}}: url=http://127.0.0.1:8200')}}"
register: result_hashi_vault
delegate_to: 127.0.0.1
become: no
run_once: true
no_log: yes

- name: check db connect from app-servers
shell: |
mysql -u user database -p'{{ result_hashi_vault.msg.user }}' -h 127.0.0.1 -P 3306 -e "show variables like 'version'"
register: result_mysql_app_to_cloudsql


 おわりに

自分が実際に使って重宝した機能を書いてみましたが、実はここで紹介した機能は全てAdvanced Playbooks Featuresで紹介されています。

特にdebuggerはこの記事を書くときに初めて知ったのですが、引数、変数のダンプが個人的にはめちゃめちゃ便利で開発が捗りそうだと思いました。

使える機能はしっかり使って効率よく開発したいですね!