Edited at

Ansible勉強&実践結果(随時更新)


概要

Ansibleを学習するのにあたっての勉強メモをかねています

書籍「Asinble徹底入門」と実践した中で学んだことを併せて記録します

動作環境は、先日投稿した「Docker内でAnsibleの勉強をしよう」で実施しています

現在は自社Saasで実践的に利用しています。先日Ansibleユーザ会で「自社クラウドサービスをAnsibleで作った話 recap」を公開したので、参考になれば幸いです


Ansibleの特徴


  • Python製

  • 非エージェント型の構成管理ツール

  • プラットフォーム問わずに利用できる(Linux、Windows、Docker、Iaas、ベアメタル、ネットワーク機器、HWベンダーのマネージド機器)

  • yml、jsonで構成定義ファイル(Playbook)を書けるため、学習コストが低い

  • Red Hat社がメインで開発しているOSS

    Red Hatがバックについているので、長期的な開発が見込める

    各ベンダーを巻き込んだ取り組みをしていることもあり、対応プラットフォームが多い

  • NASA、NEC、HP、Juniper、CISCO、EA、CocaColaなど名だたる大企業が使っている


呼び名の整理


  • Ansible Core ・・・ Ansibleの本体部分


  • Ansible Tower ・・・ Red Hat社が提供するWeb GUIベースのAnsible管理ツール(AWXの特定のバージョンから切り出したバージョン)


  • Ansible Engine ・・・ Red Hat社がエンタープライズ向けに有償保証を加えた商品(コアモジュールも保証対象)


  • AWX ・・・ Ansible Towerの開発版でApache v2ライセンスで運用されている


Ansibleの構成要素


  • Ansible本体

  • Inventory ・・・ 操作対象のマシン(ホスト)の管理ファイル

  • Module ・・・ 操作対象のマシンを操作する


    • コアモジュールと有志モジュールが多数(13000?)


    • 自作モジュールも簡単に作れる



  • Playbook ・・・ どのInventoryにどのModuleでどのように操作するかを定義する定義ファイル(手順書)


実行イメージ


YAMLファイル

YAMLファイルの書式


  • 先頭行は---で開始すること

  • インデントは半角スペース2つ

  • コメントは#


sample.yml

---

hoge
fuga


わかりやすいスライド


Ansible設定

Ansibleの動作設定を定義するための設定ファイル

v2.4 リファレンス


優先順位


  1. ANSIBLE_CONFIG環境変数 (v1.5以前は2番目)

  2. カレントディレクトリ(Playbook)のansible.cfg (v1.5以前は1番目)

  3. ~/.ansible.cfg

  4. /etc/ansible/ansible.cfg


hostチェックをしない

新しいホストに接続する際に、該当のホストを受け入れるかの質問をしないようにする

host_key_checking=False


変数のマージ

同名の変数があった場合は、変数をそのまま上書きするのではなく


存在しないプロパティ値をマージする

hash_behaviour=merge


リトライファイルの出力をしないようにする

実行時失敗時に生成されるxxxx.retryファイルを生成しないようにする

retry_files_enabled=False


実行ログの出力

コンソールに表示された実行ログをログファイルに出力

log_path=path/to/xxxx.log


実行時デバックモード

タスクの実行エラー時に、デバック画面に移行する

strategy=debug

http://docs.ansible.com/ansible/latest/playbooks_debugger.html


タスクの実行時間の表示

callback_whitelist=profile_tasks


Inventory


  • Inventory(静的)


    • INI形式、YAML形式でホスト情報を記述

    • ホストの接続情報があらかじめわかっている



  • Dynamic Inventory(動的)


    • ホスト情報をjsonに出力するスクリプト

    • ansibleコマンド実行時にリアルタイムでスクリプトが実行される

    • スクリプト経由で動的にホスト情報を取得できる




Inventoryファイル(静的)

書式(INI形式)


hosts

[app]

app1 ansible_host=xxxx.app1.com
app2 ansible_host=xxxx.app2.com

[db]
db1 ansible_host=xxxx.db1.com
db2 ansible_host=xxxx.db2.com

[app:vars]
admin_username=app_user
admin_uid=1001

[db:vars]
admin_username=db_user
admin_uid=1002

[pod1]
app1
db1

[pod2]
app2
db2



  • グループごとに[グループ名]セクションで区切る

  • グループごとに変数を定義する場合は、[グループ名:vars]セクションを用いる(外部ファイル化も可能(後述))

    グループ変数 < ホスト変数でホスト変数優先、グループ変数が重複した場合は後優先

  • 真偽値は大文字開始(True/False)であること

    小文字の場合は文字列として扱われる

  • リストや辞書構造は扱えない

  • 複数のグループに所属することができる

  • ホスト名が同じ場合は、同一ホストとして扱われる


IP範囲指定

172.17.12.0~172.17.12.255まで一括指定

[myhosts]

172.17.12.[0:255]


グループごとにssh鍵やユーザの設定

例)AWS EC2

[aws:vars]

ansible_user=ec2-user
ansible_ssh_private_key_file=~/.ssh/hogehoge.pem

リファレンス


Dynamic Inventory Script(動的)

システム連携で動的に操作対象のホストが変わるようなシーンで使う

scriptによって生成されたinventoryに各種操作を行う

scriptはpython、javascript、php、javaと開発言語問わず、dynamic inventoryとして認識できるjsonを返せばOK


実行方法(例)

inventory.jsのようにプログラム内でinventoryを定義したjsonを返す

サンプルはjsになっていますが、python, php, shellscriptでもOK

> ansible-playbook -i inventory.js playbook.yml


inventory.js



#!/usr/bin/node

// ↑はnodejsの実行パスに適宜変更してください

console.log(dynamicHosts());

function dynamicHosts() {
const inventory = {};
const hosts = ['12.345.67.890']; // instance ip
const vars = {
'var1': True,
'ansible_ssh_private_key_file': '/root/.ssh/hogehoge.pem', // ssh鍵の個別指定
'ansible_ssh_user': 'hogehoge-user' // sshユーザの個別指定
};

inventory['groupname'] = {
'hosts': hosts,
'vars': vars
}

return JSON.stringify(inventory);
}



playbook.yml

---

- name: Test dynamic inventory
become: true
hosts: "groupname"
tasks:
- name: echo debug
debug: var='test'



参考


Playbook


基本構成


playbook.yml

---

- name: play book
hosts: all
tasks:
- name: <task name>
<module name>:
<module arg1>: <arg1 value>
<module arg2>: <arg2 value>



実行方法

> ansible-playbook -v path/to/playbook.yml

-vで実行内容の表示


タスクの基本構成

~ 中略 ~

tasks:
- name: <task name>
<module name>:
<module arg1>: <arg1 value>
<module arg2>: <arg2 value>

例)

  tasks:

- name: install nginx
yum:
name: nginx
state: present

または(簡略化する書式)

  tasks:

- name: install nginx
yum: name=nginx state=present


操作対象のホストから情報収集しない場合

  gater_facts: false


管理者権限で実行

  become: true

# v1.9未満
sudo: yes


ユーザ指定実行

  become_user: xxxxx

# v1.9未満
sudo_user: xxxxx


アトリビュート


リトライ処理(retries

Webサーバの立ち上げ&死活確認する場合は、retriesを用いる

untilの条件になるまで指定されたretries数を実行する

実行間隔はdelay


retries.yml


- name: wait app is available
uri:
url: "https://qiita.com"
method: "GET"
register: _response_result
until: _response_result.status == 200
retries: 5
delay: 30


出力ログにパスワードを非表示にする(no_log

> - name: secret task

shell: /usr/bin/do_something --value={{ secret_value }}
no_log: True


モジュール


yumパッケージのインストール(yum)


yum.yml

- name: remove the Apache package

yum:
name: httpd
state: present

stateの状態名が過去分詞になっている

インストール済み状態であることを確認するであって、インストールするではない

すでにインストール済みの場合は実行せずに処理が進む(冪等性)


  • present

  • latest

  • absent


複数のパッケージを同時インストール(カンマ区切り v2.x)


yum.yml

- name: install the Apache, nginx, tomcat package

yum:
name: httpd,nginx,tomcat
state: present


複数のパッケージを同時インストール(with_items)


yum.yml

- name: install the Apache, nginx, tomcat package

yum:
name: "{{ item }}"
state: present
with_items:
- httpd
- nginx
- tomcat


ファイル操作(copy)


copy.yml


- name: deploy web contents
copy:
src: ./www/site-a/index.html
dest: /usr/share/nginx/html/site-a/index.html

※大量ファイルをコピーしたい場合はsynchronizeモジュールを使おう


 双方にrsyncがインストールされていることが必要


コピー先のファイルを上書きしないようにする


copy.yml

copy:

force: false


コピー先のファイルを上書きせずに古いファイルをBKする


copy.yml

  copy:

backup: true

※etc/xxx.d/のように自動でファイル読み込みする場合は、古いファイルも読み込まれるので要注意


リモート内でのファイル操作(ver2.x)


copy.yml

  copy:

remote_src: yes


行単位でのファイル編集(lineinfile)


lineinfile.yml

- name: edit sshd config(deny root user login with password)

lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^PermitRootLogin\s+'
line: PermitRootLogin without-password
validate: ssh -t -f %s

%sで一時ファイルに対してバリデーションする


任意コマンドを実行(command)


command.yml

- name: make ssh key in tmp dir

command: "/usr/bin/ssh-keygen -b 2048 -t rsa -N '' -f /tmp/new-id_rsa"
args:
creates: /tmp/new-id_rsa
- name: echo home env
command: "echo {{ ansible_env.HOME | quote}}"

※ パイプ、リダイレクト(<, >)が使えない(shellモジュールでは使えるが推奨されていない)

※ $記号を使った環境変数を使えない

※ 変数を扱う場合は、quiteフィルターでサニタイズすること


Python未対応の環境でコマンド実行(raw)


raw.yml

- name: yum install python-simplejson in raw env

raw: yum install -y python-simplejson

※pythonを載せられない環境

※2.4~2.6未満のpythonの環境(simplejsonのみをインストール)


処理中に動的にInventoryに新規ホストを追加(add_host)

※add_hostした次のplayから操作が適用される


addhost.yml

- name: add new host to Inventory

add_host:
name: created-host
groups: created,app
ansible_host: "{{ created_host_ip }}"
ansible_port: 22


serviceの管理(service)


service.yml

- name: start nginx service

service:
name: nginx
state: started
enabled: true


動的にホストをグループ分け(group_by)

登録済みホストを新たにグループ分け

※Ansibleではグループ名にスペースを使えないため、-に変換される


group_by.yml

- name: group by OS family

group_by:
key: "{{ ansible_os_family }}"

- name: Red Hat play
hosts: Red Hat

- name: Debian play
hosts: Debian



リモート先での実行結果を表示する(debug/register)

registerに一度出力結果を代入して、debugモジュールでregisterの内容を出力


debug.yml

- name: echo remote stdout

command: echo $PATH
register: remote_path
- name: echo remote path
debug: var=remote_path.stdout


ファイルまたはファイルシステムのステータスを取得する(stat)

ファイルの所有権の有無を確認し、権限がない場合は、failモジュールでエラー表示


stat.yml

- name: check foo.conf exist

stat:
path: /etc/foo.conf
register: st
- fail:
msg: "Whoops! file ownership has changed"
when: st.stat.pw_name != 'root'


条件に応じて実行(when)

whenの条件を満たした場合は、タスクを実行する

リスト形式の場合は、AND条件として認識される

MODE == 'product' OR ansible_os_family == 'RedHat' でOR条件になる


when.yml

vars:

MODE: product

~ 中略 ~

- name: deploy Web contents for product page
copy:
src: ./www/site-a/product.html
dest: /usr/share/nginx/html/site-a/product.html
when:
- MODE == 'product’
- ansible_os_family == 'RedHat'



特定の文字が含まれている場合に実行(match

url変数に「http://example.com/users/.*/resources/.*」が含まれている場合に実行


match.yml

- debug:

msg: "matched pattern 1"
when: url is match("http://example.com/users/.*/resources/.*")


対話形式の自動入力


同一の質問が複数あった場合の書き方

参考


長時間の処理に対応する

asyncpollディレクティブを使用する


async.yml


- name: 最大45秒の処理を5秒ずつポーリングする
command: /bin/sleep 15
async: 45
poll: 5

http://docs.ansible.com/ansible/latest/playbooks_async.html


特定のホストが接続できるまで待機するwait_for

例えばEC2を生成後にそのインスタンスに対して初期設定などをしたい時に、接続可能になるまで待機する時に用います


wait_for_playbook.yml


- name: create new EC2 instance
hosts: local
gather_facts: False
tasks:
- name: create new instance
ec2:
~中略~
register: _instance_result

# 新しいインスタンスを後続の処理で対象ホストとして設定する
- name: set new host
add_host:
name: "{{ item.public_ip }}"
group: new_host
with_items: "{{ _instance_result.instances }}"

# 22ポートが接続可能になるまで待機(最大待ち時間はtimeout)
- name: wait new instance is available
wait_for:
timeout: 600
host: "{{ item.public_ip }}"
port: 22
state: present
with_items: "{{ _instance_result.instances }}"

# 新しいホストでの処理
- name: setup os in new instance
hosts: new_host
# 接続ユーザと鍵を変える場合
vars:
ansible_user: "ec2-user"
ansible_ssh_private_key_file: "/root/.ssh/hogehoge.pem"
tasks:
- name: update to latest packages
yum:
name: "*"
state: latest
become: True



OS再起動

OSを再起動する際に、処理が終了しないようにする

sleep、async:1、poll:0、local_actionでwait_forがポイント!

sleepがないと即座にシャットダウンプロセスに入るため、接続断と見なされてしまう

async1、poll:0で非同期処理としてこのshellモジュールの結果を待たずに次の処理に進みます

リモートが再起動処理中のため、実行元をローカル(local_action)に移し、22ポートが返ってくるまで待つ


reboot_os.yml

- name: Reboot OS

shell: "sleep 2 && reboot"
args:
executable: /bin/bash
async: 1
poll: 0
become: yes

- name: Wait for rebooted
local_action:
module: wait_for
host: "{{ inventory_hostname }}"
port: 22
delay: 30


※上記よりもrebootモジュールが安定していますので、そちらをご利用ください


変数

Jinja2というPythonのテンプレートエンジンを用いて変数展開している

playbook内で使用するための変数はいくつかの手段で定義することができる

定義方法によって適用の優先順位がある

優先順位をうまく活用することで、より柔軟な運用を実現することができる

※参考・・・これだけ覚えとけばもう怖くないAnsibleの(動的)変数


Facts変数

定義済みのansible変数

例)

- ansible_os_family : OSのファミリー情報(CentOS、RedHat)

- ansible_distribution_major_version : OSのメジャーバージョン情報

- ansible_ssh_user : ssh接続ユーザ

- ansible_ssh_host : sshホスト

詳細はこちらまで


ファイル名規則と定義方法

Inventory用の変数をyamlファイルに切り出せる


  • ホスト変数定義ファイル(host_vars/<host名>.yml)

  • グループ変数定義ファイル


    • group_vars/<group名>.yml

    • group_vars/<group名>/hogehoge.yml(ファイル名の昇順で上書きされる)



  • InventoryファイルまたはPlaybookファイルと同じ場所に配置(ファイル名規則に依存しない)

  • playbook実行時のコマンドで変数ファイルの読み込みを行う(ファイル名規則に依存しない)

  • inventory内で変数を直接定義(ファイル名規則に依存しない)

  • playbook内で変数を直接定義(ファイル名規則に依存しない)

  • playbookからの変数ファイルの読み込み(ファイル名規則に依存しない)


inventoryフォルダ/playbookフォルダ
|
|-----hosts/playbook
|
|------group_vars
| |----all.yml(全体に適用できる)
| |----グループ名(グループごとに変数ファイルを分割できる)
| |---0_first.yml
| |---1_second.yml
| |----app.yml
| |----db.yml
|
|------host_vars
|----app1.yml
|----db1.yml
|----app2.yml
|----db2.yml


Ansible実行時に定義

値はすべて文字列として扱われるため、真偽値、数値には注意

> ansible-playbook -e 'key1=value key2=value' xxx.yml

値を適切な型として扱えるようにするために、json形式で定義

> ansible-playbook -e '{"key1": True, "key2": 100}' xxx.yml

外部ファイル(YAMLファイル)から読み込み

> ansible-playbook -e '@var-file.yml' xxx.yml


Playbook内で定義

PlayとTask内で直接変数を指定する

PlayまたはTask内のみで有効


vars.yml

---

- name: set vars
hosts: all
vars:
key1: value
key2: value


外部ファイルから読み込み

Play内のみ利用可能


vars_files.yml

---

- name: set vars
hosts: all
vars_files:
- xxx_vars1.yml
- xxx_vars2.yml



List変数のマージ

二つのlist型変数をマージする


marge_list_to_list.yml

- set_fact:

list1: [ "a", "b", "c" ]
list2: [ "e", "f", "g" ]

- set_fact:
list_all: "{{ list1 }} + {{ list2 }}"



変数をList変数に追加


variable_add_list.yml

- set_fact:

var1: "a"
list2: [ "e", "f", "g" ]

- set_fact:
list_all: "[ '{{ var1 }}' ] + {{ list2 }}"



List変数にdict変数を追加

ポイントは_listを配列定義後にもう一度同じ_list変数を定義&dict値の追加を行う


list_add_dict.yml


- set_fact:
_data:
- "1"
- "2"
- "3"

- set_fact:
_list: []
- set_fact:
_list: "{{ _list + [{ 'att1': item, 'attr2': 2 }] }}"
with_items: "{{ _data }}"

--------- result ---------
{
"attr1": "1",
"attr2": 2
},{
"attr1": "2",
"attr2": 2
},{
"attr1": "3",
"attr2": 2
}



dictから値を抽出したlistを作る

.values()でdictの値をlistで抽出することができる


dictvalue_to_list.yml


- set_fact:
_data:
a: 1
b: 2
c: 3

- set_fact:
_list: "{{ _data.values() }}"

- debug: msg=_list

--------- result ---------
"msg": [
"1",
"2",
"3"
]



List in ListのLoop(json_query

List内にあるListの値をループしながら取得したい時にjson_queryフィルターを用いると便利

それ以外で変数からデータを抽出したい時にもかなり便利


json_query.yml


# サンプル変数
- set_fact:
test:
results:
- stdout_lines:
- "val1"
- "val2"
p: yes
- stdout_lines:
- "val3"
- "val4"
p: no

# results配列内のstdout_lines配列の値を順に取得する
- debug:
msg: "{{ item }}"
with_items: "{{ test.results | json_query('[*].stdout_lines') }}"

ok: [app1] => (item=None) => {
"msg": "val1"
}
ok: [app1] => (item=None) => {
"msg": "val2"
}
ok: [app1] => (item=None) => {
"msg": "val3"
}
ok: [app1] => (item=None) => {
"msg": "val4"
}



Listから特定の文字列を抽出

特定の値を抽出して、新たな配列を作成

match対象の文字列に変数は使えないのか?


list_search.yml


- set_fact:
list:
- "val1"
- "val2"
- "val3"
- "val4"
- debug:
msg: "{{ list | select('match', 'val1') | list }}"

# 取得結果をlistに代入している
ok: [localhost] => {
"msg": [
"val1"
]
}



重複値があるListから一意値のListを抽出

uniqueフィルタを使用することでListから一意の値のみのListを抽出


uniqe_list.yml


- set_fact:
_list: ['1', '2', '2', '3']

- debug:
msg: "{{ _list | unique }}"

TASK [debug] *********************************
ok: [localhost] => {
"msg": [
"1",
"2",
"3"
]
}



文字列の任意のインデックスから文字を切り出す


get_char.yml


# 先頭から1文字を取得
- debug:
msg: "{{ 'hello qiita'[0] }}"

ok: [localhost] => {
"msg": "h"
}

# 末尾から1文字を取得
- debug:
msg: "{{ 'hello qiita'[-1] }}"

ok: [localhost] => {
"msg": "a"
}



パスワード変数(Vault

パスワードを生の状態で保管したくないので、暗号化して読み込む時に複合する

https://qiita.com/takuya599/items/2420fb286318c4279a02


変数ファイルの暗号化

鍵ファイルを用いた暗号化

> ansible-vault --vault-password-file=path/to/key encrypt path/to/var.yml


変数ファイルの復号化

> ansible-vault --vault-password-file=path/to/key decrypt path/to/var.yml


時刻関連


現在時刻の取得


time.yml


- debug: msg="{{ lookup('pipe', 'date +%Y-%m-%dT%H:%M:%S') }}"


時刻の加減算

時間の加減算をする

https://dateparser.readthedocs.io/en/latest/


time.yml


# 9時間を足す(9 hour)
- debug: msg="{{ lookup('pipe', 'date -d \"9 hour \" +%Y-%m-%dT%H:%M:%S') }}"

# 9時間を引く(9 hour ago)
- debug: msg="{{ lookup('pipe', 'date -d \"9 hour ago\" +%Y-%m-%dT%H:%M:%S') }}"



jinjaテンプレート内で変数値の生成

複雑な条件分岐で変数を生成したい時に、jinjaテンプレートで変数の値を生成することができます

ポイントは{%--%}です

-がないと変数ではなく、文字列として出力されますので、ご注意!

テンプレートで値の代入をする際は、必ずset 変数名が必要になります

そのため、下記のように例えば既存の変数を更新するような場合は、dummyのようにsetして値を更新します


jinja_variable.yml


- set_fact:
test: >-
{%- set qiita = {"test": "qiita"} -%}
{%- set dummy = qiita.update({"test": "qiita.com"}) -%}
{{ qiita }}

- name: test debug
debug: msg="{{ test }}"

ok: [localhost] => {
"msg": {
"test": "qiita.com"
}
}



jinjaテンプレート内で変数で無駄な空白や改行文字を防ぐ

jinjaテンプレート内で条件分岐などして変数を抽出する際に、

値に無駄な空白や改行文字が含まれるのを防ぐために{%- hogehoge -%}で囲むことで解決されます

今までif文を一行にまとめて書いていたが、これを活用することで複数行で書けるようになり、可読性も高くなる


jinja_remove_blank.yml


# Bad!!
- set_fact:
_test: >-
{% if true %}
Stop
{% endif %}

- debug:
msg: "{{ _test}}"

ok: [localhost] => {
"msg": " Stop\n"
}

# Good!!
- set_fact:
_test: >-
{%- if true -%}
Stop
{%- endif -%}

- debug:
msg: "{{ _test}}"

ok: [localhost] => {
"msg": "Stop"
}



優先順位(公式


  1. コマンド実行時に-eパラメーター

  2. set_fact、registerの値

  3. include_vars

  4. include paramsの変数

  5. Roleとinclude_roleのパラメータ

  6. Task内の変数

  7. Block内の変数

  8. Roleのvars内変数定義ファイル/inclue_varsモジュールで読み込んだ変数定義ファイル

  9. Play内のvars_filesで読み込んだ変数

  10. Play内のvars_promptで入力した変数

  11. Playのvarsの変数

  12. HostのFact情報

  13. Playbookディレクトリ内のhost_varsのホスト変数

  14. Inventoryディレクトリ内のhost_varsのホスト変数

  15. Inventoryファイル内のホスト変数

  16. Playbookディレクトリ内のgroup_varsのグループ変数

  17. Inventoryディレクトリ内のgroup_varsのグループ変数

  18. Playbookディレクトリ内のall変数

  19. Inventoryディレクトリ内のall変数

  20. Inventoryファイル内のグループ変数

  21. Roleのdefaults内のデフォルト変数定義ファイル


おすすめ優先順位

初めは優先順位の低い形式で定義し、運用を重ねた上で上位の優先順位を適用することでメンテコストを軽減することができる


変数の使い方

{{ key_name }} + "(ダブルクオート)で囲む必要がある


playbook.yml

---

- name: set vars
hosts: all
vars:
key1: value1
tasks:
- name: set vars
debug:
msg: "vars is {{ key1 }} "


階層構造になっている場合は、ドット繋ぎまたは['キー名']


vars.yml

Key:

subkey1: value
subkey2: value


playbook.yml

tasks:

- name: set vars
debug:
msg: "vars is {{ Key.subkey1 }} "
または
debug:
msg: "vars is {{ Key['subkey1'] }} "

階層構造が同じ変数があった場合は、優先順位で上書きされるが、

ansible.cfgでhash_behaviour = mergeと設定することで、値が自動的にマージされる

http://docs.ansible.com/ansible/latest/intro_configuration.html#hash-behaviour


分岐条件(if)

{% endif %}で終了すること

ダブルクオートを忘れずに!


playbook.yml

vars:

os: "{% if ansible_os_family == 'RedHat' %}"redhat"{% elif ansible_os_family == 'Debian' %}"debian"{% else %}"other"{% endif %}"


タスクの実行結果に応じて変数値を更新する場合(set_fact)


set_fact.yml

- name: set var

set_fact:
is_exist: False

- name: check file exist
stat:
path: /var/lib/pgsql92/data/PG_VERSION
register: pg_stat
- name: update exist flag
set_fact:
is_exist: True
when: pg_stat.stat.exists



項目の無視(omit)

パラメータが未定義の時にモジュールの項目を無視する


omit.yml


- name: touch files with an optional mode
file:
dest: {{item.path}}
state: touch
mode: {{ item.mode | default(omit) }}


パスワードファイル(変数)の暗号化

Ansibleでパスワードを暗号化して管理する


Role

Playを機能単位に分割し、再利用しやすくするための仕組み


Roleの生成

以下のコマンドnginx roleが生成される

※コマンドによる生成でなく、手動で作成しても問題ない

> ansible-galaxy init --init-path="roles" nginx

このようなディレクトリとファイルが生成される

playbook

|
|----xxxx.yml
|----roles
|----nginx
|----defaults(Role内で使う変数のデフォルト値定義)
|----files(対象ホストにコピーするファイル配置用)
|----handlers(ハンドラーとして使用するタスクの定義)
|----meta(Roleの依存関係の定義)
|----tasks(Roleで実行するタスク)
| |----main.yml(メインのtaskファイル)
|----templates(対象ホストに展開するJinja2テンプレート)
|----tests(Roleをテストするための定義)
| |----xxx.yml(テスト用のplaybook)
| |----inventory(テスト用のinventory)
|----vars(Role内で使う変数)

roleをグローバルに使いたい場合は


  • /etc/ansible/rolesに配置する

  • 設定ファイルの[defaults]ブロックにroles_pathで指定

  • 環境変数ANSIBLE_ROLES_PATHで指定

※Role間での変数の共有は変数の優先順位


playbookをroleに分割するイメージ


ロール内のtasksの分割

roles/hogehoge/tasks/main.ymlにすべてのタスクを詰め込めずに再利用性と可読性を高めるためにimport_tasks or include_tasksを用いて分割することができる(importは静的、includeは動的)


main.yml

---

- import_tasks: install.yml
- import_tasks: after_install.yml



install.yml

---

- yum: name=vi state=present


after_install.yml

---

- debug: var=after

※Role内のinclude_tasks先でfilesやtemplatesからファイル展開しても問題なく相対パスを参照してくれる!


tasksとrolesの実行順序

playbook内でtasksとrolesが混在している場合は、基本的にrolesの後にtasksが実行される

事前に実行したい場合は、 pre_tasksを用いること

tasks内で include_role を用いることで、taskの途中から他のroleを実行できる(Ansible v2.4~)

http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html#using-roles


条件に応じてroleを実行


main.yml


---
- hosts: all
roles:
- { role: some_role, when: "ansible_os_family == 'RedHat'" }

or

- hosts: all
tasks:
- include_role:
name: some_role
when: "ansible_os_family == 'RedHat'"


http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html#using-roles


Moduleの自作

libraryフォルダをplaybookと同じディレクティブに配置する

ゼロから始める自作モジュールが丁寧に解説されています(クラスメソッド社ブログ)

- Ansibleのモジュール開発(基礎編)

- Ansibleのモジュール開発(Python実装編)

- Ansibleのモジュール開発(テスト編)


簡単にAWS向け自作モジュールを作る


フィルタ、Jinja2


list-dictから新たなdictを作成する

以下のsubnet_result変数のitem.nameをキーにしたdictを作成する


dict.yml


# 元データ(subnet_result)
subnet_result =
{
"results": [
{
"item": {
"name": "xxx-subnet1"
"cidr": "10.0.0.0/21"
},
"subnet": {
"id": "subnet-000001"
}
},{
"item": {
"name": "xxx-subnet2"
"cidr": "10.0.8.0/21"
},
"subnet": {
"id": "subnet-000002"
}
},{
"item": {
"name": "xxx-subnet3"
"cidr": "10.0.16.0/21"
},
"subnet": {
"id": "subnet-000003"
}
}
]
}

- set_fact:
subnet_name_id: "{{ subnet_name_id | default({}) | combine( {item.item.name: item.subnet.id} ) }}"
with_items: "{{ subnet_result.results }}"

- debug: msg=subnet_name_id

# 結果*********************
"msg": {
"xxx-subnet1": "subnet-000001",
"xxx-subnet2": "subnet-000002",
"xxx-subnet3": "subnet-000003"
}



参照リンク集


AWX


構築手順

参考:Ansible AWX(Docker版)の構築と使い方


便利なツール



  • awx-manage・・・AWX(Ansible Tower)内の機能実行、設定情報の取得などができる


Ansible Container


Dockerとのすみわけ


  • Ansibleは周辺環境の構成管理が可能?


環境作成


windows10

>  pip install ansible-container[docker,k8s,pypiwin32]


AWS関連


Route53 ホストゾーンID

各種リソースの連携(Route53とELB連携)で連携先のRoute53のホストゾーンIDを設定しなければならない時に役に立つのRoute53のホストゾーンIDの一覧になります(リージョンごとに固定値)

https://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html


S3のELBアカウントID一覧

ELBのログ出力する際にポリシーに含める必要があるELBアカウントIDの一覧(リージョンごとに固定値)

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/enable-access-logs.html