サーバー構築作業を行う際、設定ファイルに何かを書く作業は欠かせない。
Ansibleは多数のモジュールを提供していて人によって設定ファイルの書き方は様々だ。
この記事は私なりのベストプラクティスであり、正解ではない。
command系(shell含め)
command系は使い勝手がいいが、冪等性が保証されない。
これはAnsibleだけではなくChefとかも同じだ。
そもそも何が実行されるのか分からないので、Ansibleが冪等性を保証することはできない。
使用者が直接やるしかない。
ファイルが存在したらTaskを実行しないとかプロセスが立ち上がったらTaskを実行しないとかなど。
command系のTaskはPlaybookを実行するたびに毎度実行される。
最初1回だけ実行したい場合は「args」のcreatesを追加する。
- name: create a file
shell: echo "hello world!" > /tmp/hello
args:
creates: /tmp/hello
これは「/tmp/hello」というファイルが存在する場合は二度と実行されない。
しかし、変更事項があって適用したい場合は元のファイルを消す必要がある。
いろいろ面倒な手間がかかるので、次のcopyモジュールを使うことを推奨する。
copy
copyモジュールは単純にファイルをコピーするときだけ使うものではない。
ファイルの生成に関わるほとんどの操作はcopyモジュールで実現できる。
- name: create a file
copy:
dest: /tmp/hello
content: hello world
contentの内容に変更がない場合はchangedにならない。
contentの内容に変更がある場合はchangedになり、変更内容が反映される。
command系よりシンプルで使い勝手がいい。
replace
既に存在するファイルからマッチするところだけ変更したい場合は、replaceモジュールを使う。
lineinfileはregexpでマッチされる最初1回は問題がないが、再び実行するとマッチされないから新しくlineを追加してしまう。
- name: modify a file
lineinfile:
dest: /tmp/hello
regexp: '\sworld$'
line: ' ansible'
これで試してみると「hello world」が「ansible」になっている。
そして再び実行すると「ansible」が2行になっている。
またlineinfileはマッチする部分だけではなくマッチされたらそのlineが新しいlineに変更されるので、注意を払う必要がある。
勘違いによる不具合が発生する可能性が高いのであまり推奨しない。
command系もそうだがどうしても使いたい場合はstatとregisterを組み合わせて、lineinfileのところでwhenでregisterの変数をチェックして修正が要らない場合はskipするようにする。
それに比べて__replaceはマッチされる部分だけ変更ができるし、マッチされなかったらchangedにならない。__
- name: modify a file
replace:
dest: /tmp/hello
regexp: '\sworld$'
replace: ' ansible'
blockinfile
マッチされるところの前後に何かを追加したい場合は、blockinfileを使う。
追加する内容に変更がない場合はchangedにならない。
再び実行しても最初実行したときと同じだ。
- name: add a new line
blockinfile:
dest: /tmp/hello
insertafter: '^hello world$'
content: hello ansible
markerが自動で追加されてlineの前後についてしまう。
markerの内容は修正が可能だ。
(https://docs.ansible.com/ansible/blockinfile_module.html#examples)
cat /tmp/hello
hello world
# BEGIN ANSIBLE MANAGED BLOCK
hello ansible
# END ANSIBLE MANAGED BLOCK