前回はタスクが終了した際に後続処理を実行する方法についてご紹介しました。今回は設定ファイルなどをローカルから管理対象のサーバへコピーする際に、単純なコピーではなくもう少し複雑なことができる「テンプレート」について取り上げます。
設定ファイルをコピーする
単純なコピー
ローカルにあるファイルを、管理対象のサーバに転送するだけであればcopy
モジュールを使えば手軽に実現できます。
- hosts: servers remote_user: ansibleman become: yes tasks: - name: copy Apache vhost config file copy: src: conf/httpd/vhost.conf dest: /etc/httpd/conf.d/vhost.conf
ファイルの構造は以下の通りです。
~/📁 ansible1st 📒 ansible.cfg 📒 playbook.yml 📁 inventory 📒 production 📁 conf 📁 httpd 📒 vhost.conf ★NEW
実行すると転送されてるのが確認できますね。お手軽です。
$ ansible-playbook playbook.yml $ ssh ansibleman@example.com [example.com] $ ls -1 /etc/httpd/conf.d | grep vhost vhost.conf
このように予め設定ファイルを用意しておけばコピーするだけで済むのですが、今回はAnsible上で動的に設定ファイルを作成する方法に挑戦してみます。
設定ファイルを動的に作成する
Ansibleにはtemplateモジュールが用意されており、Python用のテンプレートエンジンであるJinja2を利用できます。これらはAnsibleに最初から入ってますので別途インストールや設定の必要はありません。
では単純に特定の箇所を置換するシンプルな方法から試してみます。
playbook.yml
まずはPlaybookです。
- hosts: servers remote_user: ansibleman become: yes vars: #------------------------------------------# # テンプレートに渡す変数 #------------------------------------------# hostname: example.com document_root: /var/www/example.com/htdocs tasks: #------------------------------------------# # Apahceの設定ファイルを動的に作成して転送 #------------------------------------------# - name: generate Apache vhost config file template: src: template/conf/httpd/vhost.conf.j2 dest: /etc/httpd/conf.d/vhost.conf
Jinja2へ渡すための変数をvars
に定義します。もちろんvars_filesで外部のファイルを読み込んでも大丈夫です。
タスクの方にはtemplate
モジュールを書いてやります。オプションとしてsrcにテンプレートファイルを、descに転送先のパスを書いてあげます。テンプレートファイルの拡張子は通常.j2
とするのが慣習となっています。
vhost.conf.j2
こちらはテンプレートです。
テンプレート内で{{ 変数名 }}
と記述すると、Playbookで定義した変数をそのまま埋め込むことが可能です。
<VirtualHost *:80> ServerName {{ hostname }} DocumentRoot {{ document_root }} </VirtualHost>
現在のファイル構造
以下の通りです。
~/📁 ansible1st 📒 ansible.cfg 📒 playbook.yml 📁 inventory 📒 production 📁 template 📁 conf 📁 httpd 📒 vhost.conf.j2 ★NEW
実行してみる
実際にPlaybookを実行すると、{{ 変数名 }}
の部分が置換されているのがわかります。
$ ansible-playbook playbook.yml $ ssh ansibleman@example.com [example.com] $ cat /etc/httpd/conf.d/vhost.conf <VirtualHost *:80> ServerName example.com DocumentRoot /var/www/example.com/htdocs </VirtualHost>
設定ファイルを「ループ」して作成する
単純な置換だけでもかなり便利に使えるのですが、今度は複数のVirtualHostを同時に定義してみます。
パッと思いつく方法としては以下のように頭に接頭詞(prefix)を付けて区別するやり方ですが、数が多くなってくると人間が対応するのは現実的ではなくなってきます。ミスを無くして楽をするためにAnsibleを導入するハズが本末転倒ですねw
vars: a_hostname: example.com b_hostname: example.net c_hostname: example.org
<VirtualHost *:80> ServerName {{ a_hostname }} </VirtualHost> <VirtualHost *:80> ServerName {{ b_hostname }} </VirtualHost> <VirtualHost *:80> ServerName {{ c_hostname }} </VirtualHost>
こういった時のために、Jinja2にはループ機能が備わっています。
playbook.yml
Playbookのvars
にはハッシュの配列を用意してやります。
- hosts: servers remote_user: ansibleman become: yes vars: vhosts: - { name: example.com, docroot: /var/www/example.com/htdocs } - { name: example.net, docroot: /var/www/example.net/htdocs } - { name: example.org, docroot: /var/www/example.org/htdocs } tasks: - name: generate Apache vhost config file template: src: template/conf/httpd/vhost.conf.j2 dest: /etc/httpd/conf.d/vhost.conf
vhost.conf.j2
こちらはテンプレートです。ようはプログラミング言語で言うfor/foreach文が利用できます。
{% for host in vhosts %} <VirtualHost *:80> ServerName {{ host.name }} DocumentRoot {{ host.docroot }} </VirtualHost> {% endfor %}
Jinja2では {% 制御構文 %}
で囲むと制御構造として利用できます。
Playbookで定義した配列vhosts
を1つずつhost
に代入し、最後まで順番に処理をします。ハッシュの各要素にアクセスするときはドット(.)で区切ってハッシュのキーを指定するだけです。
実行してみる
実際にPlaybookを実行すると、期待通り繰り返し処理が置換されているのがわかります。
$ ansible-playbook playbook.yml $ ssh ansibleman@example.com [example.com] $ cat /etc/httpd/conf.d/vhost.conf <VirtualHost *:80> ServerName example.com DocumentRoot /var/www/example.com/htdocs </VirtualHost> <VirtualHost *:80> ServerName example.net DocumentRoot /var/www/example.net/htdocs </VirtualHost> <VirtualHost *:80> ServerName example.org DocumentRoot /var/www/example.org/htdocs </VirtualHost>
設定ファイルを「条件分岐」して作成する
for/foreachが使えるとなると、if文も使いたくなるのが人の常という物ですねw これもJinja2の機能で利用することができます。
今回はhost.isauth
の値がTrueならBASIC認証をかける設定ファイルを作成してみます。
playbook.yml
真ん中のexample.net
だけTrueにしました。
- hosts: servers remote_user: ansibleman become: yes vars: vhosts: - { name: example.com, isauth: false, docroot: /var/www/example.com/htdocs } - { name: example.net, isauth: true, docroot: /var/www/example.net/htdocs } - { name: example.org, isauth: false, docroot: /var/www/example.org/htdocs } tasks: - name: generate Apache vhost config file template: src: template/conf/httpd/vhost.conf.j2 dest: /etc/httpd/conf.d/vhost2.conf
vhost.conf.j2
テンプレートの方はというと、こちらもループと同様に{% if %}
でif文を定義することができます。
{% for host in vhosts %} <VirtualHost *:80> ServerName {{ host.name }} DocumentRoot {{ host.docroot }} {% if host.isauth %} <Directory {{ host.docroot }}> AuthType Basic AuthName "Basic Auth" AuthUserfile /etc/httpd/conf.d/.htpasswd Require valid-user </Directory> {% endif %} </VirtualHost> {% endfor %}
条件演算子
一般的なプログラミング言語で使うようなものは一通り用意されています。
- 比較
- ==, !=, >, >=, <, <=
- 論理
- and, or, not
- その他
- in
inは配列の中に指定した値があるかを判定する物です。
{% if "banana" in ["apple", "banana", "orange"] %} ... {% endif %}
else if / else
else if
やelse
ももちろん書けます。
{% if host.redirect == "blog" %} Redirect https://blog.example.com/ {% elif host.domain == "db" %} Redirect https://db.example.com/ {% else %} Redirect https://example.com/ {% endif %}
例は1行だけですが、各ブロックの中は複数行になっても大丈夫です。
実行してみる
では最後に実行します。無事に生成されましたがインデントがちょっと汚いですねw Apacheさんは気にせず実行してくれますが、人間が見るとちょっとウッとなるやつです。
$ ansible-playbook playbook.yml $ ssh ansibleman@example.com [example.com] $ cat /etc/httpd/conf.d/vhost.conf <VirtualHost *:80> ServerName example.com DocumentRoot /var/www/example.com/htdocs </VirtualHost> <VirtualHost *:80> ServerName example.net DocumentRoot /var/www/example.net/htdocs <Directory /var/www/example.net/htdocs> AuthType Basic AuthName "Basic Auth" AuthUserfile /etc/httpd/conf.d/.htpasswd Require valid-user </Directory> </VirtualHost> <VirtualHost *:80> ServerName example.org DocumentRoot /var/www/example.org/htdocs </VirtualHost>
この手のテンプレートエンジンあるあるですが、テンプレート側をキレイに書くか、出力ファイルをキレイにするか…悩ましいところですねw
Jinja2の機能で{%- 〜 -%}
とパーセントの横にマイナスを書くことでホワイトキャラクター(改行やスペース)を削除することができますので、これで調整するのも手です。…が思ったとおりに一発で決まった試しがないので最初は動くことを最優先でテンプレートを書いて、余力あるときに見た目をキレイにするのが良いかと思います。(プログラミングで言う有る種のリファクタリングですね)
続き
参考ページ
インプレス (2019-10-18)
売り上げランキング: 17,753