前書き

Ansibleは、さまざまな環境でインフラストラクチャとアプリケーションをセットアップおよび管理するために使用される強力な構成管理システムです。 Ansibleは読みやすい構文、柔軟なワークフロー、強力なツールを提供しますが、展開環境や機能によって異なる場合、多数のホストを管理するのは難しい場合があります。

このガイドでは、Ansibleを使用してマルチステージ展開環境を操作するためのいくつかの戦略について説明します。 通常、さまざまな段階の要件により、コンポーネントの数と構成が異なります。 たとえば、開発サーバーのメモリ要件は、ステージングおよび実稼働用のメモリ要件とは異なる場合があり、それらの要件を表す変数の優先順位を明示的に制御することが重要です。 この記事では、これらの違いを抽象化できるいくつかの方法と、構成の再利用を促進するためにAnsibleが提供するいくつかのコンストラクトについて説明します。

Ansibleを使用したマルチステージ環境を管理するための不完全な戦略

Ansible内の環境を管理できる方法は多数ありますが、Ansible自体は、十分に評価されたソリューションを提供していません。 むしろ、環境の管理に使用できる多くの構成要素を提供し、ユーザーが選択できるようにします。

このガイドで説明するアプローチは、Ansible *グループ変数*および*複数のインベントリ*に依存しています。 ただし、検討する価値のある他の戦略がいくつかあります。 これらのアイデアの一部と、複雑な環境に実装した場合に問題が発生する理由について説明します。

Ansibleの推奨戦略を開始する場合は、https://www.digitalocean.com/community/tutorials/how-to-manage-multistage-environments-with-ansible#ansible-recommended-strategyのセクションに進んでください。 -using-groups-and-multiple-inventories [Ansibleグループと複数のインベントリを使用]。

グループ変数のみに依存

一見すると、グループ変数はAnsibleが必要とする環境間のすべての分離を提供するように見えるかもしれません。 特定のサーバーを開発環境に属するサーバーとして指定したり、他のサーバーをステージング領域や実稼働領域に割り当てることができます。 Ansibleを使用すると、グループを簡単に作成して変数を割り当てることができます。

ただし、グループ交差は、このシステムに深刻な問題をもたらします。 グループは、複数のディメンションを分類するためによく使用されます。 例えば:

  • デプロイメント環境(ローカル、開発、ステージ、製品など)

  • ホスト機能(Webサーバー、データベースサーバーなど)

  • データセンター地域(NYC、SFOなど)

これらの場合、ホストは通常​​、カテゴリごとに1つのグループになります。 たとえば、ホストは、NYC(データセンター地域)のステージ(展開環境)のWebサーバー(機能)である場合があります。

同じ変数がホストの複数のグループによって設定されている場合、Ansibleには優先順位を明示的に指定する方法がありません。 展開環境に関連付けられた変数を使用して他の値を上書きすることもできますが、Ansibleではこれを定義する方法を提供していません。

代わりに、* Ansibleは最後に読み込まれた値を使用します*。 Ansibleはグループをアルファベット順に評価するため、辞書の順序で最後になったグループ名に関連付けられた変数が優先されます。 これは予測可能な動作ですが、グループ名のアルファベット順の明示的な管理は、管理の観点からは理想的ではありません。

グループの子を使用して階層を確立する

Ansibleでは、インベントリで `+ [:children] +`構文を使用して他のグループにグループを割り当てることができます。 これにより、特定のグループを他のグループのメンバーに名前を付けることができます。 子グループには、親グループによって設定された変数をオーバーライドする機能があります。

通常、これは自然分類に使用されます。 たとえば、グループ「+ dev」、「+ stage」、「+ prod」を含む「+ environments」というグループを作成できます。 これは、変数を「+ environment 」グループに設定し、「 dev 」グループでオーバーライドできることを意味します。 同様に、グループ「 web」、「+ database」、および「+ load balancer」を含む「+ functions +」という親グループを作成できます。

子グループは親のみをオーバーライドするため、この使用法はグループの交差の問題を解決しません。 子グループは親内の変数をオーバーライドできますが、上記の組織は、「+ environments 」や「 functions +」などのグループカテゴリ間の関係を確立していません。 2つのカテゴリ間の変数の優先順位は未定義です。

非自然なグループメンバーシップを設定することにより、このシステムを悪用することができます。 たとえば、最高の優先度から最低の優先度まで、次の優先順位を設定する場合:

  • 開発環境

  • 領域

  • 関数

次のようなグループメンバーシップを割り当てることができます。

在庫の例

. . .
[function:children]
web
database
loadbalancer


[region:children]
nyc
sfo


[environments:children]
dev
stage
prod

ここでは、「+ region 」グループは「 function 」グループの子であるため、地域変数が機能変数をオーバーライドできる階層を確立しました。 同様に、 ` environments `グループに設定された変数は、他のすべてをオーバーライドできます。 つまり、同じ変数を「 dev 」、「 nyc 」、および「 web 」グループの異なる値に設定すると、これらのそれぞれに属するホストは「 dev +」の変数を使用します。

これにより、望ましい結果が得られ、予測可能です。 しかし、それは直感的ではなく、真の子と階層を確立するために必要な子の区別を混乱させます。 Ansibleは、その構成が明確で、新しいユーザーでも簡単に理解できるように設計されています。 このタイプの回避策は、その目標を妥協します。

明示的な読み込み順序を許可するAnsibleコンストラクトの使用

Ansibleには、明示的な変数のロード順序を許可するいくつかの構成要素、つまり `+ vars_files `と ` include_vars `があります。 これらはhttps://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks[Ansible plays]内で使用して、ファイル内で定義された順序で追加の変数を明示的にロードできます。 ` vars_files `ディレクティブはplayのコンテキスト内で有効ですが、 ` include_vars +`モジュールはタスクで使用できます。

一般的な考え方は、 `+ group_vars +`に基本的な識別変数のみを設定し、これらを活用して、残りの必要な変数とともに正しい変数ファイルをロードすることです。

たとえば、いくつかの `+ group_vars +`ファイルは次のようになります。

group_vars / dev

---
env: dev

group_vars / stage

---
env: stage

group_vars / web

---
function: web

group_vars / database

---
function: database

次に、各グループの重要な変数を定義する個別のvarsファイルを作成します。 これらは通常、明確にするために別個の `+ vars `ディレクトリに保持されます。 ` group_vars `ファイルとは異なり、 ` include_vars `を扱う場合、ファイルには ` .yml +`ファイル拡張子を含める必要があります。

それぞれの + vars`ファイルで + server memory size`変数を異なる値に設定する必要があるとしましょう。 開発サーバーは、運用サーバーよりも小さくなる可能性があります。 さらに、Webサーバーとデータベースサーバーのメモリ要件が異なる場合があります。

vars / dev.yml

---
server_memory_size: 512mb

vars / prod.yml

---
server_memory_size: 4gb

vars / web.yml

---
server_memory_size: 1gb

vars / database.yml

---
server_memory_size: 2gb

次に、 `+ group_vars `ファイルからホストに割り当てられた値に基づいて、正しい ` vars +`ファイルを明示的にロードするプレイブックを作成できます。 ロードされたファイルの順序が優先順位を決定し、最後の値が優先されます。

`+ vars_files +`を使用すると、プレイの例は次のようになります。

example_play.yml

---
- name: variable precedence test
 hosts: all
 vars_files:
   - "vars/{{ env }}.yml"
   - "vars/{{ function }}.yml"
 tasks:
   - debug: var=server_memory_size

機能グループは最後に読み込まれるため、 `+ server_memory_size `の値は ` var / web.yml `および ` var / database.yml +`ファイルから取得されます。

ansible-playbook -i inventory example_play.yml
Output. . .
TASK [debug] *******************************************************************
ok: [host1] => {
   "server_memory_size":
}
ok: [host2] => {
   "server_memory_size":
}
ok: [host3] => {
   "server_memory_size":
}
ok: [host4] => {
   "server_memory_size":
}
. . .

ロードするファイルの順序を切り替えると、デプロイメント環境変数の優先度を高くすることができます。

example_play.yml

---
- name: variable precedence test
 hosts: all
 vars_files:
   - "vars/{{ function }}.yml"
   - "vars/{{ env }}.yml"
 tasks:
   - debug: var=server_memory_size

プレイブックを再度実行すると、デプロイメント環境ファイルから適用されている値が表示されます。

ansible-playbook -i inventory example_play.yml
Output. . .
TASK [debug] *******************************************************************
ok: [host1] => {
   "server_memory_size":
}
ok: [host2] => {
   "server_memory_size":
}
ok: [host3] => {
   "server_memory_size":
}
ok: [host4] => {
   "server_memory_size":
}
. . .

タスクとして動作する `+ include_vars +`を使用した同等のプレイブックは次のようになります。

---
- name: variable precedence test
 hosts: localhost
 tasks:
   - include_vars:
       file: "{{ item }}"
     with_items:
       - "vars/{{ function }}.yml"
       - "vars/{{ env }}.yml"
   - debug: var=server_memory_size

これは、Ansibleが明示的な順序付けを許可する1つの領域であり、非常に便利です。 ただし、前の例と同様に、いくつかの重大な欠点があります。

まず、 `+ vars_files `と ` include_vars `を使用するには、別の場所にあるグループに緊密に関連付けられている変数を配置する必要があります。 ` group_vars `の場所は、 ` vars `ディレクトリにある実際の変数のスタブになります。 これにより、複雑さが増し、明瞭さが低下します。 ユーザーは正しい変数ファイルをホストに一致させる必要があります。これは、Ansibleが ` group_vars +`を使用するときに自動的に実行するものです。

さらに重要なことは、これらの手法に依存することで、これらの手法が必須になることです。 すべてのプレイブックには、正しい変数ファイルを正しい順序で明示的にロードするセクションが必要です。 これがないPlaybookは、関連する変数を使用できません。 さらに、アドホックタスクに対して `+ ansible +`コマンドを実行することは、変数に依存するものではほぼ完全に不可能です。

Ansibleの推奨戦略:グループと複数のインベントリの使用

これまで、マルチステージ環境を管理するためのいくつかの戦略を検討し、それらが完全なソリューションではない理由を説明しました。 ただし、Ansibleプロジェクトでは、複数の環境にわたってインフラストラクチャを抽象化する最善の方法についていくつかの提案があります。

推奨されるアプローチは、各オペレーティング環境を完全に分離することにより、マルチステージ環境で作業することです。 単一のインベントリファイル内ですべてのホストを維持する代わりに、個々の環境ごとにインベントリが維持されます。 個別の `+ group_vars +`ディレクトリも維持されます。

基本的なディレクトリ構造は次のようになります。

.
├── ansible.cfg
├── environments/         # Parent directory for our environment-specific directories
│   │
│   ├── dev/              # Contains all files specific to the dev environment
│   │   ├── group_vars/   # dev specific group_vars files
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # Contains only the hosts in the dev environment
│   │
│   ├── prod/             # Contains all files specific to the prod environment
│   │   ├── group_vars/   # prod specific group_vars files
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # Contains only the hosts in the prod environment
│   │
│   └── stage/            # Contains all files specific to the stage environment
│       ├── group_vars/   # stage specific group_vars files
│       │   ├── all
│       │   ├── db
│       │   └── web
│       └── hosts         # Contains only the hosts in the stage environment
│
├── playbook.yml
│
└── . . .

ご覧のとおり、各環境は明確に区分されています。 環境ディレクトリには、インベントリファイル(任意に「+ hosts 」という名前が付けられます)と別の「 group_vars +」ディレクトリが含まれます。

ディレクトリツリーには明らかな重複がいくつかあります。 個々の環境ごとに `+ web `および ` db `ファイルがあります。 この場合、複製が望ましいです。 コードまたは構成の変更の場合と同様に、テスト後に変数を最初に1つの環境で変更し、次の変数に移動することにより、変数の変更を環境全体に展開できます。 ` group_vars +`変数は、各環境の現在のデフォルトを追跡します。

1つの制限は、環境全体で機能ごとにすべてのホストを選択できないことです。 幸いなことに、これは上記の変数複製の問題と同じカテゴリーに分類されます。 タスクのためにすべてのWebサーバーを選択すると便利な場合がありますが、ほとんどの場合、一度に1つの環境全体に変更をロールアウトする必要があります。 これにより、ミスが実稼働環境に影響するのを防ぐことができます。

クロス環境変数の設定

推奨セットアップでは不可能なことの1つは、環境間での変数の共有です。 クロス環境変数の共有を実装する方法はいくつかあります。 最も簡単な方法の1つは、ファイルの代わりにディレクトリを使用するAnsibleの機能を活用することです。 各「+ group_vars 」ディレクトリ内の「 all 」ファイルを「 all +」ディレクトリに置き換えることができます。

ディレクトリ内で、すべての環境固有の変数をファイルに再度設定できます。 次に、クロス環境変数を含むファイルの場所へのシンボリックリンクを作成できます。 これらの両方は、環境内のすべてのホストに適用されます。

まず、階層のどこかにクロス環境変数ファイルを作成します。 この例では、 `+ environments +`ディレクトリに配置します。 すべてのクロス環境変数をそのファイルに配置します。

cd environments
touch 000_cross_env_vars

次に、 `+ group_vars `ディレクトリのいずれかに移動し、 ` all `ファイルの名前を変更して、 ` all +`ディレクトリを作成します。 名前を変更したファイルを新しいディレクトリに移動します。

cd dev/group_vars
mv all env_specific
mkdir all
mv env_specific all/

次に、クロス環境変数ファイルへのシンボリックリンクを作成できます。

cd all/
ln -s ../../../000_cross_env_vars .

各環境で上記の手順を完了すると、ディレクトリ構造は次のようになります。

.
├── ansible.cfg
├── environments/
│   │
│   ├──
│   │
│   ├── dev/
│   │   ├── group_vars/
│   │   │   ├──
│       │   │   ├──
│   │   │   │   └──
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   ├── prod/
│   │   ├── group_vars/
│   │   │   ├──
│   │   │   │   ├──
│   │   │   │   └──
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   └── stage/
│       ├── group_vars/
│       │   ├──
│       │   │   ├──
│       │   │   └──
│       │   ├── db
│       │   └── web
│       └── hosts
│
├── playbook.yml
│
└── . . .

`+ 000_cross_env_vars +`ファイル内で設定された変数は、優先度の低い各環境で利用できます。

デフォルト環境インベントリの設定

`+ ansible.cfg +`ファイルでデフォルトのインベントリファイルを設定することができます。 これにはいくつかの理由があります。

最初に、明示的なインベントリフラグを「+ ansible」と「+ ansible-playbook」に残すことができます。 だからタイプする代わりに:

ansible -i environments/dev -m ping

次のように入力して、デフォルトのインベントリにアクセスできます。

ansible -m ping

第二に、デフォルトのインベントリを設定すると、不必要な変更がステージング環境や実稼働環境に誤って影響するのを防ぐことができます。 開発環境にデフォルト設定することにより、最も重要でないインフラストラクチャが変更の影響を受けます。 変更を新しい環境にプロモートすることは、 `+ -i +`フラグを必要とする明示的なアクションです。

デフォルトのインベントリを設定するには、 `+ ansible.cfg `ファイルを開きます。 これは、設定に応じてプロジェクトのルートディレクトリまたは ` / etc / ansible / ansible.cfg +`にあります。

nano ansible.cfg

上記のように、開発環境をデフォルトのインベントリとして設定することをお勧めします。 含まれているhostsファイルではなく、環境ディレクトリ全体を選択する方法に注意してください。

[defaults]
inventory = ./environments/dev

これで、 `+ -i `オプションなしでデフォルトのインベントリを使用できるはずです。 デフォルト以外のインベントリには、「-i +」を使用する必要があります。これにより、偶発的な変更から保護されます。

結論

この記事では、複数の環境にわたってホストを管理するためにAnsibleが提供する柔軟性を検討しました。 これにより、ユーザーがホストが複数のグループのメンバーである場合、変数の優先順位を処理するためのさまざまな戦略を採用できますが、あいまいさと公式の方向性の欠如は困難な場合があります。 他のテクノロジーと同様に、組織に最適なものは、ユースケースと要件の複雑さに依存します。 ニーズに合った戦略を見つける最良の方法は、実験することです。 以下のコメントでユースケースとアプローチを共有してください。