構成管理ツールの 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 さんにアドバイスを頂きました。