Welcome!
Ansible 101
Ansible 基礎「独習」コースへようこそ
本コースは Ansible の基礎をすばやく学習するためのコースです。以下のポイントを解説しています。
- Ansible の基本設定ファイル(ansible.cfg) 、インベントリー、認証情報
- Ad-Hoc コマンドを使ったモジュールの実行
- 基礎的な Playbook の作成
本教材の作成にあたり以下のコンテンツを参考にしています。
ミスや修正箇所を見つけた方は以下へご連絡ください。
免責事項
- 本教材は作成者 @irix_jp およびコントリビューターが個人で知りうる範囲で作成しており、その正確性と完全性を保証するものではありません。
- 本教材から得られた情報により、何らかの損害を負った場合であっても、作成者並びにコントリビューターは一切の責任を負いません。予めご了承ください。
Congratulations!
You've completed the scenario!
Scenario Rating
ミスや修正箇所を見つけた方は以下へご連絡ください。
Your environment is currently being packaged as a Docker container and the download will begin shortly. To run the image locally, once Docker has been installed, use the commands
cat scrapbook_irixjp_ansible-101_container.tar | docker load
docker run -it /irixjp_ansible-101:
Oops!! Sorry, it looks like this scenario doesn't currently support downloads. We'll fix that shortly.

Steps
Ansible 101
Step 1
演習環境の準備を行います。
準備作業
以下のコマンドを実行して演習環境を準備します。この操作は1-2分程度で終わります。
yum install -y git && git clone https://github.com/irixjp/katacoda-scenarios && cd katacoda-scenarios/master-course-data/assets/tools/
bash ./kata_setup.sh
環境の概要
この演習では以下のような環境を利用します。node-1
, node-2
, node-3
という3台のサーバーが起動しており、ここに対して Ansible を使って様々な操作を行っていきます。
補足事項
ターミナルの上部に node-1
, node-2
というタブがあります。ここをクリックすると、各サーバーのポート80へ接続されます。今は各ノードで何も起動していないのでクリックしても何も起こりませんが、演習の中でこのタブを使用します。
Note: 実際にこのポートはコンテナにアクセスしており、 8081 -> node-1:80, 8082 -> node-2:80 という形のアクセスになっています。
演習のステップで「ブラウザでノードにアクセスしてください」という指示があった場合には、このタブをクリックしてください。
Step 2
Ansible の基礎、インベントリー、認証情報
Ansible の基本となるインベントリー(inventory)と認証情報(credential)について学習します。これは Ansible を動かす上で最低限準備する3つの情報のうちの2つに該当します。
演習環境での Ansible の実行
まず以下のコマンドを実行してください。これは Ansible を使って3台の演習ノードのディスク使用量を確認しています。
cd ~/
ansible all -m shell -a 'df -h'
node-1 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 10G 885M 9.2G 9% /
devtmpfs 473M 0 473M 0% /dev
tmpfs 495M 0 495M 0% /dev/shm
tmpfs 495M 13M 482M 3% /run
tmpfs 495M 0 495M 0% /sys/fs/cgroup
tmpfs 99M 0 99M 0% /run/user/1000
node-2 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 10G 885M 9.2G 9% /
devtmpfs 473M 0 473M 0% /dev
tmpfs 495M 0 495M 0% /dev/shm
tmpfs 495M 13M 482M 3% /run
tmpfs 495M 0 495M 0% /sys/fs/cgroup
tmpfs 99M 0 99M 0% /run/user/1000
node-3 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 10G 885M 9.2G 9% /
devtmpfs 473M 0 473M 0% /dev
tmpfs 495M 0 495M 0% /dev/shm
tmpfs 495M 13M 482M 3% /run
tmpfs 495M 0 495M 0% /sys/fs/cgroup
tmpfs 99M 0 99M 0% /run/user/1000
Note: 実際の出力内容と上記の出力例の差分は無視してください。重要なのは
df -h
が実行されたということです。
これで3台のノードからディスク使用量の情報が取得できました。しかし、この3台のノードはどのように決定されたのでしょうか。もちろんこれは演習用に予め設定されているものですが、その情報は Ansible のどこに設定されているか疑問を持つ方もいるはずです。今からその設定について確認していきます。
ansible.cfg
まず以下のコマンドを実行します。
ansible --version
ansible 2.9.0
config file = /jupyter/.ansible.cfg
configured module search path = ['/jupyter/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
executable location = /usr/local/bin/ansible
python version = 3.6.8 (default, Oct 7 2019, 17:58:22) [GCC 8.2.1 20180905 (Red Hat 8.2.1-3)]
Note: 出力内容は環境によって異なる場合があります。
ansible コマンドに --version
オプションをつけると、実行環境に関する基本的な情報が出力されます。バージョンや利用している Python のバージョンなどです。ここでは以下の行に注目します。
config file = /jupyter/.ansible.cfg
これは、このディレクトリで ansible コマンドを実行した際に読み込まれる Ansible の設定ファイルのパスを表示しています。このファイルは Ansible の基本的な挙動を制御するための設定ファイルです。
「このディレクトリで実行したとき」という表現をつけましたが、 Ansible は ansible.cfg を検索する順番が決まっています。詳細は Ansible Configuration Settings に記載されております。
簡単に説明すると、ansible.cfg は環境変数で与えられたパス、現在のディレクトリ、ホームディレクトリ、OS全体の共通パスという順序で検索され、今回はホームディレクトリ ~/.ansible.cfg
が最初に見つかるため、このファイルが利用されています。
この中身を確認してみましょう。
cat ~/.ansible.cfg
[defaults]
inventory = inventory
host_key_checking = False
force_color = True
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
いくつかの設定が演習用に設定されています。ここで重要となるのが以下の設定です。
inventory = inventory
これは、Ansible が自動化の実行対象を決定する「インベントリー」に関する設定です。
次のこの設定について詳しくみていきましょう。
インベントリー
インベントリーは Ansible が自動化の実行対象を決定するための機能です。ファイルの中身を確認してみましょう。設定ファイルでは inventory = inventory
となっており、ややわかりにくいですが、これは ansible.cfg からの相対パスで inventory
というファイルが指定されていることを意味しています。
このファイルの内容を確認してみます。
cat ~/inventory
[web]
node-1 ansible_host=3.114.16.114
node-2 ansible_host=3.114.209.178
node-3 ansible_host=52.195.15.8
[all:vars]
ansible_user=centos
ansible_ssh_private_key_file=/jupyter/aitac-automation-keypair.pem
このインベントリーは ini
ファイル形式で記述されています。他にも YAML
形式や、スクリプトで動的にインベントリーを構成する ダイナミックインベントリー
という仕組みもサポートされています。詳細は How to build your inventory を確認してください。
このインベントリーファイルは以下のルールで記述されています。
node-1
node-2
のように1行1ノードで情報を記述します。- ノード行は
ノードの識別子(node-1)
、ノードに与えるホスト変数(複数可) (ansible_host=xxxx)
から構成されます。 node-1
の部分にはIPアドレスやFQDNを指定することも可能です。
- ノード行は
[web]
でホストのグループを作ることができます。ここではweb
というグループが作られます。- グループ名は
all
とlocalhost
以外の名前を自由に使用できます。- 例)
[web]
[ap]
[db]
などシステムをグループ分けする目的で使用されます。
- 例)
- グループ名は
[all:vars]
では、all
というグループに対してグループ変数
を定義しています。all
は特別なグループで、インベントリーに記述された全ノードを指し示すグループです。- ここで与えられている、
ansible_user
ansible_ssh_private_key_file
は特別な変数で、各ノードへのログインに使われるユーザー名とSSH秘密鍵のパスを示しています。ansible_xxxx
というマジック変数で、Ansible の挙動を制御したり、Ansible が自動的に取得する環境情報など特別な値が格納されています。詳細は変数の項目で解説します。
実際にこのインベントリーを利用して定義されたノードの対して Ansible を実行してみます。以下のコマンドを実行してください。
ansible web -i ~/inventory -m ping -o
node-3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
node-1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
node-2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
このコマンドのオプションの意味は以下になります。
web
: インベントリー内のグループを指定しています。-i ~/inventory
: 利用するインベントリーファイルを指定します。-m ping
: モジュールping
を実行します。モジュールに関しての詳細は後述します。-o
: 出力を1ノード1行にまとめます。
今回の環境では、 ansible.cfg
ファイルによって、デフォルトのインベントリーが指定されているため、以下のように -i ~/inventory
を省略することが可能です。
ansible web -m ping -o
node-3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
node-1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
node-2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
Note: 以降の演習では、上記のようにインベントリーの指定は省略します。
以下のように、グループ名の代わりにノード名を指定することも可能です。
ansible node-1 -m ping -o
node-1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
複数のノードを指定することも可能です。
ansible node-1,node-3 -m ping -o
node-1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
node-3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
特別なグループである all
を指定してみます。all
はインベントリーに含まれる全てのノードを対象とします。今回のインベントリーは all
と web
のグループが同じものを指しているため、結果も同じになります。
ansible all -m ping -o
node-1 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
node-2 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
node-3 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"},"changed": false,"ping": "pong"}
認証情報
上記のインベントリーの確認では、3台のノードに対して ping
モジュールを実行しました。このモジュールは実際にノードに対してログインを行い、Ansible が実行可能な状態かを調べています。このときにログインに使われる Credential (認証情報) について見ていきます。
今回の演習環境では、先に見たインベントリーの中で認証情報が指定されています。以下が抜粋となります。
[all:vars]
ansible_user=centos
ansible_ssh_private_key_file=/jupyter/aitac-automation-keypair.pem
ここでは、全てのグループに対する変数として [all:vars]
を定義し、そこで認証に利用する変数を定義しています。
ansible_user
: Ansible がログインに利用するユーザー名を指定する。ansible_ssh_private_key_file
: Ansible がログインに利用する秘密鍵を指定する。
今回の演習では秘密鍵を用いていますが、ログインにパスワードを指定することも可能です。
ansible_password
: Ansible がログインに使用するパスワードを指定する。
この他にも認証情報を与える方法がいくつか提供されいます。代表的なものとしてコマンドラインのオプションとして与える方法があります。
ansible all -u centos --private-key ~/aitac-automation-keypair.pem -m ping
-u centos
: ログインに使用するユーザー名を指定できます。--private-key
: ログインに使用する秘密鍵を指定できます。
パスワードを使用する方法もあります。以下がサンプルになります。
$ ansible all -u centos -k -m ping
SSH password: ← ここでパスワード入力を求められる
node-1 | FAILED! => {
"msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}
node-2 | FAILED! => {
"msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}
node-3 | FAILED! => {
"msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}
Note: 演習環境はパスワードログインが許可されていないため、実際に実行してもこの手順は失敗します。
-k
: コマンド実行時に、パスワード入力のプロンプトを出す。
Ansible に認証情報を渡す仕組みは、他にもいくつかの方法があります。本演習では最もベーシックな手段(変数で直接指定)を用いていますが、実際に本番で利用する際には、認証情報をどう扱うかは事前に熟慮が必要です。
一般的には、Ansible Tower や AWX 等の自動化プラットフォームソフトウェアと組み合わせ使う方法がよく採用されます。
Step 3
Ad-Hocコマンドとモジュール
ここでは Ansible における重要な要素である Module
と、モジュールを実行するための Ad-hoc コマンド
について学習します。
モジュールとは
モジュールとは「インフラ作業でよくある操作を部品化」したものです。Ansible は約3000個のモジュールを標準で内蔵しています。正確な表現ではないですが、「インフラ作業におけるライブラリ集」だと言うこともできるかもしれません。
モジュールが提供されることで、自動化の記述をシンプルにすることができます。1つの例として、Ansible で提供される yum
モジュールについて見てみます。
この yum
モジュールはOSに対してパッケージの管理を行うモジュールです。このモジュールにパラメーターを渡すことでパッケージのインストールや削除を行うことができます。ここで、同じ動作をするシェルスクリプトを記述することを考えてみましょう。
最も単純に実装すると以下のようになります。
function yum_install () {
PKG=$1
yum install -y ${PKG}
}
Note: スクリプト内容は動作を説明するためのもので正確ではありません
実際に利用するにはこのスクリプトでは不十分です。例えば、インストールしようとしたパッケージがすでにインストールされている場合を考慮しなければなりません。すると以下のようになるはずです。
function yum_install () {
PKG=$1
if [ Does this package already exist? ]; then
exit 0
else
yum install -y ${PKG}
fi
}
Note: スクリプト内容は動作を説明するためのもので正確ではありません
しかし、まだこれでも不十分です。パッケージが既にインストールされているとして、そのパッケージのバージョンが、いまからインストールするものよりも古い、同じ、新しいといったケースではどうすればよいでしょうか?このケースも考慮するようにスクリプトを拡張しましょう。
function yum_install () {
PKG=$1
VERSION=$2
if [ Does this package already exist? ]; then
case ${VERSION} in
lower ) yum install -y ${PKG} ;;
same ) exit 0
higher ) exit 0
else
yum install -y ${PKG}
fi
}
Note: スクリプト内容は動作を説明するためのもので正確ではありません
このように、パッケージをインストールという単純な動作でも、ゼロから実装しようとすると様々な考慮事項が発生し、それに対応するための実装が必要となっていきます。
そこで Ansible のモジュールには、あらかじめこのような考慮事項が組み込まれており、ユーザーは細かな制御を実装することなく自動化を利用可能になります。つまり、自動化の記述量を大幅に減らすことができます。
モジュールの一覧
Ansible が持つモジュールの一覧は以下の公式ドキュメント から確認できます。
別の方法として Ansible がインストールされた環境では、ansible-doc
というコマンドでも参照することができます。
インストール済みのモジュールの一覧を表示するには以下のコマンドを実行します。
ansible-doc -l
Note: space で進む、b で戻る、q で終了。
特定のモジュールのドキュメントを参照するには以下のように実行します。
ansible-doc yum
> YUM (/usr/local/lib/python3.6/site-packages/ansible/modules/packaging/os/yum.py)
Installs, upgrade, downgrades, removes, and lists packages and
groups with the `yum' package manager. This module only works
on Python 2. If you require Python 3 support see the [dnf]
module.
* This module is maintained by The Ansible Core Team
* note: This module has a corresponding action plugin.
モジュールのドキュメントでは、モジュールに与えられるパラメーターの説明や、モジュールが実行された後の戻り値、そして実際の利用方法のサンプルが参照できます。
Note: モジュールの利用方法のサンプルは非常に参考になります。
Ad-hoc コマンド
先に紹介したモジュールを1つ呼び出して Ansible に小さな仕事を実行させることができます。この方法を Ad-hoc コマンド
とよびます。
コマンドの形式は以下になります。
$ ansible all -m <module_name> -a '<parameters>'
-m <module_name>
: モジュール名を指定します。-a <parameters>
: モジュールにわたすパラメーターを指定します。省略可能な場合もあります。
Ad-hoc コマンドを利用して、いくつかのモジュールを実際に動作させてみましょう。
ping
ping
モジュールを実行してみましょう。これは Ansible が操作対象のノードに対して「Ansible としての疎通」が可能かどうかを判定するモジュールです。パラメーターは省略可能です。
ansible all -m ping
node-1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
node-2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
node-3 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
shell
次に、shell
モジュールを呼び出してみましょう。これは対象のノード上で任意のコマンドを実行し、その結果を回収するコマンドです。
ansible all -m shell -a 'hostname'
node-1 | CHANGED | rc=0 >>
ip-10-0-0-92.ap-northeast-1.compute.internal
node-3 | CHANGED | rc=0 >>
ip-10-0-0-204.ap-northeast-1.compute.internal
node-2 | CHANGED | rc=0 >>
ip-10-0-0-218.ap-northeast-1.compute.internal
他にもいくつかのコマンドを実行して結果を確かめてください。
ansible all -m shell -a 'uname -a'
ansible all -m shell -a 'date'
ansible all -m shell -a 'df -h'
ansible all -m shell -a 'rpm -qa |grep bash'
yum
yum
はパッケージの操作を行うモジュールです。このモジュールを利用して新しくパッケージをインストールしてみます。
今回は screen パッケージをインストールします。まず現在の環境に screen がインストールされていないことを確認します。
ansible all -m shell -a 'which screen'
このコマンドは screen が存在しないためエラーになるはずです。
では、 yum モジュールで screen のインストールを行います。
ansible all -b -m yum -a 'name=screen state=latest'
-b
: become オプション。これは接続先のノードでの操作に root 権限を利用するためのオプションです。パッケージのインストールには root 権限が必要となるため、このオプションをつけています。つけない場合、このコマンドは失敗します。
再度、screen コマンドの確認を行うと、今度はパッケージがインストールされたため成功するはずです。
ansible all -m shell -a 'which screen'
setup
setup
は対象ノードの情報を取得するモジュールです。取得された情報は ansible_xxx
という変数名で自動的にアクセス可能となります。
出力される情報量が多いため、1台のノードのみに実行します。
ansible node-1 -m setup
このように Ansible は様々なモジュールを持ち、これらを使ってノードに対して操作を行ったり、情報収集を行うことが可能です。
Step 4
Playbookの記述と実行
先の演習ではモジュールを1つずつ実行しましたが、実際に作業を行う場合はいくつのも手順を連続して実行することになります。このときに使用するのが playbook
です。playbook には呼出したいモジュールとパラメーターを順番に記述し、一連の手順として連続して実行することができます。
Playbook の基礎
playbook
は YAML 形式で記述します。YAMLに関して重要なポイントを以下に記載します。
- YAML はデータを表記するためのテキストフォーマットであること。
- ファイルの先頭は
---
から始まる - インデントが意味を持つ
- インデントは
space
で表記する。tab
ではエラーとなります。
- インデントは
-
はリストを表すkey
:value
で辞書形式となる- json と相互に変換可能
以下は playbook のサンプルです。
---
- hosts: all
become: yes
tasks:
- name: first task
yum:
name: httpd
state: latest
- name: second task
service:
name: httpd
state: started
enabled: yes
この内容は、json で表記すると以下のようになります。
[
{
"hosts": "all",
"become": "yes",
"tasks": [
{
"name": "first task",
"yum": {
"name": "httpd",
"state": "latest"
}
},
{
"name": "second task",
"service": {
"name": "httpd",
"state": "started",
"enabled": "yes"
}
}
]
}
]
playbook の作成
では実際に playbook を作成します。
~/working/first_playbook.yml
をエディタで開いてください。このファイルには先頭に ---
のみが記載されています。以下の説明に従いこのファイルへ追記を行い、playbook として完成させます。
ここでは、WEBサーバーを構築する playbook を作成します。
play パート
以下のようにファイルに追記してください。
---
- name: deploy httpd server
hosts: all
become: yes
ここで記述した内容な以下になります。
name:
: ここには、この playbook で行う処理の概要を記載します。省略可能。日本語を使うことも可能です。hosts: all
: playbook の実行対象となるグループやノードを指定します。become: yes
: この playbook では権限昇格を行うことを宣言しています。コマンドラインで与える-b
と同じ意味です。
この部分は、playbook 内の play
パートと呼ばれる部分で全体に関する挙動を宣言します。playパートで指定できる項目の詳細については公式ドキュメント を確認してください。
task パート
次に以下を追記します。インデントの階層に注意してください。
---
- name: deploy httpd server
hosts: all
become: yes
tasks:
- name: install httpd
yum:
name: httpd
state: latest
- name: start & enabled httpd
service:
name: httpd
state: started
enabled: yes
ここで追記した内容は task
パートと呼ばれる部分になり、実際にこの playbook が行う処理を記述していきます。task パートではモジュールを呼び出す順番に列挙し、必要なパラメーターを与えます。
tasks:
これ以降が task パートであることを宣言しています。- name: ...
このタスクの説明を記載しています。省略可能yum:
service:
呼び出すモジュールを指定しています。- 以下はモジュールに与えられているパラメーターです。
name: httpd
state: latest
name: httpd
state: started
enabled: yes
ここで呼び出しているモジュールは以下になります。
作成した playbook に構文エラーがないかを以下のコマンドで確認できます。
cd ~/working
ansible-playbook first_playbook.yml --syntax-check
playbook: first_playbook.yml
上記はエラー無しのケースです。もしインデントなどに誤りがある場合は以下のようになります。
$ ansible-playbook first_playbook.yml --syntax-check
ERROR! Syntax Error while loading YAML.
expected <block end>, but found '<block sequence start>'
The error appears to be in '/notebooks/working/first_playbook.yml': line 6, column 2, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
tasks:
- name: install httpd
^ here
この場合には、playbook のインデントなどがサンプルと同じになっているかを再度確認してください。
playbook の実行
作成した playbook を実行します。playbook の実行には ansible-playbook
コマンドを利用します。成功すれば httpd サーバーが起動して apache の初期画面が参照できるはずです。
ansible-playbook first_playbook.yml
PLAY [deploy httpd server] **************************************************
TASK [Gathering Facts] ******************************************************
ok: [node-2]
ok: [node-3]
ok: [node-1]
TASK [install httpd] ********************************************************
changed: [node-1]
changed: [node-2]
changed: [node-3]
TASK [start & enabled httpd] ************************************************
changed: [node-1]
changed: [node-2]
changed: [node-3]
PLAY RECAP ******************************************************************
node-1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-2 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-3 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
上記のような出力となれば成功です。node-1,2,3 に対してブラウザでアクセスしてサイトの動作を確認してください。
Note: katacoda 上で演習をしている場合は、画面上部の
node-1,2,3
をクリックします。Note: Jupyter 上で演習をしている場合は、アクセスするIPアドレスを
~/inventory
で確認し、ブラウザでアクセスしてください。
以下のような画面が表示されれば成功です。
タスクの追加
作成した plyabook にサイトのトップページを配布するタスクを追加します。
~/working/files/index.html
をエディタで開きます。
ファイルを以下のように編集します。
<body>
<h1>Apache is running fine</h1>
</body>
さらに first_playbook.yml
を以下のように編集します。
---
- name: deploy httpd server
hosts: all
become: yes
tasks:
- name: install httpd
yum:
name: httpd
state: latest
- name: start & enabled httpd
service:
name: httpd
state: started
enabled: yes
- name: copy index.html
copy:
src: files/index.html
dest: /var/www/html/
編集が完了したら、構文チェックを実施した後に playbook を実行してみましょう。
ansible-playbook first_playbook.yml --syntax-check
ansible-playbook first_playbook.yml
PLAY [deploy httpd server] **************************************************
TASK [Gathering Facts] ******************************************************
ok: [node-2]
ok: [node-3]
ok: [node-1]
TASK [install httpd] ********************************************************
ok: [node-1]
ok: [node-3]
ok: [node-2]
TASK [start & enabled httpd] ************************************************
ok: [node-2]
ok: [node-1]
ok: [node-3]
TASK [copy index.html] ******************************************************
changed: [node-1]
changed: [node-3]
changed: [node-2]
PLAY RECAP ******************************************************************
node-1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-2 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-3 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
正常終了したら再びブラウザで3台のノードへアクセスしてください。正しく playbook が記述され、動作したのならば先程作成した index.html
の内容が表示されるはずです。
冪等性(べきとうせい)
Ansible のモジュールを利用するメリットして、記述量を大幅に減らせるという解説を行いましたが、その他にもメリットがあります。それが 冪等性
です。
この演習では ansible-playbook first_playbook.yml
を2回実行しています。httpd のインストールと起動を行った時、そしてサイトのトップページを追加したときです。つまり、httpd のインストールと起動のタスクは2回実行されています。しかし、2回目の playbook 実行にもエラー等は起きていません。これは Ansible の 冪等性
が機能しているからです。
1回目の実行結果と2回目の実行結果を注意深く確認すると、出力結果に違いがあることに気づくはずです。異なる箇所は、それぞれの処理において changed
と出力されたか、ok
と出力されたかです。
changed
: Ansibleが処理を実行した結果、対象ホストの状態が変わった(Ansibleが実際に設定を行った)ok
: Ansibleは処理を行おうとしたが、既に想定した設定になっているので状態が変わらなかった(Ansibleは設定を行わなかった・行う必要がなかった)
この動作が Ansible が備える冪等性になります。Ansible は今から行おうとする処理を実行する必要があるのか、無いのかを実行前に判断してくれています。
ここで再度この playbook を実行してみましょう。3つのタスクの状態がどのようになるか考えてから実行してください。
ansible-playbook first_playbook.yml
PLAY [deploy httpd server] **************************************************
TASK [Gathering Facts] ******************************************************
ok: [node-3]
ok: [node-1]
ok: [node-2]
TASK [install httpd] ********************************************************
ok: [node-2]
ok: [node-1]
ok: [node-3]
TASK [start & enabled httpd] ************************************************
ok: [node-1]
ok: [node-2]
ok: [node-3]
TASK [copy index.html] ******************************************************
ok: [node-3]
ok: [node-1]
ok: [node-2]
PLAY RECAP ******************************************************************
node-1 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-2 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-3 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
全てのタスクは ok
となったはずです。playbook 実行時の最後の PLAY RECAP
部分を並べてみると結果の差が分かりやすく確認できます。ここでは各ノードにおいて、何個のタスクが changed
になったのかを確認できます。
1回目(タスク2個がchanged)
PLAY RECAP ******************************************************************
node-1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-2 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-3 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2回目(タスク1個がchanged)
PLAY RECAP ******************************************************************
node-1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-2 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-3 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3回目(changedは0)
PLAY RECAP ******************************************************************
node-1 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-2 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node-3 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
では、この冪等性は何が嬉しいのかというと、
- Playbookには「処理の手順ではなく、状態を宣言的に記述できる」→ Playbook=設定パラメータ+手順書として扱えるようになる。
- 仮に複数台のホストに対して実行した処理が途中で失敗しても、最初から流し直せる(設定が成功した部分はスキップされるため)
Ansible の各モジュールはこの冪等性を考慮するように作られており、このモジュールを利用することで簡単に、そして安全に自動化を記述することができるようになっています。
これがスクリプトの場合、特に再実行においてはスクリプトを頭から流し直していいのか?ダメなのか?等の面倒な考慮点が生まれてしまうことは容易に想像できると思います。
Note: ただし、Ansibleも全てのモジュールが完全な冪等性を保証しているわけではありません。モジュールの中には shell のように何が実行されるかわからないものや、操作対象(NW系機器やクラウド環境)によっては原理的に冪等性の確保が難しいものも存在しています。こういったモジュールを使う場合は利用者が注意を払う必要があります