Ansibleで多段階環境を管理する方法
序章
Ansibleは、さまざまな環境でインフラストラクチャとアプリケーションをセットアップおよび管理するために使用される強力な構成管理システムです。 Ansibleは、読みやすい構文、柔軟なワークフロー、および強力なツールを提供しますが、展開環境や機能によって異なる場合、多数のホストを管理するのは難しい場合があります。
このガイドでは、Ansibleを使用して多段階のデプロイメント環境で作業するためのいくつかの戦略について説明します。 通常、さまざまなステージの要件により、コンポーネントの数と構成が異なります。 たとえば、開発サーバーのメモリ要件は、ステージングおよび本番環境のメモリ要件とは異なる場合があり、これらの要件を表す変数の優先順位を明示的に制御することが重要です。 この記事では、これらの違いを抽象化できるいくつかの方法と、構成の再利用を促進するためにAnsibleが提供するいくつかの構成について説明します。
Ansibleで多段階環境を管理するための不完全な戦略
Ansible内の環境を管理する方法はいくつかありますが、Ansible自体は独創的なソリューションを提供していません。 むしろ、環境の管理に使用できる多くの構成を提供し、ユーザーが選択できるようにします。
このガイドで説明するアプローチは、Ansibleグループ変数および複数のインベントリに依存しています。 ただし、検討する価値のある他のいくつかの戦略があります。 以下では、これらのアイデアのいくつかと、複雑な環境で実装したときに問題が発生する理由について説明します。
Ansibleの推奨戦略を開始したい場合は、Ansibleグループと複数のインベントリの使用のセクションに進んでください。
グループ変数のみに依存する
一見すると、グループ変数はAnsibleが必要とする環境間のすべての分離を提供しているように見えるかもしれません。 特定のサーバーを開発環境に属するものとして指定し、他のサーバーをステージングおよび実稼働エリアに割り当てることができます。 Ansibleを使用すると、グループを簡単に作成して変数を割り当てることができます。
ただし、グループの交差は、このシステムに深刻な問題をもたらします。 グループは、複数のディメンションを分類するためによく使用されます。 例えば:
- デプロイメント環境(ローカル、開発、ステージ、製品など)
- ホスト機能(Webサーバー、データベースサーバーなど)
- データセンターリージョン(NYC、SFOなど)
このような場合、ホストは通常、カテゴリごとに1つのグループに属します。 たとえば、ホストは、NYC(データセンターリージョン)のステージ(展開環境)上のWebサーバー(機能)である場合があります。
同じ変数がホストの複数のグループによって設定されている場合、Ansibleには優先順位を明示的に指定する方法がありません。 デプロイメント環境に関連付けられた変数を他の値をオーバーライドすることをお勧めしますが、Ansibleはこれを定義する方法を提供していません。
代わりに、Ansibleは最後にロードされた値を使用します。 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
。
これにより、望ましい結果が得られ、予測も可能です。 ただし、これは直感的ではなく、真の子と階層を確立するために必要な子の区別を混乱させます。 Ansibleは、その構成が明確で、新しいユーザーでも簡単に理解できるように設計されています。 このタイプの回避策は、その目標を妥協します。
明示的な読み込み順序を許可するAnsibleコンストラクトの使用
Ansibleには、明示的な可変負荷順序付けを可能にするいくつかの構成要素、つまりvars_files
とinclude_vars
があります。 これらをAnsiblePlays 内で使用して、ファイル内で定義された順序で追加の変数を明示的にロードできます。 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
ファイル拡張子を含める必要があります。
server_memory_size
変数を各vars
ファイルで異なる値に設定する必要があるとしましょう。 開発サーバーは、実稼働サーバーよりも小さい可能性があります。 さらに、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が自動的に行うことです。
さらに重要なことに、これらの手法に依存すると、これらの手法が必須になります。 すべてのプレイブックには、正しい変数ファイルを正しい順序で明示的にロードするセクションが必要です。 これがないと、関連する変数を使用できなくなります。 さらに、アドホックタスクに対して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
にある可能性があります。
注:次の例は、プロジェクトディレクトリ内のansible.cfg
ファイルの編集を示しています。 変更に/etc/ansibile/ansible.cfg
ファイルを使用している場合は、以下の編集パスを変更してください。 /etc/ansible/ansible.cfg
を使用する場合、インベントリが/etc/ansible
ディレクトリの外部に保持されている場合は、inventory
値を設定するときに、相対パスではなく絶対パスを使用してください。
nano ansible.cfg
上記のように、開発環境をデフォルトのインベントリとして設定することをお勧めします。 含まれているhostsファイルではなく、環境ディレクトリ全体を選択する方法に注目してください。
[defaults] inventory = ./environments/dev
これで、-i
オプションなしでデフォルトのインベントリを使用できるようになります。 デフォルト以外のインベントリでは、-i
を使用する必要があります。これにより、偶発的な変更からインベントリを保護できます。
結論
この記事では、Ansibleが複数の環境でホストを管理するために提供する柔軟性について説明しました。 これにより、ホストが複数のグループのメンバーである場合に、ユーザーは変数の優先順位を処理するためのさまざまな戦略を採用できますが、あいまいさや公式の方向性の欠如は難しい場合があります。 他のテクノロジーと同様に、組織に最適なものは、ユースケースと要件の複雑さによって異なります。 ニーズに合った戦略を見つける最良の方法は、実験することです。 以下のコメントでユースケースとアプローチを共有してください。