概要
この記事は 富士通クラウドテクノロジーズ Advent Calendar 2018 の11日目の記事になります。昨日は@jinshiさんのOperation as a code 実現に向けた運用統合ライブラリの作成活動についてでした。オペレーションのコード化は今最も求められているものと言っても過言ではないでしょう。運用の自動化だけでなく、ツールや言語のコードに定められた共通ルールが存在することによって、運用及び構成の説明をコードベースで行うことも可能です。今後の展開に目が離せませんね。
就職しました。FJCTでエンジニアをやってます@pachicourseです。
最近はもっぱらAnsibleを勉強しつつ得た知識を業務に転用しています。
現在はあるメンテナンス手順の自動化を目指す+チームにAnsibleのノウハウを共有するために活動していますが、今後はさらに範囲を広げて別チームや部署間でplaybook, rolesの引き渡しによる連携などをしていきたい所存です。
今回は0知識から始めた初心者が学習していて詰まったところや覚えておいた方が良いと感じたものについてまとめます。
環境
- Ansible: 2.4
- 管理対象サーバのpython: 3.6.6
用語
ここではAnsibleによってプロビジョニング、構成管理される対象のサーバを管理対象サーバと呼ぶことにします。詰まった所
shell moduleを使用してgrepするtaskがFailedとなり落ちる
題名の通りです。shellモジュールでgrepを叩いているだけなのにFailedになって落ちます。原因はgrepのexit statusにあります。
$ man grep
~省略~
EXIT STATUS
Normally, the exit status is 0 if selected lines are found and 1 otherwise. But the exit
status is 2 if an error occurred, unless the -q or --quiet or --silent option is used and a
selected line is found. Note, however, that POSIX only mandates, for programs such as grep,
cmp, and diff, that the exit status in case of error be greater than 1; it is therefore
advisable, for the sake of portability, to use logic that tests for this general condition
instead of strict equality with 2.
~省略~
ざっくり言うとマッチする行が存在する場合にexit statusが0になり、見つからなければ1になります(これが原因で落ちる)。マッチしなかった場合の空文字列も欲しいもしくはマッチしなくても落ちないようにしたい場合は結果を
cat
に流してあげるのがシンプルです。- name: Check contents of the file
shell: 'grep hoge hoge.txt | cat'
register: grep_result
registerはmoduleの結果を保存しておくことができます。grepの結果はgrep_result.stdout
で取り出すことが出来ます。特定のファイルの数を数えたいときはcat
の部分をwc -l
などにすると良いでしょう。crontab -lもFailedになりうる
です。crontab -l
を実行した際にスケジューリングされたタスクが対象ユーザに存在しない場合exit statusは1となりFaildになります。例えばrootユーザ用に設定されたタスクが存在しない場合、”stderr”: “no crontab for root”というメッセージが確認できます。この場合は
failed_when: False
として無視するのが手っ取り早いです。- name: Ensure crontab_backup file exists
shell: crontab -l > /path/to/backup/dir/crontab_backup
failed_when: False # taskが設定されていないときfailedになってしまうので
limitオプションを指定した場合localhostが対象から外れる
外れます。あまり無いシチュエーションですがリモートの管理対象サーバの一部の他にローカルに何かしらの設定を適用したい場合には、limitオプションへ対象group/hostsの情報に加えてlocalhostの記述も追加する必要があります。
$ ansible-playbook site.yml --limit group1,localhost -i inventoryファイルのpath
覚えておきたいと思ったところ
mysql dbにデータを挿入する
Ansibleは様々なDatabase moduleを持ちます。それぞれのmoduleでは、ユーザ設定を行ったり、DBの追加、レプリケーションの設定などが可能です。
mysql_* moduleの中にはクエリを発行してくれるmoduleがあるかと期待しましたがどうやら無いようです(Influxdbにはありそうです)。
sqlファイルに起こして
mysql_db module
のstate
パラメータをimport
にセットして使うか、単純にshellモジュールを使用します。- mysql_db:
name: db_name
state: import # ここが大事です
target: /path/hoge.sql # 管理対象サーバ視点でのpathです
includeは非推奨になりました
Ansibleでは2.4からincludeが非推奨(DEPRECATED)になり、実行時に警告が出るようになりました(includeは2.8で廃止予定です)。playbookやtasksのincludeには、import_[tasks|playbook] または include_[tasks|playbook]を使います。
import_は静的読み込みで、include_は動的読み込みです。
基本的には実行前に文法エラーを吐いてくれるimport_を用いるのが良いと思います。with_itemsなどのloopは実行時に評価される必要があるのでinclude_を使わなければエラーとなります。
# 非推奨1
- include: playbook.yml
# 非推奨2
- include: tasks.yml
with_items: '{{ something }}'
↓
# 修正後1
- import_playbook: playbook.yml
# 修正後2
- include_tasks: tasks.yml
with_items: '{{ something }}'
hostsを正規表現で指定する
見出し通りです。特定の文字にマッチするhostsのみを実行対象とすることが出来ます。Ansibleでは先頭に~
をつけると正規表現として扱われます。## webserver1ケタ番台のサーバにだけfuga roleを適用
- name: hoge
hosts: ~webserver00([0-9])
roles:
- fuga
変数から渡すこともできます## 渡されたhosts_patternを適用(hoge.yml)
- name: hoge
hosts: '{{ hosts_pattern }}'
roles:
- fuga
## 以下を実行する形でもOK
ansible-playbook hoge.yml -i inventoryファイルのpath -e 'hosts_pattern=~webserver00([0-9])'
-e 及び –extra-varsオプションはコマンド実行時に変数と値のセットを渡せます↑をwhenで実装する
タスクごとに実行ホストを絞りたい場合便利です。Ansibleにはinventory_hostname
というマジック変数が定義されており、実行対象ホストの名前が格納されています(ここで格納されるのはinventoryファイルに定義されているhostnameです)。match()
を使うことで正規表現による絞り込みができます- name: do something
shell: echo hello
when: inventory_hostname | match('webserver00[0-9]')
管理対象サーバ上のPythonを指定する
Ansibleはほとんどの場合、moduleを実行するために管理対象サーバにPythonを必要とします。 Ansibleでは実行時に標準パス以外のPythonインタプリタを指定することが出来ます。 設定方法はansible_python_interpreterという変数にpathを渡します。 Ansibleでは変数を様々な場所で定義できるので、予めgroup_vars/something.ymlに定義したり、set_factで指定したりすることができ、管理対象サーバ全体に適用、特定のグループに適用、ある処理だけ別のPythonを使用、などといった動作が実現可能です。# group_vars/all.ymlで全体指定したり
## all.yml
ansible_python_interpreter: /path/to/python/bin
# set_factで定義
## something_role/tasks/main.yml
- name: Set python interpreter.
set_fact:
- ansible_python_interpreter: /path/to/python/bin
# inventoryで特定グループに対して定義
## inventory
[group_a:vars]
ansible_python_interpreter=/path/to/python/bin
# 実行時にextra-varsで指定
$ ansible-playbook site.yml -e 'ansible_python_interpreter=/path/to/python/bin'
limitやextra-varsはファイルに切り分けられる
上記オプションの引数は直接コマンドラインで渡す方法もありますが、ファイルに切り出すことも可能です。その場合は @file_name という形式で指定します。 [plain] $ ansible-playbook site.yml -i inventoryファイル –limit @limit_file –extra-vars @extra_vars_file [/plain] Ansibleでは、playbookの実行に失敗したときに対象ホストやグループ名を記述したretryファイルを生成します。retryファイルはlimitオプションで指定するように促されます。 [plain] ~省略(failedメッセージ)~ to retry, use: –limit @/root/ansible/test_task.retry PLAY RECAP ******************************************************************************************** cent : ok=0 changed=0 unreachable=0 failed=1ubuntu : ok=0 changed=0 unreachable=0 failed=1 [/plain]
小ネタ
同名グループを2回inventoryに書いたとき
統合されます。こんな感じのinventoryがあったとします。[group_a]
cent
[group_a]
ubuntu
以下のようなplaybookを用意(test_task.yml)---
- hosts: all
gather_facts: no
tasks:
- debug:
var: groups
こうなります。ansible-playbook test_task.yml -i inventory
PLAY [all] ********************************************************************************************
TASK [debug] ******************************************************************************************
ok: [cent] => {
"groups": {
"all": [
"cent",
"ubuntu"
],
"group_a": [
"cent",
"ubuntu"
],
"ungrouped": []
}
}
ok: [ubuntu] => {
"groups": {
"all": [
"cent",
"ubuntu"
],
"group_a": [
"cent",
"ubuntu"
],
"ungrouped": []
}
}
PLAY RECAP ********************************************************************************************
cent : ok=1 changed=0 unreachable=0 failed=0
ubuntu : ok=1 changed=0 unreachable=0 failed=0
group_aにcent,ubuntuが所属していることが分かります。