Ansible の Playbook を使ってみる
構成管理ツール Ansible を使ってみる の続きです。
今回は Playbook について調べた事をまとめてみます。
Playbook は Ansible のモジュールを使用した処理の組合せにより対象サーバの構成を記述したものです。
例えば Apache を使用して Web サーバを構築する場合、単に yum で Apache をインストールして完了、という事はなく、その後 httpd.conf の内容を適切に設定したり、iptables でポートを開けたり、といった一連の作業がありますが、そういったサーバ構築のための作業をまとめて Playbook として定義しておく事で、「実行できる手順書」として残しておく事が出来ます。
Puppet で言うところのマニフェスト、Chef でのレシピに相当するものになります。
Ansible の Playbook はフォーマットが YAML なので、気軽に書き始める事ができます。
簡単な Playbook
まずは単純に yum から MySQL をインストールするだけの Playbook を作成してみます。
- mysql.yml
--- - hosts: all user: root tasks: - name: install mysql yum: name=mysql state=installed
作成した Playbook を実行するには、ansible コマンドの時と同様に対象ホストを記述したファイルを用意して ansible-playbook コマンドを実行します。
$ ansible-playbook mysql.yml -i hosts -k
これで yum モジュールを使用して対象ホストに mysql パッケージがインストールされます。
yaml ファイルの先頭付近、hosts には対象ホストのホスト名または対象ホストのグループ名を指定します。
user には ansible が SSH ログインする際のユーザ名を指定します。
tasks
Tasks list
http://docs.ansible.com/ansible/latest/playbooks_intro.html#tasks-list
実際に実行する処理は tasks 以下に定義していきます。
name にそのタスクの判り易い名前を指定し、その下に使用するモジュールとオプションを指定します。
先ほどの例では一つのパッケージのみインストールしているので ansible コマンドを直接実行するのとあまり変わりませんが、Playbook では以下のように tasks の下に複数の処理を定義することが出来ます。
--- - hosts: all user: root tasks: - name: install mysql yum: name=mysql state=installed - name: install mysql-server yum: name=mysql-server state=installed - name: install mysql-devel yum: name=mysql-devel state=installed
上記を実行すると対象サーバに mysql, mysql-server, mysql-devel の三つのパッケージがインストールされます。
ちなみに上記は with_items を使用して以下のように書く事が出来ます。
--- - hosts: all user: root tasks: - name: install mysql packages yum: name=$item state=installed with_items: - mysql - mysql-server - mysql-devel
with_items に記述した要素が一つずつ yum モジュールの name に指定した item 変数に渡され、インストールされます。
with_items による繰り返し実行は他のモジュールでも使用できるので覚えておくと便利です。
vars
Variables
http://docs.ansible.com/ansible/latest/playbooks_variables.html
Playbook 内では変数を使用する事もできます。
基本的には vars セクションで定義し、tasks セクション内などで値を参照する事が出来ます。
(with_items での item や register で作成する変数もあります。)
プログラムのバージョンなどのように頻繁に変わる値や、複数のタスクで共通に参照する値などは変数として定義しておいた方が変更が楽になります。
--- - hosts: all user: root vars: mysql_port: 3306 tasks: - name: install mysql packages yum: name=$item state=installed with_items: - mysql - mysql-server - mysql-devel - name: insert iptables rule lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}" insertafter="^:OUTPUT " line="-A INPUT -p tcp --dport {{ mysql_port }} -j ACCEPT"
上記の例では vars セクションで MySQL のポート番号を mysql_port という変数に定義し、「insert iptables rule」タスクで定義した変数を参照しています。
変数は以下のような書き方で参照することが出来ます。
- $var
- ${var}
- {{ var }}
handlers
Handlers: Running Operations On Change
http://docs.ansible.com/ansible/latest/playbooks_intro.html#handlers-running-operations-on-change
task の実行により何か変更が発生した場合のみ実行したい処理を定義するために用意されているのが handler です。
例えば、設定ファイルの変更が発生した場合のみ、サービスを再起動したい、といった事を実現するための仕組みです。
handler は通常の task と定義方法は同じですが、handlers セクションに定義します。
--- - hosts: all user: root vars: mysql_port: 3306 handlers: - name: restart iptables service: name=iptables state=restarted tasks: - name: install mysql packages yum: name=$item state=installed with_items: - mysql - mysql-server - mysql-devel - name: insert iptables rule lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}" insertafter="^:OUTPUT " line="-A INPUT -p tcp --dport {{ mysql_port }} -j ACCEPT" notify: restart iptables
handler の実行のトリガとなる task には notify を定義しておきます。
これによりその task により何かが変更された場合(changed になった場合)に notify に指定した名前を持つ handler が実行されます。
二度目の実行などでその task による変更が発生しなかった場合は実行されません。
handler が実行されるタイミングは対象の task 実行後ではなく、 Playbook の最後になります。
また、複数の task から同じ handler が notify されていた場合でも一度だけしか実行されません。
task の include
Including and Importing
http://docs.ansible.com/ansible/latest/playbooks_reuse_includes.html
tasks, vars, handlers が分かれば後はモジュールのマニュアルを見ながら大抵の処理は書けますが、複数のミドルウェアから構成されるアプリケーションのインストール処理や、複数台のサーバで構成されるシステムの構築手順を Playbook にする場合、どうしても YAML ファイルが肥大化してしまいます。
そのため、Ansible では別のファイルに定義した task を include する事により、複数ファイルから構成される Playbook を作成する事が出来るようになっています。
- site.yml(一部)
- hosts: all user: root tasks: - name: install python-selinux yum: pkg=libselinux-python state=latest - name: disable selinux selinux: state=disabled - include: redis.yml vars: libunwind: http://dl.fedoraproject.org/pub/epel/6/x86_64/libunwind-1.1-2.el6.x86_64.rpm libunwind_devel: http://dl.fedoraproject.org/pub/epel/6/x86_64/libunwind-devel-1.1-2.el6.x86_64.rpm gperftools_libs: http://dl.fedoraproject.org/pub/epel/6/x86_64/gperftools-libs-2.0-11.el6.3.x86_64.rpm redis: http://rpms.famillecollet.com/enterprise/6/remi/x86_64/redis-2.6.13-1.el6.remi.x86_64.rpm ・ ・ ・
- redis.yml
- name: install redis packages action: yum name=$item state=installed with_items: - ${libunwind} - ${libunwind_devel} - ${gperftools_libs} - ${redis} - name: start redis action: service name=redis state=started enabled=yes
この例ではメインとなる site.yml の中から Redis インストール用の redis.yml を include しています。
include される側となる redis.yml には通常 tasks セクション内に書く task のみ記述し、その他のセクションについては記述しません。
変数については include 時に vars を使って渡す事が出来ます。
Playbook の include
task の include 以外にも、 name と同じレベルで include を使う事で Playbook 自体を include することも出来ます。
- dbserver.yml
--- - name: setup database server hosts: all user: root vars: mysql_port: 3306 handlers: - name: restart iptables service: name=iptables state=restarted tasks: - name: insert iptables rule lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}" insertafter="^:OUTPUT " line="-A INPUT -p tcp --dport {{ mysql_port }} -j ACCEPT" notify: restart iptables - include: mysql.yml - include: redis.yml
- mysql.yml
--- - name: setup mysql hosts: all user: root tasks: - name: install mysql packages yum: name=$item state=installed with_items: - mysql - mysql-server - mysql-devel
- redis.yml
--- - name: setup redis hosts: all user: root vars: libunwind: http://dl.fedoraproject.org/pub/epel/6/x86_64/libunwind-1.1-2.el6.x86_64.rpm libunwind_devel: http://dl.fedoraproject.org/pub/epel/6/x86_64/libunwind-devel-1.1-2.el6.x86_64.rpm gperftools_libs: http://dl.fedoraproject.org/pub/epel/6/x86_64/gperftools-libs-2.0-11.el6.3.x86_64.rpm redis: http://rpms.famillecollet.com/enterprise/6/remi/x86_64/redis-2.6.13-1.el6.remi.x86_64.rpm tasks: - name: install redis packages action: yum name=$item state=installed with_items: - ${libunwind} - ${libunwind_devel} - ${gperftools_libs} - ${redis} - name: start redis action: service name=redis state=started enabled=yes
上記の例では dbserver.yml を実行すると include されている mysql.yml と redis.yml も実行されます。
Role
Roles
http://docs.ansible.com/ansible/latest/playbooks_reuse_roles.html
include を更に便利にしたような仕組みとして Role というものが用意されています。
Role ではサーバの役割毎にディレクトリを分け、更にその下にセクション毎にディレクトリを分けた構造で Playbook を管理します。
シンプルな例だと以下のような感じ。
mysql ├─hosts ├─site.yml └─roles └─mysql ├─handlers │ └─main.yml ├─tasks │ └─main.yml ├─templates │ └─my.cnf.j2 └─vars └─main.yml
Role を使用すると以下のようなメリットがあります。
- roles/x/tasks/main.yml が存在すれば自動的に読み込まれる
- roles/x/handlers/main.yml が存在すれば自動的に読み込まれる
- roles/x/vars/main.yml が存在すれば自動的に読み込まれる
- copy タスクで roles/x/files/ 以下のファイルはパスを指定せずに参照可能
- script タスクで roles/x/files/ 以下のファイルはパスを指定せずに参照可能
- template タスクで roles/x/templates/ 以下のファイルはパスを指定せずに参照可能
要するに、推奨されている規約に従ってディレクトリ構造を作成しておけば自動的にファイルが include される仕組みです。
ansible-examples が Role を使った構造になっているので、実際に使用する際には大変参考になります。
ansible/ansible-examples
https://github.com/ansible/ansible-examples
Playbook のデバッグ方法
Playbook が上手く動作してくれない、という時に原因を調べる方法は以下のようにいくつかあります。
「--syntax-check」オプションを指定して実行してみる
YAML ファイルの文法チェックができます。
$ ansible-playbook dbserver.yml --syntax-check
Playbook Syntax is fine
「-C, --check」オプション付きで実行してみる
いわゆる dry run です。
実際には対象サーバは変更されません。
ただ、/var/log/messages にはログは出力されます。
$ ansible-playbook dbserver.yml -i hosts -k -C
「-vvv」オプションを指定する
ansible コマンドの時と同じで、コンソールに実行時の詳細なメッセージが表示されます。
$ ansible-playbook dbserver.yml -i hosts -k -vvv
対象サーバの /var/log/messages を確認する
ansible によって何が実行されているかがログに出力されています。
# tail -f /var/log/messages Aug 13 11:05:12 centos6 ansible-setup: Invoked with filter=* Aug 13 11:05:12 centos6 ansible-yum: Invoked with name=mysql,mysql-server,mysql-devel list=None disable_gpg_check=False conf_file=None state=installed disablerepo=None enablerepo=None Aug 13 11:05:30 centos6 yum[12936]: Installed: mysql-5.1.69-1.el6_4.x86_64 Aug 13 11:05:42 centos6 yum[13057]: Installed: mysql-server-5.1.69-1.el6_4.x86_64 Aug 13 11:05:51 centos6 yum[13185]: Installed: mysql-devel-5.1.69-1.el6_4.x86_64
「--step」オプション付きで実行してみる
task を一つずつステップ実行することが出来ます。
$ ansible-playbook dbserver.yml -i hosts -k --step
debug モジュールを使う
debug - Print statements during execution
http://docs.ansible.com/ansible/latest/debug_module.html
以下のような感じで使います。
任意のメッセージや変数の値をコンソールに出力して確認することが出来ます。
--- - hosts: all user: root tasks: - command: cat /etc/redhat-release register: rhel_version - debug: msg="{{ rhel_version.stdout }}"
まとめ
Ansible の Playbook にはこの他にもまだまだ沢山の便利な機能があります。
実際に作成する際には以下のドキュメントが非常に役に立ちました。
Playbooks
http://docs.ansible.com/ansible/latest/playbooks.html
Playbooks: Special Topics
http://docs.ansible.com/ansible/latest/playbooks_special_topics.html
また、どう書くか迷った時には ansible-examples にある Playbook や、その他にも GitHub で Playbook を公開されている方が結構いるので、それらを参考にさせて貰う事で大体なんとかなります。
というわけで、私も以下で自作の Playbook の公開を始めてみました。
akishin/ansible-playbooks
https://github.com/akishin/ansible-playbooks
まだまだ数も少ないですが、今後少しずつ増やしていきたいと思います。
実際に Playbook を作成される際に少しでも参考になれば嬉しいです。
参考
Ansible Tutorial
http://yteraoka.github.io/ansible-tutorial/