Ansibleをはじめる人に。

  • 521
    いいね
  • 0
    コメント

この記事で書いていること

この記事では Ansibleをはじめる人 を対象に、下記の Ansible 入門的な内容についてまとめています。

  1. Ansibleとは
  2. Ansibleを調べる
  3. Ansibleを試す
  4. Ansibleをもう少し試す

既にわかっている人向けに用語を使えば、

jinja templateによるファイルの動的配置などとroleによるタスクの切り出しまでをハンズオン

となります。
なお、環境は Python 2.6.6 + Ansible 1.9.4 です。

Ansibleとは

Ansibleは "構成管理ツール" と呼ばれ、最近(少なくとも私の中で)話題のツールです。
Infrastructure as a Code なんていう文脈の中では Chef, Puppet などと並んで必ず出てくるツールの1つかと思います。

このあたりの話は話しだすとそれだけで記事が書けてしまうので、既に公開されているありがたい資料へのリンクを貼らせていただきます。

資料にも述ありますが、構成管理ツールAnsible、という点に限って特徴を述べると

  • エージェントレスな構成管理ツール
  • サーバはPython 2.6+、ホストはPython 2.4+があれば動作可能
  • 設定ファイルが少なく、記述がシンプル
  • シンプルさ故に複雑構成は苦手

というような点が挙げられるかと思います。

Ansibleの由来

個人的に製品名とか略称の元ネタが気になってしまうタチなので、 Ansible も調べてみました。

どうやらこいつらしい。 SF小説に出てくる "超光速通信" の技術名

まるでアンシブルのように超光速でサーバ構築をびゅーん

みたいな認識でいいんでしょうか。

どう見てもIT界隈の人が読んでとっても面白いそうなのでポチッてみようかしら。

Ansibleを調べる

Ansibleのことを調べたい場合、ググる以外では大きく分けて以下の方法があるかと思います。

  • Ansible本を読む
  • 公式ドキュメントを読む
  • Ansible(のソースコード)を読む

Ansible本を読む

意外とAnsibleをメインに扱った日本語の本は多分まだこれしかありません1
とはいえ、この記事でまとめているような内容に加え、より発展的ところも書かれているので特に不自由はないかと思います。

この本では執筆時における最新版 Ansible 1.6.6 をベースに書かれているので、その点だけ注意が必要です。
ただ、近々リリースされる Ansible 2.0 でも互換は維持されるのであまり気にする必要もないかもしれません。

公式ドキュメントを読む

やはりどのようなソフトウェアであっても、公式ドキュメントが大事と思います。

残念ながら英語でしか書かれていませんが、 Sphinxで書かれたきれいな構成なんとなく読みやすいフォント で個人的にはとても読みやすいと思っています。

...すみません、章立てがよくできているからとても読みやすいのだと思います。

Ansibleを実際に使う中では Playbooks とか Module Index をよく読みました。

Ansible(のソースコード)を読む

半分冗談、半分マジみたいな話ではありますが、本や公式ドキュメントでダメならソースを読むのが理論上は最高です。
ただ、まだそこまで切羽詰まったことがないので真面目に読み解いたことはないです。
開発中のバージョンもここにあるのでAnsible 2.0を試す際に立ち入ったりしています。

Ansibleを試す

やはりツールを知る上では 百聞は一見に如かず ということでハンズオンが一番よいかと思います。
Ansibleは最小2ファイルからで動かせるのでハンズオンの敷居も非常に低いです。

さて、ハンズオン環境は Vagrant + VirtualBox で整えることにしましょう。

Vagrant + VirtualBoxの導入

それぞれ環境に合ったものをダウンロード、インストールしてください。
基本的にインストーラに沿って進めれば問題はないかと思います。

インストール後は下記のようにコマンドでバージョンが確認できればOKです。
(Windows風に書いていますが、MacやらLinuxの方は適宜読み替えてください)

C:\> vagrant -v
Vagrant 1.7.4

C:\> VBoxManage -v
5.0.12r104815

というわけでハンズオン環境は上のバージョンで作っていきます。

Ansibleハンズオン用VMの準備

適当なフォルダにVagrant関係のファイルを作ります。

C:\> mkdir vagrant
C:\> cd vagrant
C:\vagrant> vagrant init

vagrant initを実行すると、カレントディレクトリに vagrantfile が生成されます。
vagrantfileはvagrantから起動するVMの設定ファイルで、今回は下記のように書いて下さい。

vagrantfile
Vagrant.configure(2) do |config|
  config.vm.define "controller" do |node|
        node.vm.box = "centos6.7"
        node.vm.hostname = "controller"
        node.vm.network :private_network, ip: "192.168.100.10"
        node.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2210
  end
  config.vm.define "target" do |node|
        node.vm.box = "centos6.7"
        node.vm.hostname = "target"
        node.vm.network :private_network, ip: "192.168.100.20"
        node.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2220
  end
end

詳しい記述については割愛しますが、多分なんとなく内容は想像つくかと思います。
用途としてはcontroller側でAnsibleを実行し、targetがその対象になるという感じです。

さて、それぞれのVMは centos6.7 というBoxをベースにすると書いてありますが、これは準備する必要があります。
下記のコマンドを実行しましょう。

vagrant box add centos6.7 https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box

これはvagrantにおけるVMテンプレートである Box を手元に追加しています。
今回は特に意味もなくCentOS 6.7でやりますが、 Vagrantbox.es でお好みのものを選んでもよいかと思います。
その際には box addするときの名前vagrantfileに指定するbox名を揃える ようにして下さい。

それではBoxがダウンロードできたら、 vagrantfileのあるフォルダvagrant up コマンドを実行しましょう。

C:\vagrant>vagrant up
Bringing machine 'controller' up with 'virtualbox' provider...
Bringing machine 'target' up with 'virtualbox' provider...

(略)

念のためVMのステータスを確認します。 vagrant status コマンドでどうぞ。

C:\vagrant>vagrant status
Current machine states:

controller                running (virtualbox)
target                    running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.

Ansibleのインストール

それでは controllerにAnsibleを導入 しましょう。
controllerへのSSHはlocalhost:2210が転送されるようにvagrantfileで設定しています。

が、考えるのが面倒なのでteratermマクロを作りました。よかったら使ってやって下さい。
(当然接続するのは Putty でも MobaXterm でもなんでもよいです)

login.ttl
USERNAME = 'root' 
PASSWORD = 'vagrant' 

MESSAGE = 'Please choose a connection host.'#13#13 
strconcat MESSAGE ' 1. controller'#13 
strconcat MESSAGE ' 2. target'#13 
inputbox MESSAGE 'Input host number.'

str2int hostnum inputstr

if hostnum = 1 then 
    HOSTADDR = '127.0.0.1:2210' 
elseif hostnum = 2 then 
    HOSTADDR = '127.0.0.1:2220' 
else 
    messagebox 'Input number 1 or 2.' 'Input error' 
    end 
endif

COMMAND = HOSTADDR
strconcat COMMAND ' /ssh /2 /auth=password /user=' 
strconcat COMMAND USERNAME 
strconcat COMMAND ' /passwd=' 
strconcat COMMAND PASSWORD

connect COMMAND

end

AnsibleサーバのAnsible導入要件は python 2.6+ でしたので念のため確認します。

[root@controller ~]# python --version
Python 2.6.6

あとはyumを使ってAnsibleをインストールしましょう。

[root@controller ~]# yum install ansible

(略)

Installed:
  ansible.noarch 0:1.9.4-1.el6

Dependency Installed:
  PyYAML.x86_64 0:3.10-3.1.el6                          libyaml.x86_64 0:0.1.3-4.el6_6
  python-babel.noarch 0:0.9.4-5.1.el6                   python-crypto.x86_64 0:2.0.1-22.el6
  python-crypto2.6.x86_64 0:2.6.1-2.el6                 python-httplib2.noarch 0:0.7.7-1.el6
  python-jinja2.x86_64 0:2.2.1-2.el6_5                  python-keyczar.noarch 0:0.71c-1.el6
  python-paramiko.noarch 0:1.7.5-2.1.el6                python-pyasn1.noarch 0:0.0.12a-1.el6
  python-setuptools.noarch 0:0.6.10-3.el6               python-simplejson.x86_64 0:2.0.9-3.1.el6
  sshpass.x86_64 0:1.05-1.el6

Complete!

依存関係によって結構色々とインストールされます。

[root@controller ~]# ansible --version
ansible 1.9.4

というわけで Ansible 1.9.4 がインストールされました。

と、ここで ansible までタイプしてからTab補完をしてみてください。

[root@controller ~]# ansible
ansible           ansible-doc       ansible-galaxy    ansible-playbook  ansible-pull      ansible-vault

色々出てきますね。ただし今回のハンズオンで使うのは基本的に ansible-playbookだけ です。悪しからず。

Ansibleの実行テスト

Ansibleでは Playbook と呼ばれる単位で対象の構成手順を記述しますが、一旦ワンライナーで疎通確認をします。
Ansibleはエージェントレスな構成管理ツールでSSHを使います。
ですので、Ansibleサーバから対象にSSHでログインできるようにするため、公開鍵を送り込んでおきましょう。

[root@controller ~]# ssh-keygen -t rsa
(パスフレーズは入力しない)

[root@controller ~]# ssh-copy-id root@192.168.100.20
(接続確認が出るのでyes)

こうすることで、controllerから ssh root@192.168.100.20 と実行してもパスワードの入力を求められずに済むかと思います。
(ハンズオンのためとりあえずrootでAnsibleを動かしています。モラルについてはお察しです。ご注意ください。)

これでAnsibleの実行は目前です。対象ホストをまとめて記述したinventoryファイルを作りましょう。

[root@controller ~]# mkdir /ansible
[root@controller ~]# cd /ansible
[root@controller ansible]# mkdir inventory
[root@controller ansible]# vi inventory/hosts
[targets]
192.168.100.20

このinventoryファイルでは、targetsという グループ に192.168.100.20という ホスト を属させています。
今回のハンズオンではホスト1台しか扱わないのですが、実際に使う際にはグループ分けであれやこれや悩むことになるかと思います。

それでは長い道のりでしたが疎通確認しましょう。

[root@controller ansible] ansible all -i inventory/hosts -m ping
192.168.100.20 | success >> {
    "changed": false,
    "ping": "pong"
}

このコマンドでは先ほど記述したinventoryファイル中にある全て(all)のホストに対してpingモジュールで疎通確認をしています。
結果は [ホスト] | [結果] >> [メッセージ] というように表示されるかと思います。

Playbookを試してみる

さて、ここから Playbook を作りながら真面目にハンズオンを進めましょう。
以下のファイルを配置してみましょう。

group_vars/targets.yml
message: "Hello Ansible !"

fruits:
  apples:
    amount: 10
  bananas:
    amount: 20
  oranges:
    amount: 30

このファイルではグループ変数を記述しています。
拡張子からもわかるように YAML という形式で記述します。

YAML は YAML Ain't Markup Language の略で、IT業界ではたまに目にする 再帰定義 の1つですね。
他にはGNU(GNU is Not Unix), PHP(PHP: Hypertext Processor), RPM(RPM Package Manager)なんかが有名でしょうか。何言うてまんねん的な感じが割と好きです。
公式サイトを見ると分かるように非常にきっちりした定義があるのですが、Ansibleでは「 配列 」「 シーケンス 」とか限られた範囲がわかっていれば十分です。

YAML Syntax

test.yml
- hosts: targets
  user: root
  tasks:
    - name: output message.
      debug: msg="{{ message }}"

    - name: output fruits
      debug: msg="We want {{ item.value.amount }} {{ item.key }} !" 
      with_dict: {{ fruits }}

こちらがPlaybookと呼ばれるファイルです。こちらもYAMLで簡潔に記述します。
解説は後にしてまず実行してみましょう。

[root@controller ansible]# ansible-playbook -i inventory/hosts test.yml
PLAY [targets] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.100.20]

TASK: [output message.] *******************************************************
ok: [192.168.100.20] => {
    "msg": "Hello Ansible !"
}

TASK: [output fruits] *********************************************************
ok: [192.168.100.20] => (item={'key': 'apples', 'value': {'amount': 10}}) => {
    "item": {
        "key": "apples",
        "value": {
            "amount": 10
        }
    },
    "msg": "We want 10 apples !"
}
ok: [192.168.100.20] => (item={'key': 'oranges', 'value': {'amount': 30}}) => {
    "item": {
        "key": "oranges",
        "value": {
            "amount": 30
        }
    },
    "msg": "We want 30 oranges !"
}
ok: [192.168.100.20] => (item={'key': 'bananas', 'value': {'amount': 20}}) => {
    "item": {
        "key": "bananas",
        "value": {
            "amount": 20
        }
    },
    "msg": "We want 20 bananas !"
}

PLAY RECAP ********************************************************************
192.168.100.20             : ok=3    changed=0    unreachable=0    failed=0

というような感じに動きます。
実行したコマンドに注目していただきたいのですが、コマンドで与えたのは

  • inventoryファイル
  • Playbook

の2つです。
実行結果を見ると、group_varsでtargetsグループに定義した変数が反映されている ことがわかると思います。
このパターンではtest.ymlで実行対象としてhosts: targetsというグループを指定したため、group_varsから自動的にtargets.ymlが読み込まれています。

このように、Ansibleでは決まったディレクトリなどから情報を 暗黙的に取得 してくるため、そういった依存関係を理解しておく必要があります。
ただ、一度理解してしまえば簡潔な記述で多彩に使い回しができるようになるため、しっかりと理解することを強くおすすめします。

また、それぞれのtaskではdebugモジュールを使って、変数を出力しているだけですが、ディクショナリ形式で書いたfruitsの使い方に注目してみてください。

fruits:
  apples:
    amount: 10
  bananas:
    amount: 20
  oranges:
    amount: 30
- name: output fruits
  debug: msg="We want {{ item.value.amount }} {{ item.key }} !" 
  with_dict: fruits

まず、このタスクは fruits 変数の値を with_dict で取り出しながらdebugモジュールでメッセージを出力していきます。
ループ毎に取り出された要素は item という定義済み変数に格納され、処理中に使うことができます。
このとき fruits 直下にある果物名は item.key で取得でき、果物名の下に持たせている数量は item.value.amount で取得されます。

このあたりについては少し慣れないかもしれませんが、書いているうちになんとなく身についてきますのでdebugモジュールを使いながら地道に試してみるのがいいかと思います。

Ansibleをもう少し試す

ここまででinventoryファイルとPlaybook, group_varsなどで実際にAnsibleを動かしてみました。
実際にパッケージインストールやファイル作成などを行って、構成管理を実施してみましょう。

パッケージ

パッケージ操作は yumモジュール 等で行えます。下記のようなイメージです。

- name: install packages from yum
  yum: name={{ item }} state=latest
  with_items:
    - jq
    - ruby
    - httpd

これは最もシンプルな書き方で、指定パッケージの最新版(latest)をインストールします。オプションで使用するyumリポジトリの指定なども可能です。
また、上のコードでは「 jq, ruby, httpdがインストールされていること 」を示しますが、 state=absent とすれば インストールされていないこと を示すコードになります。

cron

cronモジュール を使ってジョブを設定することもできます。

- name: register cron job
  cron: name="check ping" day="*/2" hour="12" minute="0" job="ping -c 3 192.168.100.10"

個人的にはcron書く時に日時をどの順で書くか毎回調べてしまうので、これはわかりやすくてとてもよいです(無能)

ファイル・ディレクトリ

Ansibleではもちろんディレクトリの作成やファイルの配置を行うことができます。

ディレクトリ作成

ディレクトリ作成は fileモジュール で実施します。

- name: create directories
  file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
  with_items:
    - { "path":"/opt/ansible", "owner":"root", "group":"root", "mode":"755" }
    - { "path":"/opt/vagrant", "owner":"vagrant", "group":"vagrant", "mode":"755" }

ディレクトリ作成の場合は state=directory を指定しますが、他には link, file などもあります。
こちらもyumの時と同様に state=absent を指定すると path のものが削除されるように動きます。
ドキュメントにも記載がありますが、fileモジュールとして受け取る mode は8進数であることに注意してください。

remember that modes are actually octal numbers (like 0644). Leaving off the leading zero will likely have unexpected results.
(modeで指定する値が8進数であることを忘れないで下さい。もし先頭の0を忘れた場合、予期せぬ動きになります。)

また、 mode に関しては u+rwx など シンボルで指定する ことも Ansible 1.8以降 で可能になったようです。

静的ファイル配置

静的ファイル配置と言っていますが要するにファイルのコピーです。
copyモジュール はAnsibleサーバ側に保持しているファイルをホスト側に配置します。

- name: copy files
  copy: src=./files/hoge dest=/opt/ansible/hoge owner=root group=root mode=0755

動的ファイル配置

こちらは templateモジュール を用いて、ファイルのコピー時に変数を展開しながら配置します。

- name: copy template files
  template: src=./templates/fuga.j2 dest=/opt/ansible/fuga owner=root group=root mode=0755

コピー元として使用されるテンプレートファイルは jinja2 形式で記述する必要があります。

templates/fuga.j2
This is a jinja template file.

{{ message }}

jinja template can extract variables. like, ...

{% for key,value in fruits.iteritems() %}
We want {{ value.amount }} {{ key }} !
{% endfor %}

jinja2 も YAML 同様に決まり事は多いのですが、 これも全てを把握する必要はなく、

Template Designer Documentation

の内容を使えれば大体OKかと思います。
jinja template においても Playbook 内とほぼ大差ない形で変数を使うことができます。
with_dict のようにfor文でイテレーションも可能ですが、こちらは item を挟まずに使えます。

動かしてみよう

これまでの内容を全て詰め込んでみましょう。

main.yml
- hosts: targets
  user: root
  tasks:
  - name: install packages from yum
    yum: name={{ item }} state=latest
    with_items:
      - jq
      - ruby
      - httpd

  - name: register cron job
    cron: name="check ping" day="*/2" hour="12" minute="0" job="ping -c 3 192.168.100.10"

  - name: create directories
    file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
    with_items:
      - { "path":"/opt/ansible", "owner":"root", "group":"root", "mode":"755" }
      - { "path":"/opt/vagrant", "owner":"vagrant", "group":"vagrant", "mode":"755" }

  - name: copy files
    copy: src=./files/hoge dest=/opt/ansible/hoge owner=root group=root mode=0755

  - name: copy template files
    template: src=./templates/fuga.j2 dest=/opt/ansible/fuga owner=root group=root mode=0755

ここまでやると多分こんな感じになっているかと思います。

ansible/
├─inventory/
│ └─hosts
├─group_vars/
│ └─targets.yml
├─files/
│ └─hoge
├─templates/
│ └─fuga.j2
├─test.yml
└─main.yml

ただ、色々書いて不安なので一旦 Check Mode で動かしてみましょう。
Check Mode とは実際に変更を行わないモードで、 Playbook を実行した際にどの部分に変更が加わるのかを確認することができます。一般的には Dry run と呼ばれるものです。
オプションで --check を与えると Check Mode で動きます。

[root@controller ansible]# ansible-playbook --check -i inventory/hosts main.yml

PLAY [targets] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.100.20]

TASK: [install packages from yum] *********************************************
changed: [192.168.100.20] => (item=jq,ruby,httpd)

TASK: [register cron job] *****************************************************
skipping: [192.168.100.20]
ok: [192.168.100.20]

TASK: [create directories] ****************************************************
changed: [192.168.100.20] => (item={'owner': 'root', 'path': '/opt/ansible', 'group': 'root', 'mode': '755'})
changed: [192.168.100.20] => (item={'owner': 'vagrant', 'path': '/opt/vagrant', 'group': 'vagrant', 'mode': '755'})

TASK: [copy files] ************************************************************
changed: [192.168.100.20]

TASK: [copy template files] ***************************************************
changed: [192.168.100.20]

PLAY RECAP ********************************************************************
192.168.100.20             : ok=5    changed=4    unreachable=0    failed=0

さて、おそらくコマンド自体は動いたかと思いますが cron の部分が少し気になります。
skipping と表示されていますが、これはなんでしょうか。

もうすこし詳細に見るために -v オプションを使いましょう。

[root@controller ansible]# ansible-playbook --check -i inventory/hosts main.yml -v

(略)

TASK: [register cron job] *****************************************************
skipping: [192.168.100.20]
ok: [192.168.100.20] => {"changed": false, "msg": "remote module does not support check mode", "skipped": true}

(略)

ということでした。
Check Mode のドキュメントにもありますが、全てのモジュールが Check Mode に対応しているわけではないため、確認がスキップされた、ということのようです。

‘check mode’ (which contains most of the primary core modules, but it is not required that all modules do this)
(多くのコアモジュールでcheck modeがサポートされますが、全てのモジュールがそうというわけではありません。)

ちなみに -v オプションですが、vを重ねる毎にメッセージが詳細になり、-vvv ではSSHで具体的にどう動いているのかまで分かるようになります。
それでは Playbook を実行してみましょう。

[root@controller ansible]# ansible-playbook -i inventory/hosts main.yml

PLAY [targets] ****************************************************************

GATHERING FACTS ***************************************************************
ok: [192.168.100.20]

TASK: [install packages from yum] *********************************************
changed: [192.168.100.20] => (item=jq,ruby,httpd)

TASK: [register cron job] *****************************************************
changed: [192.168.100.20]

TASK: [create directories] ****************************************************
changed: [192.168.100.20] => (item={'owner': 'root', 'path': '/opt/ansible', 'group': 'root', 'mode': '755'})
changed: [192.168.100.20] => (item={'owner': 'vagrant', 'path': '/opt/vagrant', 'group': 'vagrant', 'mode': '755'})

TASK: [copy files] ************************************************************
changed: [192.168.100.20]

TASK: [copy template files] ***************************************************
changed: [192.168.100.20]

PLAY RECAP ********************************************************************
192.168.100.20             : ok=6    changed=5    unreachable=0    failed=0

無事終了したようです。
結果としては GATHERING FACTS を含めて6項目のタスクがあり、 問題なく終了(ok) したこと、またうち5項目で 変更が行われた(changed) ことを示しています。

jinja templateの中身チェック

念のため想定通りに jinja template が展開されたか見てみましょう。

[root@controller ansible]# ssh root@192.168.100.20 cat /opt/ansible/fuga
This is a jinja template file.

Hello Ansible !

jinja template can extract variables. like, ...

We want 10 apples !
We want 30 oranges !
We want 20 bananas !

想定通り、いいですね。

冪等性(べきとうせい)

ここでもう一度同じ Playbook を実行してみましょう。

[root@controller ansible]# ansible-playbook -i inventory/hosts main.yml

(略)

PLAY RECAP ********************************************************************
192.168.100.20             : ok=6    changed=0    unreachable=0    failed=0

当然1件も変更が行われませんでしたが、これは対象が Playbook で指定した あるべき状態 になっているということを示します。
また、既に対象があるべき状態にあったため、何の変更も行われなかった、 冪等性 が保たれたということになります。

この冪等性というものが構成管理ツールでは重要であるとされていて、例えば copy モジュールはファイルの 上書きすら行っていない のですが、これを単に cp コマンドでやっていたりすると上書きをしてしまうことでしょう。
もちろん、シェルスクリプトの作りこみで担保することは可能ですが、余計な記述が増えることになり、 色々と面倒 です。
その面倒な冪等性の担保をツールがやってくれるというのが構成管理ツールの旨味といえます。
(ただし、shellモジュールやcommandモジュールはAnsible側で冪等性を担保できないため、利用には注意が必要です)

roleの利用

ここまででタスクは1つの YAML ファイルにまとめて記述していますが、これでは似たホストを作る際に使い回しが利きません。
そこで、一部のタスクを role として切り出し、部品化をしてみましょう。

master.yml
- hosts: targets
  user: root
  tasks:
  - name: register cron job
    cron: name="check ping" day="*/2" hour="12" minute="0" job="ping -c 3 192.168.100.10"

  roles:
  - role-common
  - role-web
roles/role-common/tasks/main.yml
- name: install packages from yum
  yum: name={{ item }} state=latest
  with_items:
    - jq
    - ruby

- name: create directories
  file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
  with_items:
    - { "path":"/opt/ansible", "owner":"root", "group":"root", "mode":"755" }

- name: copy files
  copy: src=./files/hoge dest=/opt/ansible/hoge owner=root group=root mode=0755
roles/role-web/tasks/main.yml
- name: install packages from yum
  yum: name={{ item }} state=latest
  with_items:
    - httpd

- name: create directories
  file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
  with_items:
    - { "path":"/opt/vagrant", "owner":"vagrant", "group":"vagrant", "mode":"755" }

- name: copy template files
  template: src=./templates/fuga.j2 dest=/opt/ansible/fuga owner=root group=root mode=0755

単純に切り出しただけなので with_items の扱いが雑だとかは言わないで下さい(笑)

ansible/
├─inventory/
│ └─hosts
├─group_vars/
│ └─targets.yml
├─files/
│ └─hoge
├─templates/
│ └─fuga.j2
├─roles/
│ ├─role-common/
│ │ └─tasks/
│ │    └─main.yml
│ └─role-web/
│   └─tasks/
│      └─main.yml
├─test.yml
├─main.yml
└─master.yml

roles で呼び出した role は rolesディレクトリ配下の名前で探しだされ、tasks直下のmain.ymlが実行されます。
role では依存関係を持たせる dependencies なども使えますが今回は扱いません。

では実行しましょう。

[root@controller ansible]# ansible-playbook --check -i inventory/hosts master.yml

(略)

TASK: [role-common | install packages from yum] *******************************
ok: [192.168.100.20] => (item=jq,ruby)

(略)

PLAY RECAP ********************************************************************
192.168.100.20             : ok=7    changed=0    unreachable=0    failed=0

内容的には先ほどまでと変わっていないため changed は出ませんが、 role に含まれているタスクの表示が変わったかと思います。

おわりに

ハンズオンを終えたら、VMのシャットダウンを忘れずに。

C:\vagrant>vagrant halt

また最初から遊びたい人は無慈悲に vagrant destroy してもいいでしょう。
(元々 sahara プラグイン使ったらもっと楽チンにできるんですけどね、説明が面倒でね...)

この記事を書き始めたのは Ansible Advent Calendar 2015枠が空いてしまって気持ち悪かったから でした。
ちょうど自分の周りで今後Ansibleを触ってもらう人にどんな資料がいいかな~と思っていたところでもあったので、練習を兼ねて書いてみようと思った次第です。

記事を書き始めた当初は「 入門レベルだからそんなに分量ないだろ~ 」と思っていましたが、順を追って説明していくと思った以上に増えてしまいました。
ただ、今回説明した内容でAnsibleとして大体の部分は押さえられているかと思うので、あとは必要に応じて個別に調べていけるのかなと思います。

Ansibleが もっともっと盛り上がる 助けになれれば幸いです。


  1. (2016/12/24追記) Ansible関連情報をまとめた 記事を書きました。