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

なんか昔のメモが出てきたので少し直して晒す。

tl;dr

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/...

library/,module_utils/,*_plugins/ といったディレクトリはモジュールやプラグインといった拡張を行うためのディレクトリになる。

Developing Modules, Developing Pluginsが参考になる。ymlが複雑になるくらいならPythonを書いたほうがいい。

今回はAnsibleについて記載するが、 可搬性や再現性、そして構築スピードのためにDockerやPackerでイメージを作るのが望ましい。 その場合は、これらを実行するMakefileかシェルスクリプトをトップディレクトリに置く。

AnsibleやTerraformといったデプロイを行うスクリプトなどを準備する際には、 環境変数からパラメータを受け取るようにすると楽になる。 設定されていない場合はテスト環境が構築されると安全で便利。

最近はビルド対象のリポジトリとインフラ・コンフィギュレーションのリポジトリを統一するか悩んでいる。 例えば、Dockerでコンテナビルドするまでが含まれたり、Packerでサーバーイメージをビルドするまでが含まれる。

その場合でも、いまのところはTerraformとAnsibleによるプロビジョニングは別リポジトリに別けるつもり。

Ansibleでの定義ファイル

Ansibleでのデプロイは、playbook, role, inventory, configの4つを書くことが多い。

configはデプロイ作業自体のオプションに関わりシステム定義には関わらない。

残りの3つがデプロイ対象を表現している。playbookroleroleはtask,variableの組み合わせで書かれる。

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

taskはmoduleを使って記述する。 記述には変数(Variables)が使え、 Filterを使って加工なども行える。

ベストプラクティス

Ansibleは自由に書けるので気をつけないと扱いにくい代物ができあがる。 公式では下のような構成を提示している。

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/

これなら gcp, ec2 などの環境を inventories 配下に置いて、そこでインフラとグループの関係を記述できる。 ただし、ベストプラクティスのままだと、環境ごとに group_vars を定義する事になり、グループ間の論理的な依存関係を置く場所がなくなり各環境に向けてたくさん書くはめになる。

そこで最初に示したように、 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/...

library/,module_utils/,*_plugins/ といったディレクトリはモジュールやプラグインといった拡張を行うためのディレクトリになる。

Developing Modules, Developing Pluginsなどに拡張の作り方がある。ymlが複雑になるくらいならPythonを書いたほうがいい。 今のところ必要に駆られていない。

さらに可搬性を上げるためにDockerなどでコンテナ化したりPacker でサーバーイメージを生成してリポジトリに登録するでを実行できる Makefileかシェルスクリプトを準備するのが良い。

パラメータは環境変数から受け取るようにして設定されていない場合はテスト環境が構築されると安全で便利。

変数の利用

変数はほとんどがグローバルスコープでデプロイ中のほとんどの箇所で参照できて名前も衝突しやすいので、変数名の管理や設定箇所の管理はとても重要。 変数の優先度はVariablesに書かれている。

システムが用意してくれているMagic VariablesについてはQiita記事がまとめてくれている。

公式に記載がないため参考程度となる(おそらく*Python*依存な部分が大きい)。 実際には debug モジュールや*ansible -m setup*コマンドなどで確認するのが良い。

公式のベストプラクティスでは、 デプロイ先OSをグループで扱う方法と tasks の中でスクリプトによって include_vars を使って明示的に読み出す方法の2つが書かれている。

タグの利用

下みたいにロールやタスクにタグを付けておくと良い。

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

これにより ansible-playbook --tags application ./site.yml などで適応範囲を限定できる。全部適応すると時間がかかる事が多いのでこれで時間を節約する

comments powered by Disqus