CUBE SUGAR CONTAINER

技術系のこと書きます。

Ansible をシェルスクリプトの代替として使う

構成管理ツールの Ansible を導入するにあたって、まずはシェルスクリプトの代替として使い始めてみようと考えた。 ただ、それにはひとつ問題があって、Ansible は通常 SSH を使ってリモートホスト上でコマンドを実行する点が挙げられる。 それに対して、シェルスクリプトはローカルホスト上で実行するものなので、そのギャップをどう埋めればいいのか分からなかった。 今回は、とりあえずこれでいけそうという目処がついたので、そのやり方について書き残しておく。

やりたいこと

要件としては、デプロイする各ホストで Ansible の Playbook は共通のものを使いまわしたい。 その上で、各ホストや環境毎に Playbook の変数部分を切り替える。

今回は web1 と web2 という二つのホストをデプロイする想定で記事を書いていく。 そして、シェルスクリプトの代替として使うので web1 および web2 のホスト上で Ansible を実行することになる。

Ansible をインストールする

まずは何はともあれ Ansible をインストールしておく。 今回は Python のパッケージマネージャである PIP を使っているけど、もちろん別のやり方でも構わない。

$ pip install ansible

Inventory を用意する

次に、環境やホストの情報を書いた Inventory を用意する。 ポイントは、各ホストへの接続方法として ansible_connection=local を指定すること。 Inventory に書くホスト名は疎通のある IP アドレスや DNS で引けるものである必要はないようだ。 管理者の識別できる名前を書いた上で、そのホストに対する接続方法をスペースを空けて記述していくこともできる。 以下では ansible_connection を local に指定しているので、web1 と web2 のデプロイはローカルホスト上で行うという意味になる。

$ cat << EOF > hosts
[webs]
web1 ansible_connection=local
web2 ansible_connection=local

EOF

Playbook を用意する

次にデプロイする具体的な手順を書いた Playbook を用意する。 今回はテスト用途なので、変数の内容を表示するだけにする。 以下の Playbook (site.yml) を web1 と web2 で共通にした上で、その中にある {{ message }} という変数をホスト毎に切り替えたい。

$ cat << EOF > site.yml
---
- hosts:
  - webs
  tasks:

    - name: Greet
      debug: msg="Hello, {{ message }}"

EOF

ホスト変数を用意する

ホスト毎に変数を切り替えるにはホスト変数を用意する。 Ansible は Playbook のある場所に host_vars というディレクトリを作ると、その中にある変数を定義した YAML ファイルを読み込んでくれるようだ。

$ mkdir host_vars

作成したディレクトリの中に変数を書いた YAML ファイルを作る。

$ cat << EOF > host_vars/web1
---
message: Web1!

EOF
$ cat << EOF > host_vars/web2
---
message: Web2!

EOF

Playbook を実行する

ここまでで準備ができたので Playbook を実行する。 実行する Playbook と Inventory を指定するまでは Ansible の通常の実行手順と変わらないけど、ポイントは -l オプションで実行するホストを限定するところ。

$ ansible-playbook site.yml -i hosts -l web1

PLAY [webs] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [web1]

TASK: [Greet] ***************************************************************** 
ok: [web1] => {
    "msg": "Hello, Web1!"
}

PLAY RECAP ******************************************************************** 
web1                       : ok=2    changed=0    unreachable=0    failed=0   

-l オプションで実行するホストを変えることで、表示される変数の内容も変わることがわかる。

$ ansible-playbook site.yml -i hosts -l web2

PLAY [webs] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [web2]

TASK: [Greet] ***************************************************************** 
ok: [web2] => {
    "msg": "Hello, Web2!"
}

PLAY RECAP ******************************************************************** 
web2                       : ok=2    changed=0    unreachable=0    failed=0   

環境毎に使用する変数を切り替える

ここまででホスト毎に使用する変数を切り替えることはできた。 ただ、それ以外にも環境毎に使用する変数を切り替えたい場面もある。 例えば一口にホストと言っても開発用、ステージング用、プロダクション用など色々なものがあって、それぞれ変数が異なっているはず。

環境ごとに使用する変数を切り替えるには使用する Inventory を変えるのがよさそう。 以下ではステージング用とプロダクション用にふたつの Inventory を用意した。 その中で [all:vars] セクションにホスト共通の変数として environ を定義している。

$ cat << EOF > staging
[webs]
web1 ansible_connection=local
web2 ansible_connection=local

[all:vars]
environ=staging

EOF
$ cat << EOF > production
[webs]
web1 ansible_connection=local
web2 ansible_connection=local

[all:vars]
environ=production

EOF

実行する Playbook にも上記の変数を表示するためのタスクを追加しておこう。

$ cat << EOF > site.yml
---
- hosts:
  - webs
  tasks:

    - name: Greet
      debug: msg="Hello, {{ message }}"

    - name: Environ
      debug: msg="Environ:{{ environ }}"

EOF

準備ができたので Playbook を実行する。 環境毎に -i オプションで使用する Inventory を切り替える。

$ ansible-playbook site.yml -i staging -l web1

PLAY [webs] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [web1]

TASK: [Greet] ***************************************************************** 
ok: [web1] => {
    "msg": "Hello, Web1!"
}

TASK: [Environ] *************************************************************** 
ok: [web1] => {
    "msg": "Environ:staging"
}

PLAY RECAP ******************************************************************** 
web1                       : ok=3    changed=0    unreachable=0    failed=0   
$ ansible-playbook site.yml -i production -l web1

PLAY [webs] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [web1]

TASK: [Greet] ***************************************************************** 
ok: [web1] => {
    "msg": "Hello, Web1!"
}

TASK: [Environ] *************************************************************** 
ok: [web1] => {
    "msg": "Environ:production"
}

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

ばっちり。

まとめ

今回は Ansible をシェルスクリプトの代替として使うやり方について書いた。 最初は Playbook の hosts に localhost を指定した状態を試していたんだけど、それだと全ての変数をホスト毎×環境毎に Inventory を用意する必要があって早々に破綻した。 それに対し、今のやり方であれば Inventory の数が爆発するといった問題もない。 ホスト変数やグループ変数といった Ansible の持つ機能もそのまま使えるので、なんとかなりそうな感じ。

謝辞

今回のやり方については悩んでいたところ @r_rudi さんにアドバイスを頂きました。