大規模Ansibleデプロイメント最適化のベストプラクティス
フォーク、ストラテジープラグイン、ファクトキャッシング、SSH再利用、プレイブック設計の改善による大規模Ansible実行の高速化手法。
大規模Ansibleデプロイメント最適化のベストプラクティス
大規模なAnsibleデプロイメントは、SSHハンドシェイクの多さ、ファクト収集の過剰、コントローラのCPU不足、または最も遅いホストを待たせるプレイブックなど、単純な理由で遅くなることがよくあります。修正は一つの設定だけでは不十分です。接続オーバーヘッドの削減、並行性の調整、ホストごとの作業量を減らすプレイの作成により、最良の結果が得られます。
「大規模」を厳密なホスト数で定義するつもりはありません。300ホストのインベントリでも、各タスクが低速リンク経由でパッケージをインストールする場合、大規模に感じられます。3,000ホストのインベントリでも、コントローラが適切にサイジングされ、プレイブックがタイトであれば管理可能です。以下の数値は出発点として扱い、実際のインベントリとモジュールで測定してください。
並列性を調整してからAnsibleを非難する
並列性は通常、最初に試すべきレバーです。Ansibleはリモートホストの待機に多くの時間を費やすからです。目標は「フォーク数が多ければ勝ち」ではありません。目標は、SSH、特権昇格、パッケージリポジトリ、ターゲット自体を圧迫せずに、コントローラをビジー状態に保つ十分な並行性を実現することです。
forksによる並行性の制御
forksパラメータは、Ansibleコントローラが生成できる並列プロセスワーカーの数を定義します。最適な数を見つけるには、コントローラのリソース(CPUとメモリ)とターゲット環境の接続制限のバランスを取る必要があります。
ansible.cfgまたはコマンドライン(-fまたは--forks)でforksを設定します。
[defaults]
forks = 100
必要と思われるよりも低い値から始めてください。同じプレイを同じホストグループに対して、25、50、100、200のフォークで実行し、CPU、メモリ、SSH障害、実行時間を監視します。CPUがほとんどアイドル状態でホストが待機時間を費やしている場合は、フォークを増やします。コントローラがスワップを開始したり、Pythonプロセスが蓄積したり、ターゲットが接続を拒否したりした場合は、減らします。
適切なストラテジープラグインの選択
Ansibleのデフォルトの実行ストラテジーはlinearで、タスクがすべての対象ホストで完了してから、プレイブックの次のタスクに進む必要があります。数千のノードの場合、1台の遅いホストが実行全体のボトルネックになる可能性があります。
大規模なデプロイメントでは、freeストラテジーを使用します。
Freeストラテジー(strategy = free):
freeを使用すると、ホストはタスクを完了するとすぐにプレイブックを独立して進めることができ、遅いホストを待つ必要がありません。タスクが独立している場合、スループットが向上します。ローリングデプロイ、共有マイグレーション、またはフリート全体でのタスク順序が重要なプレイでは、盲目的に使用しないでください。
# プレイブック定義の例
---
- hosts: all
strategy: free
tasks:
- name: サービスが実行中であることを確認
ansible.builtin.service:
name: httpd
state: started
ファクトを再利用する場合はキャッシュする
ファクト収集は便利ですが、繰り返しコストがかかりやすいです。プレイブックが複数回の実行でファクトを使用する場合は、キャッシュしてください。プレイがホストファクトをまったく必要としない場合は、そのプレイの収集を無効にします。
外部キャッシュの使用(RedisまたはMemcached)
単一のコントローラの場合、JSONファイルキャッシュで十分な場合があります。複数のコントローラまたは自動化ワーカーの場合は、RedisやMemcachedなどの外部キャッシュを使用して、すべてのワーカーが同じファクトキャッシュを参照するようにします。
ansible.cfgでの設定例:
[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 7200 ; ファクトを2時間キャッシュ(秒単位)
fact_caching_prefix = ansible_facts
; Redisを使用する場合
fact_caching_connection = localhost:6379:0
キャッシュされたファクトがワークフローの一部である場合は、gathering = smartを設定します。ホストデータの一部のみが必要な場合は、すべてを収集する代わりにgather_subsetを使用します。
3. 接続とトランスポートの最適化
数千の同時SSHセッションを処理する場合、接続確立のオーバーヘッドを削減することが最も重要です。
SSHパイプライン
パイプラインは、Ansibleが多くのモジュール実行に使用するSSHラウンドトリップの数を減らします。有効にする価値はありますが、特権昇格ルールでテストしてください。
SSH接続の再利用(ControlPersist)
Unix系ターゲットの場合、ControlMasterとControlPersistの設定により、Ansibleがタスクごとに新しいSSHセッションを開始するのを防ぎます。指定された期間、制御ソケットを開いたままにし、後続のタスクが既存の接続を使用できるようにします。
ansible.cfgでの設定例:
[ssh_connection]
pipelining = True
; 積極的な接続再利用(例:30分)
ssh_args = -C -o ControlMaster=auto -o ControlPersist=30m -o ServerAliveInterval=15
パイプラインは、TTYを必要とするsudo設定と競合する可能性があります。sudoersにDefaults requirettyがまだある場合は、自動化ユーザー用に削除するか、それらのホストではパイプラインを無効のままにします。
Windowsの最適化(WinRM)
Windowsノードを対象とする場合は、WinRMを個別に調整します。Kerberosは通常、Basic認証よりも本番環境に適しており、多くのジョブが同時に接続する場合は、WinRMサービスの制限を見直す必要があるかもしれません。
4. スケールのためのインベントリ管理
ホストが頻繁に作成および破棄される場合、静的インベントリファイルは扱いにくくなります。動的インベントリはすべての大規模環境で必須ではありませんが、クラウドフリート、オートスケーリンググループ、CMDBバックアップのインフラストラクチャでは適切なデフォルトです。
動的インベントリソース
クラウドプロバイダー(AWS EC2、Azure、Google Cloud)またはCMDBシステム用のインベントリプラグインを活用します。動的インベントリにより、Ansibleが最新のデータを持つアクティブなホストのみを対象とすることが保証されます。
# 例:動的にフィルタリングされたAWSインベントリに対して実行
ansible-playbook -i aws_ec2.yml site.yml --limit 'tag_Environment_production'
スマートなターゲティングとフィルタリング
絶対に必要な場合を除き、インベントリ全体(hosts: all)に対してプレイブックを実行しないでください。詳細なグループ、制限(--limit)、タグ(--tags)を使用して、実行ターゲットセットを最小限に抑えます。
5. アーキテクチャの考慮事項とコントローラのサイジング
大規模なデプロイメントでは、Ansibleが実行される環境を適切にプロビジョニングする必要があります。
コントローラのサイジング
Ansibleは、並列実行のためにプロセスをフォークする必要があるため、コントローラのリソース、主にCPUとRAMに大きく依存します。
- CPU: フォークが多いほど、通常コントローラでのPython作業が増えます。実際のプレイブック実行中に、ロードアベレージとコアごとの飽和状態を監視します。
- RAM: 各フォークはメモリを消費します。大きなテンプレート、大きな変数、チャットなコールバックプラグインは、メモリ使用量を急速に増加させる可能性があります。
- ストレージI/O: 高速なローカルストレージは、コントローラが多くの一時ファイル、ログ、アーティファクト、ファイルベースのファクトキャッシュエントリを書き込む場合に役立ちます。
自動化プラットフォームの活用
スケジューリング、RBAC、監査証跡、複数の実行ワーカーが必要なチームには、単一のコントロールノードでの長時間のシェルセッションではなく、Ansible Automation PlatformまたはAWXを使用します。
AAPが提供するもの:
- ジョブスケジューリングと履歴: 集中ログと監査。
- 実行環境: 一貫性のある再現可能なランタイム環境。
- クラスタリングとスケーリング: 複数のワーカーノードに実行を分散し、単一のコントローラに過負荷をかけずに大規模な並行性要件を処理します。
- 資格情報管理: 大規模なシークレットの安全な処理。
6. 効率のためのプレイブック設計
インフラストラクチャが最適化されていても、プレイブックが適切に作成されていないと、パフォーマンスの向上が無効になる可能性があります。
ファクト収集の最小化
キャッシュされたファクトを使用する場合(セクション2)、冗長なファクト収集を積極的に無効にします。
- hosts: web_servers
gather_facts: no # このプレイのファクト収集を無効にする
tasks:
# ... 収集されたシステムファクトに依存しないタスクのみを実行
run_onceとdelegate_toを控えめに使用する
順次または集中的に実行する必要があるタスク(例:ローリングデプロイの開始、ロードバランサーの更新)は、run_once: trueとdelegate_to: management_nodeを使用して処理します。これにより、1つのホストのみがアクションを実行する場合の無駄な並列性を回避できます。
バッチ操作を優先する
可能な限り、バッチ操作をネイティブに処理するモジュール(例:パッケージのリストを受け入れるaptやyumなどのパッケージマネージャー)を使用し、loopやwith_itemsを使用して個別のpackageタスクで大きなリストを反復処理しないようにします。
# より良い:リストを使用した1つのパッケージタスク
- name: 必要な依存関係をインストール
ansible.builtin.package:
name:
- nginx
- python3-pip
- firewall
state: present
7. ホスト数だけでなくプレイブックを測定する
Ansibleの実行が遅い場合、さらに設定を変更する前にタイミングを追加します。組み込みのprofile_tasksコールバックは良い最初のステップです。
[defaults]
callbacks_enabled = profile_tasks, timer
代表的なホストグループに対してプレイブックを1回実行し、最も遅いタスクを確認します。ほとんどの時間が1つのパッケージインストール、1つのテンプレートレンダリングステップ、または外部サービスを待機する1つのコマンドに費やされていることがわかるかもしれません。その場合、forksを増やすと、同じボトルネックにさらに圧力がかかります。
再現可能なテストのために、インベントリスライスを安定させます。
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 50
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 100
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 200
合計実行時間、失敗したホスト、コントローラCPU、コントローラメモリ、ターゲット側のSSHまたはsudoエラーを記録します。また、テスト中にパッケージリポジトリやアーティファクトサーバーを監視します。実際の問題が、すべてのホストが過負荷の内部ミラーから同時に同じパッケージをダウンロードしていることである場合、プレイブックはAnsibleの問題のように見えることがあります。
8. 並行性を高める前に作業を減らす
大規模なAnsible実行は、より速く実行するよりも、実行する作業を減らすことで改善されることがよくあります。いくつかの例が繰り返し現れます。
- テンプレートタスクが、小さなインクルードのみが変更された場合でも、すべての実行で大きな設定ファイルをレンダリングする。
- シェルタスクが、値がすでにインベントリにあるにもかかわらず、すべてのホストでディスカバリコマンドを実行する。
- ロールがループ内でパッケージを1つずつインストールする。
- ハンドラが、1回のリロードで十分な場合に、いくつかの無関係なテンプレート変更の後にサービスを再起動する。
可能な場合はshellの代わりにモジュールの冪等性を使用します。常にchangedを報告するシェルコマンドは、数百のホストにわたってハンドラをトリガーし、無害なチェックをローリング再起動に変える可能性があります。commandまたはshellを使用する必要がある場合は、changed_whenとcreatesまたはremovesを慎重に設定します。
- name: アプリケーションディレクトリを一度初期化
ansible.builtin.command: /usr/local/bin/app-init /srv/app
args:
creates: /srv/app/.initialized
この小さなガードは、繰り返しの作業を防ぎ、誤った変更レポートを回避します。
9. リスク管理のためにバッチを使用する
大規模ではパフォーマンスだけが懸念事項ではありません。時には、最速のプレイブックが運用上危険な場合があります。サービスのフリートでは、serialを使用して影響範囲を制御します。
- hosts: app_servers
serial: 10%
max_fail_percentage: 5
tasks:
- name: アプリケーションパッケージをデプロイ
ansible.builtin.package:
name: myapp
state: latest
serialを使用すると、すべてのホストに一度に実行するよりも実行時間が長くなりますが、ロードバランサー、監視、人間が対応する時間を与えます。また、共有依存関係を保護します。パッケージミラー、データベースマイグレーションエンドポイント、シークレットマネージャーは、数千の同時リクエストに耐えられない場合があります。
大規模なAnsibleデプロイメントは、忘れられがちなシステムに負荷をかけます:DNSリゾルバー、パッケージリポジトリ、シークレットストア、ログパイプライン、監視エンドポイント。プレイブックが高いフォーク数でのみ遅くなる場合は、Ansibleを非難する前にこれらの共有サービスを確認してください。
また、コールバック出力を制御します。非常に詳細なログはデバッグ中に役立ちますが、大規模な実行を遅くし、実際の障害を埋もれさせる可能性があります。狭いホストスライスには高い詳細度を使用し、フルフリート実行には通常の出力に戻します。
10. 障害ドメインごとにプレイを分割する
見落とされがちなスケーリングのトリックは、すべての資産を1つのデプロイメントユニットとして扱うのをやめることです。データベースホスト、Webホスト、キュー、キャッシュノードがすべて同じ巨大なプレイに存在する場合、1つの遅いまたは壊れたグループが無関係な作業を遅らせる可能性があります。障害ドメインと依存関係の順序でプレイを分離します。
たとえば、ベースOS設定は広く実行しますが、アプリケーションコードはサービス層ごとにデプロイします。キャッシュノードは独自のプレイで更新します。Webノードをバッチでドレインして再起動します。データベース設定は、追加のチェックとより小さな並行性で適用します。これにより、失敗した部分を再実行する際に、すべてのホストで作業を繰り返すことなく、再試行がより安全になります。
また、所有権が明確になります。サービスを担当するチームは、グローバルな自動化のデフォルトを変更せずに、バッチサイズ、ヘルスチェック、ロールバック動作を調整できます。プレイブックの構造が、実際のインシデントやメンテナンスウィンドウ中にインフラストラクチャが実際に障害を起こす方法と一致している場合、大規模なAnsibleは保守可能なままです。
最も効果的なAnsibleパフォーマンス作業は通常、単純です:SSH接続を再利用し、不要なファクト収集を回避し、forksを適切にサイジングし、プレイブックが繰り返し小さな操作を実行しないようにします。その後、アーキテクチャを検討します。1つのコントローラがクリーンに追いつけない場合は、実行ノード間で作業を分割し、インベントリ、資格情報、ログを再現可能にします。