以前調べたけど記事にしていなかったので、Ansible 最新版の1.6.6で再度確認した上で書いておくことにしました。
yumモジュールのname属性
Ansibleのyumモジュールのname属性には以下のものが指定可能です。
- パッケージ名
- name-1.0 のようにパッケージ名にパージョンを追加したもの
- rpmファイルのURL
- rpmファイルのローカルパス
yumモジュールのマニュアルに例が出ていますのでご参照ください。
落とし穴の例
IIJのミラーからMySQLのクライアント用のrpmをダウンロードしてインストールしようと、以下の様なplaybookを書きました。
- hosts: cent65
user: vagrant
sudo: yes
vars:
mysql_download_base_url: http://ftp.iij.ad.jp/pub/db/mysql/Downloads/MySQL-5.6
mysql_download_dest_dir: /usr/local/src
mysql_common_rpms:
- MySQL-shared-5.6.19-1.el6.x86_64.rpm
- MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm
- MySQL-client-5.6.19-1.el6.x86_64.rpm
- MySQL-devel-5.6.19-1.el6.x86_64.rpm
tasks:
- file: path={{ mysql_download_dest_dir }} state=directory
- get_url: >
url={{ mysql_download_base_url }}/{{ item }}
dest={{ mysql_download_dest_dir }}
with_items: mysql_common_rpms
- yum: name="{{ mysql_download_dest_dir }}/{{ item }}" state=present
with_items: mysql_common_rpms
しかし、これはエラーになってしまいます。例によって ansible-playbook
に -vvvv
オプションをつけて実行すると、ログに以下のような行がありました。
$ ansible-playbook -vvvv cent65-mysql.yml
…(略)…
<cent65> REMOTE_MODULE yum name="/usr/local/src/MySQL-shared-5.6.19-1.el6.x86_64.rpm,MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm,MySQL-client-5.6.19-1.el6.x86_64.rpm,MySQL-devel-5.6.19-1.el6.x86_64.rpm" state=present
…(略)…
failed: [cent65] => (item=MySQL-shared-5.6.19-1.el6.x86_64.rpm,MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm,MySQL-client-5.6.19-1.el6.x86_64.rpm,MySQL-devel-5.6.19-1.el6.x86_64.rpm) => {"changed": false, "failed": true, "item": "MySQL-shared-5.6.19-1.el6.x86_64.rpm,MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm,MySQL-client-5.6.19-1.el6.x86_64.rpm,MySQL-devel-5.6.19-1.el6.x86_64.rpm", "rc": 0, "results": []}
msg: No Package file matching 'MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm' found on system
yumのname属性にカンマ区切りの文字列が指定され、最初の要素は /usr/local/src/MySQL-shared-5.6.19-1.el6.x86_64.rpm
とフルパスになっているのですが、2つめ以降は MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm
のようにファイル名のみになっています。
上記のplaybookでは /usr/local/src/
にrpmをダウンロードしているので、これでは見つからずエラーになっているというわけです。
yumモジュールにwith_itemsを指定した場合の最適化
これはAnsibleのyumモジュールをwith_itemsと組み合わせて使うと {{ item }}
の部分をwith_itemsに指定した要素をカンマで連結した値に展開して、yumを実行するようになっています。Ansibleのソースコードで言うと以下の部分です。
if len(items) and utils.is_list_of_strings(items) and self.module_name in [ 'apt', 'yum', 'pkgng' ]:
# hack for apt, yum, and pkgng so that with_items maps back into a single module call
use_these_items = []
for x in items:
inject['item'] = x
if not self.conditional or utils.check_conditional(self.conditional, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars):
use_these_items.append(x)
inject['item'] = ",".join(use_these_items)
items = None
通常は with_items
で配列を指定した場合は、要素のそれぞれについてモジュールの処理を実行します。 apt
, yum
, pkgng
の場合は上記のような最適化が入るのです。
回避策
with_items
に文字列の配列ではなくハッシュの配列を指定すれば回避できます。
- hosts: cent65
user: vagrant
sudo: yes
vars:
mysql_download_base_url: http://ftp.iij.ad.jp/pub/db/mysql/Downloads/MySQL-5.6
mysql_download_dest_dir: /usr/local/src
mysql_common_rpms:
- filename: MySQL-shared-5.6.19-1.el6.x86_64.rpm
- filename: MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm
- filename: MySQL-client-5.6.19-1.el6.x86_64.rpm
- filename: MySQL-devel-5.6.19-1.el6.x86_64.rpm
tasks:
- file: path={{ mysql_download_dest_dir }} state=directory
- get_url: >
url={{ mysql_download_base_url }}/{{ item.filename }}
dest={{ mysql_download_dest_dir }}
with_items: mysql_common_rpms
- yum: name="{{ mysql_download_dest_dir }}/{{ item.filename }}" state=present
with_items: mysql_common_rpms
こうすると with_items
の配列の要素それぞれについて yum
モジュールが実行され、エラーを回避できます。
$ ansible-playbook -vvvv cent65-mysql.yml
…(略)…
<cent65> REMOTE_MODULE yum name="/usr/local/src/MySQL-shared-5.6.19-1.el6.x86_64.rpm" state=present
…(略)…
<cent65> REMOTE_MODULE yum name="/usr/local/src/MySQL-shared-compat-5.6.19-1.el6.x86_64.rpm" state=present
…(略)…
get_urlの使い方の改善
上記のplaybookはさらに改善することができます。
Ansibleのget_urlはdestをファイル名にしてsha256sumを指定するのがお勧め - Qiitaに書きましたので、そちらをご参照ください。