第3回 Ansibleを使って構築してみよう(後編)(1/1)

技術特集

第3回 Ansibleを使って構築してみよう(後編)

Ansible

はじめに

第2回の前編ではPlaybookの基本的な書き方を紹介しました。引き続き後編の今回は再利用等応用的な使い方に関して紹介します。

前回、「再利用」が可能であることに関しては触れましたが、サンプルや紹介した手順は決め打ちのPlaybookを作成するにとどまっていました。構築対象の環境毎に毎回用意しないといけないのかと思われた方もいたかと思います。

Ansibleでは変数を用いることで値を後から定義することが出来るだけでなく、分岐や繰り返しも行えるようになるため、様々なケースに対応できる汎用性の高いPlaybookを作成することができます。

今回はこの、Playbookの汎用性を高めることが出来る、変数の使用方法に関してご紹介します。

変数の使い方

前回ご紹介したPlaybookの記載方法ですと、ソフトウェアバージョンや設定値が固定の一連の処理を複数のサーバに対して自動で行う事しかできませんでした。他のソフトウェアバージョンや設定値で使いたいといったときに対応できていません。
そこで変数を使います。

Playbook内で、変数を使用したい箇所に「”{{変数名}}”」と記載します。また別途、後述する方法にて「変数名: 値」を記載した一覧を作成します。こうすることで、ansible-playbookコマンドを実行した際に「”{{変数名}}”」に対応する「値」が代入されます。

templatesディレクトリ下に配置するファイル内でも変数を使用することが出来ます。ここに配置したファイルは、templateモジュールを使用して変数を代入したうえで操作対象のサーバにアップロードすることが出来ます。

例えば下図httpd.confのような設定ファイルにおいて変数を使用するように編集し、実行時に環境に合わせた「値」を代入した上でアップロードするといった使い方が可能となります。

Include conf.modules.d/*.conf
User {{apache_default_User}}
Group {{apache_default_Group}}
ServerName {{ansible_hostname}}
Listen {{apache_default_listen_port}}
ServerAdmin {{apache_default_ServerAdmin}}
ServerRoot {{apache_default_ServerRoot}}
DocumentRoot {{apache_default_DocumentRoot}}
<Directory ⁄>
    AllowOverride none
    Require all denied
<⁄Directory>
<Directory ⁄> "{{apache_default_BaseDir}}">
    AllowOverride None
    Require all granted
<⁄Directory>

変数の値の定義の方法

Ansibleにおいて変数の値を設定する方法は2018年12月時点で、varsディレクトリ内での指定やインベントリーファイル内での指定など22通りあります。人によって、使い方に好みがありますので、自分にとって使いやすい設定方法を探していただくとよいでしょう。本稿では、それら変数の中で私がよく使用するものを紹介します。

ご紹介するのはdefaultsディレクトリ内での設定方法、TopレベルのPlaybook内での設定方法、インベントリーファイル内での設定方法です。

① defaultsディレクトリ内での設定方法

defaultsディレクトリ内で設定した変数はPlaybookの変数のデフォルト値として扱われます。ここで設定した変数の値はその他の場所で設定した値によって上書きされます。変数定義と合わせて作成し、変更が必要な場所だけを別途、別の設定場所で定義します。毎回全ての変数を定義する手間を減らすとともに設定漏れを防ぐことが出来ます。

使用する際は、前回ご紹介したベストプラクティス構成で各Role用のディレクトリ内にdefaultsという名前のディレクトリを作り、その中にmain.ymlというファイルを作成します。

main.ymlの中に下図のように「変数名: 値」の組み合わせを記載します。

② TopレベルのPlaybook内での設定方法

変数の値はTopレベルのPlaybook内でも定義する事が出来ます。TopレベルのPlaybookはお客様ごと、設定する環境ごとに作成することになりますため、この中で変数の値の定義も一まとめにしておくと管理がしやすくなります。また、再利用を行うにあたって他環境用の値を誤って使用してしまうリスクも減らすことが可能です。
使用する際は、TopレベルのPlaybook内にvarsという項目を設け、下図のように「変数名: 値」の組み合わせを記載します。

---
- name: Install Apache
  hosts: webserver
  become: yes
  vars:
    http_port: 8080
    https_port: 8443
  roles:
    - apache

③ インベントリーファイル内での設定方法

変数の値はインベントリーファイル内でも設定する事が出来ます。インベントリーファイルにおいては、各環境への接続に必要な情報を記載します。ホスト毎に、クラウドやコンテナのように接続方法が異なることがあります。また、接続に使用するユーザが異なっていることもあります。そういったホスト固有の情報を、ホスト情報の近くに一まとめにしておくと管理がしやすくなります。
使用する際は、インベントリーファイルのホスト名(or IPアドレス)の後に「変数名=値」を記載します。複数記載する際はスペースで区切ります。

[honban]
192.168.1.1  ansible_ssh_user=hogehoge
192.168.1.2  ansible_ssh_user=foobar

グループ内のホストが全て同じ変数を使用するといった場合、 [グループ名:vars]というグループを設け、その中で定義を行う事が出来ます。なお、全ホストを対象にしたい場合は[all:vars]というグループを設け、その中で定義することも出来ます。

特殊な変数

Ansibleではループ処理や戻り値、環境情報を扱う際に前述の変数とは別の使い方をする変数を使用します。前述の変数は事前に定義していましたが、これらは自動で収集されたり、処理の結果で定義されたりする点が異なっています。
以降これら変数に関してご紹介します。

① ループ処理で使用する変数

Ansibleにおけるいくつかのループ処理手法の一つに、with_itemsモジュールを使用する方法があります。別のモジュールのパラメータ部に変数名として”{{ item }}”を指定し、with_items以降にリスト形式で値を列挙すると、値の数だけシーケンシャルに代入され、処理が行われます。
yumやaptで複数のパッケージをインストールする際に利用する事が多いかと思います。
下図の例では、MariaDB関連のパッケージのインストールを、yumモジュールを使用して行っています。with_itemsを使用しない場合はyumを複数回記載する事となります。こうした場合、冗長なコードとなってしまい、可読性が下がるだけでなく、修正を行う場合に漏れが発生する等のリスクがあります。
with_itemsを使用した場合は1か所の記載で済みます。インストールするパッケージが増えた場合はリストに追加するだけで済むため、管理が容易となり、漏れも発生しにくくなります。このリストも変数化する事が可能なため、Playbookに変更を加えずにインストールする対象を増やす事もできます。

1回のループの中で複数の変数への値の代入を行う事も可能です。この場合、モジュールのパラメータに記載する変数名は
”{{ item.○○ }}”のようになります。with_items内では{○○: 値, △△: 値}のように複数の定義を含む行を記載します。こうする事で○○に対応する識別情報が紐づけられ、設定されている値が代入されます。
こういった手法はファイルの編集を行う場合等で利用されることが多いかと思います。
下図の例では/etc/hostsのIPv6関連の設定を書き換えています。使用しているlineinfileモジュールははファイルを操作するモジュールです。with_items内でregexpに正規表現を定義し、lineに書き換える内容を定義しています。
これらがそれぞれ変数”{{item.regexp}}”と”{{item.line}}”に代入されます。

② 処理の条件分岐に使用する変数

構築作業を行う中で、「ファイルが存在していたら」とか「画面にこれが表示されたら」、「処理の結果が成功であったら」のように処理結果を元に次の手順を行うかの判断を行うケースがあるかと思います。こういった場合、Ansibleではregisterというモジュールを使用することで、結果を変数の中に一時的に保管しておく事が出来ます。

例えばget_urlやunarchive等のダウンロード関連のモジュールやshell、commandと言ったスクリプトやコマンドを直接実行するモジュール等を使用した場合、Ansibleでは2回目であっても処理を実行してしまいます。余計な時間がかかってしまうだけでなく、実行内容によっては2回目の実行により正常に動作しなくなってしまう事もあるため、回避処理を考える必要があります。
こういった際にregisterモジュールを使用することで、例えば特定ファイルの有無を確認する等した結果を変数に保存し、処理の分岐に使用する事が出来ます。

下図の例はKubernetesのクラスタに参加していなければ参加するコマンドを実行するPlaybookです。事前に、ファイルやディレクトリの情報を収集しjson形式で結果を返す、statモジュールを使用して特定のファイルが存在することを確認しています(※)。registerモジュールを使用し、確認結果を_joinedという変数名を作成して格納しています。
次のタスクの中でwhenモジュールを使用し、_joinedに格納された結果のstatの項目がexistsになっていなかった場合にshellモジュールを使って参加コマンドを実行しています。

※例として挙げましたStatモジュールですが、有無以外にも情報が得られます。得られる戻り値に関しての詳細はこちらを参照してください (https://docs.ansible.com/ansible/2.5/modules/stat_module.html)。

ファイル有無以外でも、例えば直前の処理の成否やshellコマンド等の実行結果、標準出力の内容を元に分岐を行いたいと言ったケースもあるかと思います。この場合はregisterを使用して格納した結果のrc(リターンコード)やstdout(標準出力)の項目の戻り値を使用します。

Playbookの作成段階において、戻り値がちゃんと取れている事、想定した値になっている事を確認したい際のためにdebugというモジュールが用意されています。
下図のようにvar=変数名 というパラメータを与えることで変数の中を参照することが出来ます。Playbookの開発中や試験において使用するとよいでしょう。

---
- name: Check which user is used
shell: /bin/bash –lc “whoami”
  register: _result- name: show _result
  debug: var=result

debugモジュールを使用すると、結果画面に下図のように表示されます。shellモジュールを使用してwhoamiコマンドを実行した結果が_resultという変数に格納されています。
rcに0が入っていますので、処理が正常に行われた事がわかります。この処理が正常ならば後続のタスクを実行したいといった場合、when _result.rc==0 のような分岐条件を作る事ができます。
また、stdoutに”root”が格納されているため、whoamiコマンド実行時に画面には”root”と表示されていた事がわかります。

③ 環境情報を格納する変数

Ansibleでは特に指定がない場合、処理の最初に環境情報の取得を行って変数に保存しています。この変数をファクト変数と呼びます。IPアドレスやCPU情報、OS情報と多岐にわたる情報を収集しています。
大量になるので例示しませんが、debugモジュールを使用して"{{ ansible_facts }}"の格納内容を確認する事が出来ます。ファクト変数を確認する場合は、msg: "{{ ansible_facts }}"と記載します。
例えば下図のようにapacheのインストール時にOSがRedHat系ならyum、Debian系ならaptのモジュールを使用すると言った分岐に活用する事ができます。

---
- name: Install apache(CentOS)
  yum:
   name: httpd
   state: present
  when: ansible_facts.distribution == “CentOS”- name: Install apache(Debian)
  apt:
   name: httpd
   state: present
  when: ansible_facts.distribution == “Debian”

おわりに

今回はAnsibleでもPlaybookを再利用できるようにするために変数や分岐、繰り返し等の手法が使用できる事を紹介しました。最初に変数を使用するように作成しておけば、Playbook自体は直さなくても値だけ別途定義する事で、別の環境に再利用出来る事をご理解いただけましたでしょうか?
変数を上手に活用することで、同じPlaybookを作成しなおす手間が減るだけでなく、別の案件でうまく動いたそのままの物を使用できるため、品質面でも安心できるかと思います。

次回は、Ansibleの機能拡張が行われるGUI版に関して紹介いたします。
CUI版に抵抗がある方でも利用できるようになるだけではなく、色々と便利な機能も使えるようになりますので、是非ご検討いただければと思います。