前書き
Ansibleは、さまざまな環境でインフラストラクチャとアプリケーションをセットアップおよび管理するために使用される強力な構成管理システムです。 Ansibleは読みやすい構文、柔軟なワークフロー、強力なツールを提供しますが、展開環境や機能によって異なる場合、多数のホストを管理するのは難しい場合があります。
このガイドでは、Ansibleを使用してマルチステージ展開環境を操作するためのいくつかの戦略について説明します。 通常、さまざまな段階の要件により、コンポーネントの数と構成が異なります。 たとえば、開発サーバーのメモリ要件は、ステージングおよび実稼働用のメモリ要件とは異なる場合があり、それらの要件を表す変数の優先順位を明示的に制御することが重要です。 この記事では、これらの違いを抽象化できるいくつかの方法と、構成の再利用を促進するためにAnsibleが提供するいくつかのコンストラクトについて説明します。
Ansibleを使用したマルチステージ環境を管理するための不完全な戦略
Ansible内の環境を管理できる方法は多数ありますが、Ansible自体は、十分に評価されたソリューションを提供していません。 むしろ、環境の管理に使用できる多くの構成要素を提供し、ユーザーが選択できるようにします。
このガイドで説明するアプローチは、Ansiblegroup variablesとmultiple inventoriesに依存しています。 ただし、検討する価値のある他の戦略がいくつかあります。 これらのアイデアの一部と、複雑な環境に実装した場合に問題が発生する理由について説明します。
Ansibleの推奨戦略を開始したい場合は、using Ansible groups and multiple inventoriesのセクションに進んでください。
グループ変数のみに依存
一見すると、グループ変数はAnsibleが必要とする環境間のすべての分離を提供するように見えるかもしれません。 特定のサーバーを開発環境に属するサーバーとして指定したり、他のサーバーをステージング領域および実稼働領域に割り当てることができます。 Ansibleを使用すると、グループを簡単に作成して変数を割り当てることができます。
ただし、グループ交差は、このシステムに深刻な問題をもたらします。 グループは、複数のディメンションを分類するためによく使用されます。 例えば:
-
デプロイメント環境(ローカル、開発、ステージ、製品など)
-
ホスト機能(Webサーバー、データベースサーバーなど)
-
データセンター地域(NYC、SFOなど)
これらの場合、ホストは通常、カテゴリごとに1つのグループになります。 たとえば、ホストは、NYC(データセンター地域)のステージ(展開環境)のWebサーバー(機能)である場合があります。
同じ変数がホストの複数のグループによって設定されている場合、Ansibleには優先順位を明示的に指定する方法がありません。 展開環境に関連付けられた変数を使用して他の値を上書きすることもできますが、Ansibleではこれを定義する方法を提供していません。
代わりに、Ansible uses the last loaded value。 Ansibleはグループをアルファベット順に評価するため、辞書の順序で最後になったグループ名に関連付けられた変数が優先されます。 これは予測可能な動作ですが、グループ名のアルファベット順の明示的な管理は、管理の観点からは理想的ではありません。
グループの子を使用して階層を確立する
Ansibleを使用すると、インベントリで[groupname:children]
構文を使用してグループを他のグループに割り当てることができます。 これにより、特定のグループを他のグループのメンバーに名前を付けることができます。 子グループには、親グループによって設定された変数をオーバーライドする機能があります。
通常、これは自然分類に使用されます。 たとえば、グループdev
、stage
、prod
を含むenvironments
というグループを作成できます。 これは、environment
グループに変数を設定し、dev
グループでそれらをオーバーライドできることを意味します。 同様に、グループweb
、database
、およびloadbalancer
を含むfunctions
という親グループを持つことができます。
子グループは親のみをオーバーライドするため、この使用法はグループの交差の問題を解決しません。 子グループは親内の変数をオーバーライドできますが、上記の組織では、environments
やfunctions
などのグループカテゴリ間の関係は確立されていません。 2つのカテゴリ間の変数の優先順位は未定義のままです。
非自然なグループメンバーシップを設定することにより、このシステムを悪用することは可能です。 たとえば、最高の優先度から最低の優先度まで、次の優先順位を設定する場合:
-
開発環境
-
領域
-
関数
次のようなグループメンバーシップを割り当てることができます。
在庫の例
. . .
[function:children]
web
database
loadbalancer
region
[region:children]
nyc
sfo
environments
[environments:children]
dev
stage
prod
ここでは、region
グループがfunction
グループの子であるため、地域変数が機能変数をオーバーライドできるようにする階層を確立しました。 同様に、environments
グループに設定された変数は、他の変数をオーバーライドできます。 これは、dev
、nyc
、およびweb
グループで同じ変数を異なる値に設定した場合、これらのそれぞれに属するホストがdev
の変数を使用することを意味します。 )s。
これにより、望ましい結果が得られ、予測可能です。 しかし、それは直感的ではなく、真の子と階層を確立するために必要な子の区別を混乱させます。 Ansibleは、その構成が明確で、新しいユーザーでも簡単に理解できるように設計されています。 このタイプの回避策は、その目標を妥協します。
明示的な読み込み順序を許可するAnsibleコンストラクトの使用
Ansibleには、明示的な変数のロード順序を許可するいくつかの構造、つまりvars_files
とinclude_vars
があります。 これらをAnsible plays内で使用して、ファイル内で定義された順序で追加の変数を明示的にロードできます。 vars_files
ディレクティブはプレイのコンテキスト内で有効ですが、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": "1gb" # value from vars/web.yml
}
ok: [host2] => {
"server_memory_size": "1gb" # value from vars/web.yml
}
ok: [host3] => {
"server_memory_size": "2gb" # value from vars/database.yml
}
ok: [host4] => {
"server_memory_size": "2gb" # value from vars/database.yml
}
. . .
ロードするファイルの順序を切り替えると、デプロイメント環境変数の優先度を高くすることができます。
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": "512mb" # value from vars/dev.yml
}
ok: [host2] => {
"server_memory_size": "4gb" # value from vars/prod.yml
}
ok: [host3] => {
"server_memory_size": "512mb" # value from vars/dev.yml
}
ok: [host4] => {
"server_memory_size": "4gb" # value from vars/prod.yml
}
. . .
タスクとして動作する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
ディレクトリにある実際の変数のスタブになります。 これにより、複雑さが増し、明瞭さが低下します。 ユーザーは正しい変数ファイルをホストに一致させる必要があります。これは、group_vars
を使用するときにAnsibleが自動的に行うことです。
さらに重要なことは、これらの手法に依存することで、これらの手法が必須になることです。 すべてのプレイブックには、正しい変数ファイルを正しい順序で明示的にロードするセクションが必要です。 これがない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
ディレクトリの1つに移動し、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/
│ │
│ ├── 000_cross_env_vars
│ │
│ ├── dev/
│ │ ├── group_vars/
│ │ │ ├── all/
│ │ │ ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│ │ │ │ └── env_specific
│ │ │ ├── db
│ │ │ └── web
│ │ └── hosts
│ │
│ ├── prod/
│ │ ├── group_vars/
│ │ │ ├── all/
│ │ │ │ ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│ │ │ │ └── env_specific
│ │ │ ├── db
│ │ │ └── web
│ │ └── hosts
│ │
│ └── stage/
│ ├── group_vars/
│ │ ├── all/
│ │ │ ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│ │ │ └── env_specific
│ │ ├── 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
にある可能性があります。
[.note]#Note:以下の例は、プロジェクトディレクトリ内のansible.cfg
ファイルの編集を示しています。 変更に/etc/ansibile/ansible.cfg
ファイルを使用している場合は、以下の編集パスを変更してください。 /etc/ansible/ansible.cfg
を使用するときに、インベントリが/etc/ansible
ディレクトリの外部に保持されている場合は、inventory
値を設定するときに、相対パスではなく絶対パスを使用してください。
#
nano ansible.cfg
上記のように、開発環境をデフォルトのインベントリとして設定することをお勧めします。 含まれているhostsファイルではなく、環境ディレクトリ全体を選択する方法に注意してください。
[defaults]
inventory = ./environments/dev
これで、-i
オプションなしでデフォルトのインベントリを使用できるようになります。 デフォルト以外のインベントリでは、-i
を使用する必要があります。これにより、偶発的な変更からインベントリを保護できます。
結論
この記事では、複数の環境にわたってホストを管理するためにAnsibleが提供する柔軟性を検討しました。 これにより、ユーザーがホストが複数のグループのメンバーである場合、変数の優先順位を処理するためのさまざまな戦略を採用できますが、あいまいさと公式の方向性の欠如は困難な場合があります。 他のテクノロジーと同様に、組織に最適なものは、ユースケースと要件の複雑さに依存します。 ニーズに合った戦略を見つける最良の方法は、実験することです。 以下のコメントでユースケースとアプローチを共有してください。