Ansibleプロジェクトのディレクトリ構成

Ansibleでコンフィギュレーションするのに必要なファイル構成についておさらいする。また公式のベストプラクティスを踏まえてディレクトリ構成も提案する。 これはローカルに残っていたメモを手直ししたものです。

本題

デプロイは playbook, role, inventory, configの4つで構成される。 config にはデプロイ作業で使うユーザーやsshの設定などが書かれている。他の3つが対象システムを定義している。

inventory がサーバーといったシステムリソースとシステム内での役割(スタック)の対応を定義します。

playbookでシステムコンポーネントを記載します。 roleは複数のシステムコンポーネントで使いまわせるように独立させた部分です。roleという形に独立させることでメンテナンスしやすくします。

この2つは作業 (task)と変数 (variable) を組み合わせて書きます。 このうち変数(variable)は環境などで切り替えためか、前述のinventoryの中であったり group_vars ディレクトリであったりと色々なところで設定できます。そのためチームで方針を準備しておくことが望ましい。

改めてテーブルにまとめると下になります。

concept 役割
inventory インフラとスタック(グループ)の対応づけを定義する
playbook スタックを主にロール(role)の組み合わせとして定義する
role 汎用的な設定(ミドルウェアとか)をtaskの組み合わせとして定義する

Ansibleでは task のために多くのmoduleが提供されています。 モジュールにパラメータを渡して task を書きます。 そのさいに、変数(Variables)を使ったり値をFilterで加工したりできます。

変数の優先度はVariablesに書かれています。 システムが用意してくれているMagic VariablesについてはQiita記事がまとめてくれています。ただ実装依存の部分も多く debug モジュールや ansible -m setup コマンドなどで確認しながら使う必要があると考えています。

ここまでAnsibleについておさらいしました。

どのようなインフラを用いるか? どのようなスタックでサービスを提供するか? なにが汎用的なコンフィギュレーションか? といった部分は提供するシステムやインフラ選定で決まってきます。 ここではコンフィギュレーションをどう記述するかについて焦点を当てます。この問題で私は変数の管理をどうするか? が主な問題になると感じました。

いまのディレクトリ構成

変数の定義について方針を立て関連するディレクトリ配置を提案しておきます。暫定なので他の問題意識や考え直すこともあると思っています。 公式のベストプラクティスと比較した特徴は inventories, group_vars というディレクトリがプロジェクトルートに置かれているところです。 これには次の動機がありました。

  • 複数のインフラそれぞれで複数の環境を提供したかった
  • ダイナミカルインベントリを使いたかった
  • インベントリ定義をプロジェクトルートにたくさん置きたくなかった
  • スタックに関わる変数とインフラから来る変数と整理したかった

とりあえず構成は下のようになります。

inventories/
   ec2/
      ec2.py              # dynamical inventory for ec2
      hosts               # inventory file defines how to map ec2.py's output with logical groups
      group_vars/
         all.yml          # here we assign variables to particular groups
   on-premises/
      hosts               # inventory file for on premises environment
      group_vars/
         all.yml          # here we assign variables to particular groups
   gcp/
      gcp.py              # dynamical inventory for gcp
      hosts               # inventory file for on premises environmentstaging environment
      group_vars/
         all.yml          # here we assign variables to particular groups

group_vars/
   all                    # here we assign variables to particular groups
   prod/
      prod.yml
      prod.sec.yml # encrypted with ansible-vault
   tidb.yml               # "logical group (e.g. tidb)"

site.yml                  # master playbook
group1.yml                # playbook for group1
group2.yml                # playbook for group2

roles/...
scripts/...

長所はサービスレベルのスタックの関係とインフラな変数を分けて管理できるところ。 短所はサービスレベルとインフラレベルを分離できないと煩雑になるところ。変数定義が別の変数定義を参照していて実際の値を確認するのが難しくなるところ。

いまはこれで試しているけど、汚い現実を受け止めきれないかもしれないと感じている。その時は後ろで出てくる公式ベストプラクティスの2つめ(inventoriesディレクトリ配下にそれぞれの環境を置く方式)に変更するつもり。

library/,module_utils/,*_plugins/ といったディレクトリはモジュールやプラグインといった拡張を行うためのディレクトリです。 Developing Modules, Developing Pluginsを参考にしてください。 ymlで頑張りすぎて複雑になるならPythonを書くほうがいいと思っている。

ほかの注意点

実際のデプロイやサーバー構築はMakefile, Packer, シェルスクリプトなどで行うと思う。そのようなスクリプトや定義ファイルは scripts ディレクトリ配下に置く。ただし理由がある時はプロジェクトルートも使う。 またインベントリ、グループ変数(group_var配下の変数定義)は環境変数から受け取れるようにすると楽だと考えている。 特に、環境が設定されていないときにはテスト環境とするのが安全だと思う。

ロールやタスクにはタグを付けておくと部分実行ができて時間の短縮やデバッグが楽になる。 部分実行には ansible-playbook --tags application ./site.yml のように --tags フラグを使います。

1
2
3
4
roles:
  - role: sample
    tags:
    - application

ベストプラクティス

ここでは比較として公式のベストプラクティスをメモする。公式では下のような構成を提示している。

production                # inventory file for production servers
staging                   # inventory file for staging environment

group_vars/
   group1                 # here we assign variables to particular groups
   group2                 # ""

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

site.yml                  # master playbook
group1.yml                # playbook for group1
group2.yml                # playbook for group2

roles/
    role1/                # 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
        library/          # roles can also include custom modules
        module_utils/     # roles can also include custom module_utils
        lookup_plugins/   # or other types of plugins, like lookup in this case
    role2/                # same kind of structure as "role1" was above, done for the webtier role

公式にはもう1つ inventories ディレクトリを作る構成も提示されている。

inventories/
   production/
      hosts               # inventory file for production servers
      group_vars/
         group1.yml       # here we assign variables to particular groups
         group2.yml
      host_vars/
         hostname1.yml    # here we assign variables to particular systems
         hostname2.yml

   staging/
      hosts               # inventory file for staging environment
      group_vars/
         group1.yml       # here we assign variables to particular groups
         group2.yml
      host_vars/
         stagehost1.yml   # here we assign variables to particular systems
         stagehost2.yml

library/
module_utils/
filter_plugins/

site.yml
webservers.yml
dbservers.yml

roles/
    common/
    webtier/
    monitoring/
    fooapp/

このあと

最近はビルド対象とインフラ・コンフィギュレーションを1つのリポジトリに統合するか悩んでいる。 DockerやPackerでイメージビルドするのはどこで行うかなどが整理できていない。 さらにTerraformによるインフラ準備も悩んでいる。全てを1つにできたら単純な反面、ブランチ管理がうまくいくか不安がある。

当面はAnsibleによるプロビジョニングとソフトウェアは別リポジトリで管理して、考えがまとまったら動かすつもり。

comments powered by Disqus