こちらは Ansible Advent Calendar 2018 の14日目の記事です。
はじめに
普段から構成管理にはAnsibleを使用していて、他にも作業の自動化でもガッツリ使っていますが、その際に使用して重宝したちょっと高度な機能と簡単な例を紹介したいと思います。
概要
以下の機能を紹介します。そんなの知ってるぜというかたはすっ飛ばしてください。
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単位でbecome
やwhen
ができるのでコード全体の見通しも良くなります。
---
- 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
はこの記事を書くときに初めて知ったのですが、引数、変数のダンプが個人的にはめちゃめちゃ便利で開発が捗りそうだと思いました。
使える機能はしっかり使って効率よく開発したいですね!