構成管理ツールAnsibleのv1.2以降に使えるRolesという機能を使って、VirtualBox + Vagrant + Ansibleを連携させて開発環境を構築するサンプルを作りました。

前回書いた『VirtualBox + Vagrant + Ansibleを使って『Pythonプロフェッショナルプログラミング』で使う開発環境(に近いもの)をほぼ自動で構築するPlaybookを作ってみました』という記事の続きです。なので、前回の記事を読んでないと意味不明かもしれません。

がっつり調べたというわけではなく、「色々試してやっと基本的なところが出来ました」というくらいの話しなので、軽めに書きます---と言いたかったのですが、書いてみたらえらく長くなってしまいました。

最初に結論をまとめると

  • 前回の記事で手前味噌ながら紹介した「『Pythonプロフェッショナルプログラミング』で使う開発環境を構築できるお試しセット」を、Ansibleのv1.2以降で使えるようになるRolesという機能を使って書き換えてみました。
  • URLはこちら。role-sampleという名前のブランチでRoles機能を使っています。
  • まだ作業途中なので内容が変わるかもしれませんが、やっていることはmasterブランチの内容と全く同じで、単にRolesという機能を使うように変更しただけです。
  • READMEはまだ書き換えてないので無視して下さい。
  • 現時点ではPyPIに上がっているAnsibleのバージョンは1.1なので、Ansibleをgithubから取ってきてsudo python setup.py installするなりしてAnsibleのバージョンを1.2相当にした後、実行するコマンドを以下のように変更してもらうとsshのパスワードを聞かれた後Ansibleが起動します。Roles機能を使わないときと同じ動作をすると思います。
ansible-playbook main.yml -i hosts/local -k
  • 注意事項など
    • 前回の記事で書いた注意事項は最低限気をつけて下さい。
    • 今回(Ansible1.2)の場合に気をつけるべき事はこの記事内の後のほうに書きます。

そもそもAnsibleのRolesって何よ?という人のための簡単な説明

Ansibleの説明はもう面倒臭い必要ないと思うので省きます。

前回の記事の下の方に参考記事リンクをバリバリ貼っているので、Ansibleが何か知らない人はそこを見たり、為になるこちらの記事を見てください。あと、Ansibleの基本的な使い方は前回記事で紹介したリポジトリのREADMEを読んで実際に手を動かしてみた人という想定でこれから書きます。一つ一つ説明書くといつまで経ってもRolesの説明が出来ないので*2

いまこの記事を書いている現在のAnsibleのバージョン(PyPIに上がっている方)は1.1なのですが、github上にある開発バージョンには1.1に含まれていない機能・モジュールが多数あります。Rolesもその一つです。

Rolesというのは乱暴に言ってしまうと、あらかじめ決められたディレクトリ構成を作って所定の位置にファイルを置けばAnsibleのincludeをより便利且つ楽に使えるようになる仕組みのことです。

includeとは何かというと、あるplaybookから他のplaybookを呼び出す機能*3なのですが、前回の記事で作ったリポジトリを例にした方が分かると思うのでそうします。

まずrolesを使わずincludeだけを使った場合の例を示します。

---
├── editor.yml
├── essential.yml
├── hosts
│   └── local
├── main.yml
└── python.yml

includeだけを使ったplaybookのディレクトリ構成は上のようになっています。ここで実際にAnsibleを使うときに指定するplaybookであるmain.ymlの中身を見てみましょう。

---

# main.yml

- hosts: pypro
  user: vagrant
  sudo: yes

  tasks:
    - include: essential.yml  tags=essential
    - include: editor.yml     tags=editor
    - include: python.yml     tags=python

ここでincludeが使われていますが、includeの後に呼び出したいファイルを指定するとそのファイルが読み込まれて中に書かれている内容を処理します。

ここでは最初にincludeを使って呼び出されているessential.ymlの中を見てみましょう。

---

# essential.yml

- name: Install basic server pkgs
  apt: pkg=$item force=yes update_cache=yes
  with_items:
    - build-essential
    - libsqlite3-dev
    - libreadline6-dev
    - libgdbm-dev
    - zlib1g-dev
    - libbz2-dev
    - sqlite3
    - tk-dev
    - zip
    - python-dev
    - python-pip
    - python-setuptools
    - python3.2
    - subversion
    - openjdk-7-jre-headless
    - nginx

何をやっているかはだいたい見てもらえれば分かると思いますが、Ansibleに標準付属されているaptモジュールを使って開発に必要なパッケージ群をインストールする処理が書かれています。

includeを使わないで同じ処理をしようと思ったときにmain.ymlの中身がどうなるかというと…

---

# main.yml

- hosts: pypro
  user: vagrant
  sudo: yes

  tasks:
  - name: Install basic server pkgs
    apt: pkg=$item force=yes update_cache=yes
    with_items:
      - build-essential
      - libsqlite3-dev
      - libreadline6-dev
      - libgdbm-dev
      - zlib1g-dev
      - libbz2-dev
      - sqlite3
      - tk-dev
      - zip
      - python-dev
      - python-pip
      - python-setuptools
      - python3.2
      - subversion
      - openjdk-7-jre-headless
      - nginx
(以下省略)

このように大変長くなってしまいます。これくらいならまだ一つにまとめてもいいかもしれませんが、もっと処理が多くなると一つにまとめた場合、どこにどんな処理が書いてあるのか把握するのも大変になってきます。

includeを使うと一つのファイルに処理をまとめて書く必要がなくなり、ファイルを分割して記述が出来るようになります。プログラミングで小さなモジュールを組み合わせて使うのと同じように、includeを使えば一つのファイルに必要以上に命令を書き込まず、再利用可能なplaybookを作ることが可能になるんですね。

includeがあれば再利用可能なplaybookを作るのは可能なんですが、ディレクトリ構成をどうするかなどの方針を決めずに使いすぎるとどこに必要な処理を書いたのか後から把握するのが辛くなりますし、そもそも再利用可能なディレクトリ構成を考えつつ再利用可能なplaybookを書くのは結構骨が折れます。

そもそも上で使用したのはtaskだけで、fileとかtemplate、handlerやvarといったtask以外の機能を使用してません。これらの機能も含めてplaybookを分割して分かりやすくディレクトリ構成を決めるのは大変なはずです。

そこでRolesという機能の出番です。

Rolesというのは公式ドキュメントの説明を基に超訳すると

  • 「includeディレクティブを単に自動化したもの」
  • 「参照するファイルのパスを見つける処理を改良しているだけ」

のものです。

日本語で言っているだけだと意味不明だと思うので実例を見てみましょう。

下で紹介する例は上で紹介したincludeの例を単純にrolesで置き換えたものです。

.
├── hosts
│   └── local
├── main.yml
└── roles
    └── common
        ├── tasks
        │   └── main.yml
        └── vars
            └── main.yml

hostsディレクトリとmain.ymlの場所は変わっていません。変わっているのはessential.ymlなどのplaybookが無くなり、代わりにrolesというディレクトリが増えています。

このときのmain.ymlの中身は下のようになっています。

---
# file: main.yml
- hosts: pypro
  user: vagrant
  sudo: True
  roles:
    - common

上のように、rolesというディレクティブを使い、rolesディレクトリの直下にあるディレクトリ(この場合はcommon)を指定すると、roles/tasks/main.ymlとroles/vars/main.ymlが自動的にincludeされます。

それぞれのファイルの中身のうち、essential.ymlの処理に当たる箇所は以下の通りになっています。

---

# file: roles/common/tasks/main.yml

- name: Install basic server pkgs
  apt: pkg={{ item }} force=yes update_cache=yes
  with_items: ${system_packages}
---

# file: roles/common/vars/main.yml

system_packages:
  - build-essential
  - libsqlite3-dev
  - libreadline6-dev
  - libgdbm-dev
  - zlib1g-dev
  - libbz2-dev
  - sqlite3
  - tk-dev
  - zip
  - python-dev
  - python-pip
  - python-setuptools
  - python3.2
  - subversion
  - openjdk-7-jre-headless
  - nginx

一番最初の例で使ってなかった変数の機能(var)を使っているので分かりづらくなっているかもしれませんが、ここで大事なのはplaybook内でroles: xと書けば、わざわざincludeと明示的に指定しなくても以下のファイルを勝手にincludeしてくれるということです。そのため、ansible-playbook main.yml -i hosts/local -k と打って実行すると、includeのみを使用したときと同じように動作します。

  • roles/x/tasks/main.yml
  • roles/x/handlers/main.yml
  • roles/x/vars/main.yml

後他にもroles/x/(files|templates|handlers)ディレクトリに何かファイルがあれば色々やってくれるそうですが、まだ私自身が試してないので気になる方は公式ドキュメントの説明を読んで自分で確認してみてください。

まあ、要するにRolesに関して理解すればいいのは

  • playbook内で role: x と書くと
  • roles/xディレクトリ配下にある特定のファイル(tasks/main.ymlなど)を自動的にincludeしてくれる!

ということです。基本的には。

今回はxディレクトリに当たる部分がcommonしかなかったですが、commonだけでなく、webserversディレクトリとかdbserversディレクトリとか自分の好きなように作れるので、それぞれサーバー毎に必要なplaybookの固まりをそれぞれのディレクトリの所定の場所に突っ込んでおけば、後はansible-playbookコマンドを使うときに指定するplaybook内に role: webserversとかrole: dbserversと書くだけで自動的にincludeされるので、自分でディレクトリ構成を考える必要も無く、記述も楽になるので大変オススメだ、ということです。

…長々と書いたせいか、理解できるように書けていない気がするのですが、もうこれ以上書くのも疲れたし分かりやすく書ける気がしないので、冒頭で紹介したリポジトリをcloneでもして実際に動かしてみるといいと思います(と宣伝)。

Ansibleはこの6/10にv1.2をリリース予定とのことなので、v1.2が来たらわざわざgithubから取ってきてインストールするなどといった面倒臭いことはしなくてすむのでよかったですね!

…よし、これでだいたい言いたいことは終わった−。

注意事項

2013/06/04 13:57現在、github上のAnsibleを使うと以下のような問題がありますので気をつけてください。

Ansible学習の参考になりそうな新たな記事リンク集

以上です。書くの疲れました。

*1:というかブランチ名、roles-sampleにすればよかった…

*2:説明能力が無いという理由も大きいですがね!

*3:こういう説明でいいのか自信ない…