双六工場日誌

平凡な日常を淡々と綴ります。

Ansibleを使い出す前に押さえておきたかったディレクトリ構成のベストプラクティス

Ansibleのディレクトリ構成を決める際、プロダクション環境、ステージング環境、開発環境といった環境ごとに異なる設定を変更する方法でしっくり来るものを思いつかず、どうしたものかと悩んでいたのですが、今日見つけたブログ記事でそれもスッキリ解消したのでメモっておきます。

結論

まず結論を。プロダクション環境、ステージング環境、開発環境といった環境ごとに異なる設定する場合は、以下のように対応するのが良さそうです。

  • ディレクトリ構成は、公式ドキュメントに従う。

Best Practices — Ansible Documentation

  • プロダクション、ステージング、開発など、ステージごとの変数切替は以下のブログを参考に、"group_vars"を利用して行う。
    • インベントリファイルの中に、"[production:children]"のようなグループすべてが属するグループを作ってしまい、そのグループに対応するファイル("group_vars/production"など)を作成する。

Multistage environments with Ansible – Ross Tuck

同じ悩みを持っていて、ここまでの内容とリンク先を見て納得した人は以上で、このエントリで伝えたかったことは終わりです。

悩んでいたこと

これだけではあっさりしすぎなので、悩んでいた内容も書いておきます。

Ansible公式のディレクトリ構成のベストプラクティス

Ansibleの公式ドキュメントの「Best Practice」には、以下のようなディレクトリ構成が掲載されています。

ポイントは、以下のような感じ。

  • 役割ごとにPlaybookは1つ。”site.yml"は、サイト全体のPlaybookで、役割ごとのPlaybookをincludeするのみ
  • プロダクション、ステージングなど、環境ごとにインベントリファイルを作成する
  • タスクの中身は、roleに書き、役割ごとに用意したPlaybookで利用する。
  • 変数は、group_vars, host_varsで設定(ただし、host_varsの利用はできるだけ避ける)

Best Practices — Ansible Documentation

The top level of the directory would contain files and directories like so:

production                # inventory file for production servers
stage                     # inventory file for stage environment

group_vars/
   group1                 # here we assign variables to particular groups
   group2                 # ""
host_vars/
   hostname1              # if systems need specific variables, put them here
   hostname2              # ""

library/                  # if any custom modules, put them here (optional)
filter_plugins/           # if any custom filter plugins, put them here (optional)

site.yml                  # master playbook
webservers.yml            # playbook for webserver tier
dbservers.yml             # playbook for dbserver tier

roles/
    common/               # this hierarchy represents a "role"
        tasks/            #
            main.yml      #  <-- tasks file can include smaller files if warranted
        handlers/         #
            main.yml      #  <-- handlers file
        templates/        #  <-- files for use with the template resource
            ntp.conf.j2   #  <------- templates end in .j2
        files/            #
            bar.txt       #  <-- files for use with the copy resource
            foo.sh        #  <-- script files for use with the script resource
        vars/             #
            main.yml      #  <-- variables associated with this role
        defaults/         #
            main.yml      #  <-- default lower priority variables for this role
        meta/             #
            main.yml      #  <-- role dependencies

    webtier/              # same kind of structure as "common" was above, done for the webtier role
    monitoring/           # ""
    fooapp/               # ""

環境ごとの設定の切替方法で悩む

さて、ここで困ったのが、環境ごとの設定の切替方法。公式ドキュメントの該当部分には、以下の記述の通り、ステージングとプロダクションのインベントリファイルを分けろと書いてあるだけで、それぞれに紐づく変数設定の方法は書かれていませんでした。

Stage vs Production

As also mentioned above, a good way to keep your stage (or testing) and production environments separate is to use a separate inventory file for stage and production. This way you pick with -i what you are targeting. Keeping them all in one file can lead to surprises!

そのほかのドキュメントを見てみると、インベントリファイルに変数を書いておけるということがわかったので、とりあえず、自分で取った方法は以下のような感じ。

[web-servers]
www1.example.com
www2.example.com

…

[all:vars]
sample_var: 1
vars_file: production_vars.yml

こうしておくと、このインベントリファイルを読み込んだ際、"[all:vars]"以下に書かれている変数が設定されます。設定ファイルが長くなる場合は、外出しのファイルに書いて、各Playbookで

vars_files:
  - "{{ vars_file }}"

みたいな形で、読み込むようにしていました。

ただ、これだと「Best Practice」に書かれているディレクトリ構成にはないものができてしまい、Playbook間の変数の依存関係も複雑になってしまうところが今ひとつ。。。

ブログで見つけた解決策

そこで見つけたのが以下のブログです。こちらには、group_varsとインベントリファイルを連携させるスッキリとした環境別設定のほうほうが書かれていました。

Multistage environments with Ansible – Ross Tuck

具体的なインベントリファイルの書き方は以下のようなもの。

[web-servers]
www1.example.com
www2.example.com

[db-servers]
db1.example.com
db2.example.com
…

[production:children]
web-servers
db-servers

ポイントは、最後に"[production:children]"のセクションに、"web-servers", "db-servers"のグループを書いていること、こうすることでこれらのグループ内のホストに、"group_vars/production"で設定した変数が適用されるのです!

この"[グループ名:children]"という書き方は、複数のグループを大きな1つのまとめる、インベントリファイルの記法です。ここでまとめられたグループにもgroup_varsの設定が適用されます。この仕組み自体はわかっていたものの、このような使い方があるとは!?

これを踏まえて先ほどの「Best Practice」を見てみると、「Group And Host Variables」のセクションのところに、このような使い方を示唆する記述がありますが、これを環境ごとの変数切替に使うというのは、自分では思いつかなかったです。。。

ともあれ、これでスッキリとしたAnsible Playbookのディレクトリ構成ができそうです。公式のベストプラクティスを読み込んで、内容を理解しておくのは重要ですね。

変数設定は一度作ってしまうと、変更が面倒なんですよね。最初にこの構成にたどり着いていればよかったです。既存のものはどうしますかね。。。

おまけ

途中に出てきたgroup_varsのファイル名のTipsですが、以下の2つのファイル名のファイルは同じく"production"グループで読み出されます。エディタで編集する際、タブ幅やシンタックスハイライトがつけたい場合は、".yml"をつけたものを使うと捗ります。

  • group_vars/production
  • group_vars/production.yml

おまけ2

Ansibleを始める際、YAMLの仕様はこのページが非常に参考になりました。せっかくAnsibleの記事を書いたので、本文と直接関係ないですが、このリンクもつけておきます。

Rubyist Magazine - プログラマーのための YAML 入門 (初級編)

今日はこの辺で。

おまけ3

思った以上に見てくれている人がいるようなので、さらに追記。

ベストプラクティスに基づいたPlaybookの公式サンプルが以下のリポジトリで公開されています。基本的な構成だけのシンプルなものが多いですが、Playbookを書き始める際には非常に参考になると思います。

ansible/ansible-examples · GitHub

アプリデプロイに関する追記(2015/01/16)

Ansibleで構成管理に加えて、CapistranoやFabricでやるようなアプリデプロイまで取り込む際には、これらのブログの記事が参考になりました。記事の本筋からそれますが、Ansibleのベストプラクティスを考える上で重要な内容だと思ったので、こちらも追記。

構成管理とアプリデプロイまで、一貫して管理できるのはAnsibleの強みの一つですね。