はじめに (対象読者・この記事でわかること)

この記事は、Ansibleを使って複数台のサーバに対して一括で設定変更を行いたいインフラエンジニア・運用エンジニアを対象にしています。特に、本番・ステージング・開発環境を合わせて数十台以上のサーバを管理している方に最適です。

この記事を読むことで、Ansibleのインベントリファイルを使ってサーバを論理的にグループ化し、グループ単位で変数を管理する方法がわかります。また、forksパラメータやserial戦略を使った並列実行のチューニングにより、設定変更の実行時間を大幅に短縮する方法を実践的に学べます。さらに、一部のサーバでエラーが発生した際の対処法や、ローリングアップデートの実装方法も解説します。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。

  • YAMLの基本的な記法(リスト、辞書、変数の定義)
  • SSH公開鍵認証とssh_configの基本的な使い方
  • Ansibleの基本コマンド(ansible-playbookansible-inventory)の実行経験

インベントリ設計が運用の成否を分ける理由

Ansibleで複数サーバを管理する際、最も重要かつ最初に設計すべきがインベントリ構造です。単にIPアドレスを羅列するだけでなく、論理的なグループ分けを行うことで、環境ごと・役割ごとに異なる変数を管理できます。

例えば、Webサーバ群はproduction-webグループ、APサーバ群はproduction-apグループとして定義し、データベースサーバはproduction-dbグループとして分離します。これにより、OSのバージョンアップ作業ではWeb/AP/DBを逐次更新し、設定変更のみであれば全グループ並列実行という柔軟な運用が可能になります。

また、グループ変数とホスト変数を使い分けることで、共通設定と個別設定を明確に分離できます。group_vars/all.ymlに共通パッケージリストを定義し、group_vars/production-web.ymlにApacheのモジュールリストを定義することで、変数の重複を防ぎながら柔軟な管理が実現します。

実践:100台規模での並列実行とローリングアップデート

ここでは、実際に100台のサーバに対して設定変更を行うケースを想定し、並列数のチューニングからローリングアップデートの実装まで解説します。

ステップ1:インベントリファイルの最適化と変数管理

まず、環境・役別にグループを分割したインベントリファイルを作成します。

Yaml
# production.ini [production-web:children] web-tier1 web-tier2 [production-ap:children] ap-tier1 ap-tier2 [production-db:children] db-master db-slave [web-tier1] web[01:25].example.com ansible_ssh_user=ansible [web-tier2] web[26:50].example.com ansible_ssh_user=ansible [ap-tier1] ap[01:25].example.com ansible_ssh_user=ansible [ap-tier2] ap[26:50].example.com ansible_ssh_user=ansible [db-master] db-master[01:05].example.com ansible_ssh_user=ansible [db-slave] db-slave[01:10].example.com ansible_ssh_user=ansible [production:children] production-web production-ap production-db

次に、各グループに必要な変数を定義します。

Yaml
# group_vars/all.yml --- # 共通設定 timezone: Asia/Tokyo logrotate_days: 30 monitoring_enabled: true # 並列実行数 ansible_forks: 50 max_fail_percentage: 20
Yaml
# group_vars/production-web.yml --- # Webサーバ固有 apache_modules: - rewrite - ssl - headers apache_log_dir: /var/log/httpd
Yaml
# group_vars/production-db.yml --- # DBサーバ固有 mysql_version: "8.0" innodb_buffer_pool_size: "4G" slow_query_log: true

ステップ2:並列実行数のチューニングと実行戦略

ansible.cfgで並列実行数を設定し、Playbookで実行戦略を制御します。

Ini
# ansible.cfg [defaults] forks = 50 host_key_checking = False timeout = 30 gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_cache fact_caching_timeout = 3600

Playbookでは、サービスごとに異なる戦略を適用します。

Yaml
# site.yml --- - name: Webサーバ設定変更(全台並列) hosts: production-web strategy: free # 各ホストが完了次第次へ serial: 25 # 25台ずつ実行 tasks: - name: Apacheモジュール有効化 apache2_module: name: "{{ item }}" state: present loop: "{{ apache_modules }}" notify: restart apache - name: 設定ファイル配布 template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf backup: yes notify: restart apache handlers: - name: restart apache systemd: name: httpd state: restarted daemon_reload: yes - name: APサーバ設定変更(段階的) hosts: production-ap serial: 10 # 10台ずつローリング max_fail_percentage: 20 # 20%まで失敗許容 tasks: - name: JVMヒープサイズ調整 lineinfile: path: /opt/app/bin/setenv.sh regexp: '^JAVA_OPTS.*-Xmx' line: 'JAVA_OPTS="$JAVA_OPTS -Xmx8g"' notify: restart app - name: アプリケーションログローテーション設定 template: src: logback.xml.j2 dest: /opt/app/conf/logback.xml backup: yes notify: restart app handlers: - name: restart app systemd: name: app state: restarted

ハマった点やエラー解決

100台規模での一括実行で遭遇する代表的な問題と対処法を紹介します。

問題1:SSH接続エラー「Too many authentication failures」

並列数を増やすと、SSHデーモン側のMaxAuthTriesを超えて認証失敗が発生します。

解決策

Yaml
# group_vars/all.yml に追記 ansible_ssh_common_args: '-o IdentitiesOnly=yes -o PreferredAuthentications=publickey' # SSH Configで接続設定を最適化 # ~/.ssh/config Host *.example.com User ansible IdentityFile ~/.ssh/ansible_rsa IdentitiesOnly yes ServerAliveInterval 60 ServerAliveCountMax 3

問題2:一部ホストでファクト取得に失敗しタイムアウト

ファクト取得中に特定ホストでハングアップし、全体が遅延します。

解決策

Yaml
# ファクト収集を無効化して速度向上 # site.yml - name: Webサーバ設定変更 hosts: production-web gather_facts: no # ファクト収集スキップ pre_tasks: - name: 必要なファクトのみ取得 setup: filter: - ansible_distribution - ansible_distribution_version - ansible_default_ipv4

問題3:設定変更中に負荷が集中しサービス障害

全台同時に再起動をかけると、ロードバランサが異常と判断しトラフィックを遮断します。

解決策

Yaml
# ローリングアップデート用Playbook # rolling_update.yml --- - name: Webサーバローリングアップデート hosts: production-web serial: 20% # 全体の20%ずつ pre_tasks: - name: ロードバランサから一時的に外す uri: url: "http://lb.example.com/api/disable?host={{ inventory_hostname }}" method: POST delegate_to: localhost changed_when: false tasks: - name: アプリケーション更新 include_tasks: tasks/update_app.yml post_tasks: - name: ヘルスチェックが通るまで待機 uri: url: "http://{{ inventory_hostname }}:8080/health" status_code: 200 register: result until: result.status == 200 retries: 30 delay: 10 - name: ロードバランサに戻す uri: url: "http://lb.example.com/api/enable?host={{ inventory_hostname }}" method: POST delegate_to: localhost changed_when: false

まとめ

本記事では、Ansibleで複数サーバを効率的に管理するためのインベントリ設計と並列実行のチューニング手法を解説しました。

  • インベントリを論理的にグループ化することで、環境・役割ごとの変数管理が可能
  • forks/serialパラメータを活用して、サービス特性に応じた実行戦略を実装
  • SSH設定の最適化とファクト収集の制御で、大規模環境でも高速実行を実現
  • ローリングアップデートにより、ダウンタイムゼロでの設定変更を実現

この記事を通して、100台規模のサーバ群でも設定変更作業を数分で完了させ、ヒューマンエラーを削減する自動化のメリットを実感いただけたかと思います。

今後は、Dynamic InventoryとAnsible Tower/AWXを組み合わせた、EC2/Azure等のクラウド環境での自動スケール対応や、Blue/Greenデプロイメントの実現方法についても記事にする予定です。

参考資料