はじめに
最近では、弊社でもMySQL等の構築にAnsibleでの納品が増加しています。
そんな中で、Ansibleの実行で問題が発生した場合の解決の手助けになるであろうデバッグ方法について、振り返りたいと思います。
デバッグする方法は、他にもあるかと思いますが、今回は、以下の4つの方法について記載します。
2. ansible-playbookのオプション「-v(–verbose)」を使用する
3. Playbook Debuggerを使用する
4. リモート実行ファイルを確認する
1. debugモジュールを使用する
基本ですね。[register]に登録された内容を確認したりする際に、よく使用します。
- タスク定義 (/var/lib/mysql ディレクトリの存在を確認)
1 2 3 4 5 6 7 8 |
- name: check directory exists stat: path: /var/lib/mysql register: res_exists - name: debug var res_exists debug: msg: "{{ res_exists }}" |
- 存在した場合の出力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
TASK [debug var res_exists] **************************************************************************************************************************************************** ok: [sample_node1] => { "msg": { "changed": false, "failed": false, "stat": { "atime": 1539078759.2616446, "attr_flags": "", "attributes": [], "block_size": 4096, "blocks": 0, "charset": "binary", "ctime": 1539257220.6302285, "dev": 64768, "device_type": 0, "executable": true, "exists": true, "gid": 993, "gr_name": "mysql", "inode": 101087109, "isblk": false, "ischr": false, "isdir": true, "isfifo": false, "isgid": false, "islnk": false, "isreg": false, "issock": false, "isuid": false, "mimetype": "inode/directory", "mode": "0751", "mtime": 1539257220.6302285, "nlink": 3, "path": "/var/lib/mysql", "pw_name": "mysql", "readable": false, "rgrp": true, "roth": false, "rusr": true, "size": 18, "uid": 996, "version": null, "wgrp": false, "woth": false, "writeable": false, "wusr": true, "xgrp": true, "xoth": true, "xusr": true } } } |
- 存在しない場合の出力
1 2 3 4 5 6 7 8 9 10 |
TASK [debug var res_exists] **************************************************************************************************************************************************** ok: [sample_node1] => { "msg": { "changed": false, "failed": false, "stat": { "exists": false } } } |
以降の処理で、存在しない場合に実行したいタスクであれば
「when: res_exists.stat.exists == false」とすればよいと判断できます。
存在する場合には、オーナーや権限、ディレクトリであるかを確認したりする情報が含まれているので、これらの情報を以降の処理で使用する事もできます。
2. ansible-playbookのオプション「-v(–verbose)」を使用する
ansible-playbookコマンド実行時に「-v」オプションを追加する事で、出力結果にタスクの詳細が出力されます。
また、「v」の数を増やすとより詳細な出力となり、「-vvv」と3つ以上指定するとSSH関連の接続情報まで出力する事ができます。
1.と同様の処理でステータスを確認したい場合も、debugモジュールを使用しなくても「-v」オプションを付与して実行するだけで、見にくいながらも同様の結果が出力されます。
- タスク定義
1 2 3 |
- name: check directory exists stat: path: /var/lib/mysql |
- 存在した場合の出力
1 2 |
TASK [check directory exists] ************************************************************************************************************************************************** ok: [sample_node1] => {"changed": false, "stat": {"atime": 1539078759.2616446, "attr_flags": "", "attributes": [], "block_size": 4096, "blocks": 0, "charset": "binary", "ctime": 1539257220.6302285, "dev": 64768, "device_type": 0, "executable": true, "exists": true, "gid": 993, "gr_name": "mysql", "inode": 101087109, "isblk": false, "ischr": false, "isdir": true, "isfifo": false, "isgid": false, "islnk": false, "isreg": false, "issock": false, "isuid": false, "mimetype": "inode/directory", "mode": "0751", "mtime": 1539257220.6302285, "nlink": 3, "path": "/var/lib/mysql", "pw_name": "mysql", "readable": false, "rgrp": true, "roth": false, "rusr": true, "size": 18, "uid": 996, "version": null, "wgrp": false, "woth": false, "writeable": false, "wusr": true, "xgrp": true, "xoth": true, "xusr": true}} |
- 存在しない場合の出力
1 2 |
TASK [check directory exists] ************************************************************************************************************************************************** ok: [sample_node1] => {"changed": false, "stat": {"exists": false}} |
3. Playbook Debuggerを使用する
Playbook Debuggerはバージョン2.5以降では、debuggerキーワードで設定する事ができます。
Playbook Debuggerを使用すれば、モジュールの引数を更新したり、新しい変数や引数を使用してタスクを再実行したりする事ができます。
debuggerキーワードに設定できる値
設定値 | 説明 |
---|---|
always | 常にデバッガを起動する |
never | デバッガを起動しない |
on_failed | タスクが失敗した場合にのみデバッガを起動する |
on_unreachable | ホストに到達できなかった場合にのみデバッガを起動する |
on_skipped | タスクがスキップされた場合にのみデバッガを起動する |
デバッガでの操作
デバッガを起動すると、以下のような操作を行うことができます。
コマンド | 説明 |
---|---|
p task | 処理中のタスクを表示 |
p task_vars | 処理中タスクで使用可能な変数を表示 |
p task.args | モジュールに設定された引数を表示 |
p host | 処理中のホストを表示 |
p result | 対象のタスクの処理結果情報 |
task.args[key] = value | モジュールの引数を設定する |
task_vars[key] = value | 変数を設定する |
r | タスクを再実行する |
c | 次のタスクを実行する |
q | デバッガを終了する |
それでは、デバッガを起動してみましょう。
- タスク定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- hosts: all debugger: on_failed vars: mysql_dir: /var/lib/mysql tasks: - name: check directory exists stat: path: "{{ mysql_path }}" register: res_exists - name: skip task debug: msg: "nothing!" when: res_exists.stat.exists == false |
[debugger: on_failed]を設定して、エラーが発生した際にデバッガが起動するようにします。
1つ目のタスクで使用している{{ mysql_path }}変数は定義されていないので、エラーが発生します。
- プレイブックの実行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
TASK [check directory exists] ************************************************************************************************************************************************** fatal: [sample_node1]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'mysql_path' is undefined\n\nThe error appears to have been in '/home/fukumoto/ansible/simple/main.yml': line 6, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: check directory exists\n ^ here\n"} [sample_node1] TASK: check directory exists (debug)> p task_vars['mysql_path'] ***KeyError:KeyError('mysql_path',) [sample_node1] TASK: check directory exists (debug)> task_vars['mysql_path'] = '/var/lib/mysql' [sample_node1] TASK: check directory exists (debug)> p task_vars['mysql_path'] '/var/lib/mysql' [sample_node1] TASK: check directory exists (debug)> r ok: [sample_node1] TASK [skip task] **************************************************************************************************************************************************** skipping: [sample_node1] PLAY RECAP ********************************************************************************************************************************************************************* sample_node1 : ok=1 changed=0 unreachable=0 failed=0 |
1つ目のタスクで変数が定義されていないので、エラーが発生し、デバッガが起動されます。
デバッガが起動すれば、{{ mysql_path }}変数に値を設定して、該当タスクを再実行し、以降の処理を続行させています。
4. リモート実行ファイルを確認する
Ansibleのデフォルト動作では、ターゲットとなるサーバ上へPythonスクリプトを配布して、ターゲットのサーバ上でスクリプトを実行した結果を、Ansibleを実行したサーバへ返却するという動作をします。
そして、ターゲットのサーバ上で実行したスクリプトは、処理が終われば削除されます。
少しコアな方法ですが、環境変数「ANSIBLE_KEEP_REMOTE_FILES=1」を設定してAnsibleを実行すれば、その実行スクリプトをターゲットのサーバ上に、処理が終わっても削除しないようすることができます。
これを利用すれば、ターゲットとなるサーバ上で、直接、スクリプトを実行して動作を確認する事も、スクリプトに変更を加えてデバッグしながら実行する事も可能となります。
- タスク定義
1 2 3 4 5 6 |
- hosts: all tasks: - name: check directory exists stat: path: "/var/lib/mysql" register: res_exists |
- プレイブックの実行
1 2 |
$ export ANSIBLE_KEEP_REMOTE_FILES=1 $ ansible-playbook -i {インベントリファイル} {プレイブックファイル} |
- ターゲットのサーバ上で残されたPythonスクリプトを実行
1 2 3 4 |
[vagrant@server ~]$ ls ~/.ansible/tmp/ ansible-tmp-1539330036.6-171482125343958 ansible-tmp-1539330039.25-276163780977530 [vagrant@server ~]$ python ~/.ansible/tmp/ansible-tmp-1539330039.25-276163780977530/stat.py {"invocation": {"module_args": {"checksum_algorithm": "sha1", "get_checksum": true, "follow": false, "path": "/var/lib/mysql", "get_md5": null, "get_mime": true, "get_attributes": true}}, "stat": {"charset": "binary", "uid": 996, "exists": true, "attr_flags": "", "woth": false, "isreg": false, "device_type": 0, "mtime": 1539257220.6302285, "block_size": 4096, "inode": 101087109, "isgid": false, "size": 18, "executable": true, "isuid": false, "readable": false, "version": null, "pw_name": "mysql", "gid": 993, "ischr": false, "wusr": true, "writeable": false, "mimetype": "inode/directory", "blocks": 0, "xoth": true, "islnk": false, "nlink": 3, "issock": false, "rgrp": true, "gr_name": "mysql", "path": "/var/lib/mysql", "xusr": true, "atime": 1539078759.2616446, "isdir": true, "ctime": 1539257220.6302285, "isblk": false, "wgrp": false, "xgrp": true, "dev": 64768, "roth": false, "isfifo": false, "mode": "0751", "rusr": true, "attributes": []}, "changed": false} |
※スクリプトは、Ansibleで指定した実行ユーザの「HOMEディレクトリ/.ansible/tmp/」以下に保存されます。
まとめ
モジュールを使用して、想定通り動かずにcommandモジュールや、shellモジュールでコマンドを発行する処理に変更したって方もおられるのではないでしょうか。
モジュールの仕様として実行できない処理であれば仕方ないですが、結果、冪等性を担保する為にタスクを追加して複雑なプレイブックになったりしがちですよね。
これらのデバッグ方法を活用すれば、結構すんなり解決する事もあるかと思いますので、頑張ってデバッグしましょう。
最後に・・・
Ansible Galaxyでは、弊社でサポートしているMySQL, MariaDB, Percona Server等のロールも、たくさん配布されています。
配布されているロールを使用する事で、簡単に環境が構築できますので、お気に入りのロールを見つけて、快適なAnsibleライフを楽しみましょう。