Help us understand the problem. What is going on with this article?

Ansibleのyumにwith_itemsを指定するときの落とし穴と回避策

More than 5 years have passed since last update.

以前調べたけど記事にしていなかったので、Ansible 最新版の1.6.6で再度確認した上で書いておくことにしました。

yumモジュールのname属性

Ansibleのyumモジュールのname属性には以下のものが指定可能です。

  • パッケージ名
  • name-1.0 のようにパッケージ名にパージョンを追加したもの
  • rpmファイルのURL
  • rpmファイルのローカルパス

yumモジュールのマニュアルに例が出ていますのでご参照ください。

落とし穴の例

IIJのミラーからMySQLのクライアント用のrpmをダウンロードしてインストールしようと、以下の様なplaybookを書きました。

cent65-mysql.yml
- 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のソースコードで言うと以下の部分です。

https
            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 に文字列の配列ではなくハッシュの配列を指定すれば回避できます。

cent65-mysql.yml
- 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に書きましたので、そちらをご参照ください。

sakura_internet
さくらレンタルサーバ、さくらのVPS、 さくらのクラウド、さくらの専用サーバなどのインターネットサービス・ITプラットフォームを提供しています。
https://www.sakura.ad.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away