はじめに

Web サーバーで複数のドメインを Apache のバーチャルホスト設定で運用する際、設定ファイルの管理は煩雑になりがちです。ドメイン数が多くなると、手作業による設定ミスや反映漏れも発生しやすくなります。

この記事では Ansible を活用して設定作業を自動化し、効率化する手順を紹介します。手作業による設定の煩雑さから解放されましょう!

従来の Apache バーチャルホスト管理の課題

従来の手動によるバーチャルホストの管理にはいくつかの課題が存在します。

  • 設定の煩雑さ: ドメインごとに設定ファイルを作成し編集する必要があります。ドメイン数が増えると作業量は膨大になります。
  • 設定ミスリスク: 手作業のため、タイプミスや設定漏れが発生しやすいです。一つのミスがサービス全体へ影響を与える可能性もあります!
  • 再現性の低さ: 新しいサーバーを構築する際や障害発生時に同じ設定を迅速に再現することが困難です。
  • 変更の追跡の難しさ: 設定変更の履歴が管理しづらく問題発生時の原因特定が難しくなることがあります。

これらの課題はサーバー運用において大きな負担となります。

課題解決のための選択肢と比較

これらの課題を解決するためにはいくつかの方法が考えられます。それぞれの特徴を比較してみましょう。

管理方法 メリット デメリット こんな場合に検討
手動設定 特別なツールは不要 人的ミスが多い スケーラビリティが低い 再現性が低い 作業負荷が大きい ごく小規模な環境 (ドメイン1~2つ程度)
シェルスクリプトによる自動化 ある程度の自動化が可能、定型作業を効率化できる スクリプトの保守が必要 冪等性の確保が難しい 複雑な処理には不向き 単純な繰り返し作業が多い環境
構成管理ツール (Ansible など) コードによるインフラ管理 (IaC) 実現、冪等性の確保、複雑な処理も記述可能、再利用性が高い 学習コストが発生、ツールの選定が重要 中規模以上の環境、継続的な設定変更が見込まれる環境
コンテナ技術 (Docker など) 環境分離が容易、ポータビリティが高い、スケーラビリティが高い 学習コストが発生、コンテナ特有の運用知識が求められる マイクロサービスアーキテクチャ 新規開発プロジェクト

なぜ Ansible を選ぶのか

上記比較から中規模以上の環境や継続的な設定変更が見込まれる場合構成管理ツールの利用が有効です。その中でも本記事では Ansible を選びました。選定のポイントは以下の通りです。

  • Ansible の特徴:
    • シンプルさ: YAML 形式で Playbook (手順書) を記述するため学習コストが比較的低いです!
    • エージェントレス: 管理対象ノードにエージェントをインストールする必要がありません。SSH 接続だけで済みます!
    • 冪等性: 何度実行しても結果が同じ状態になることを保証します。これにより安心して Playbook を再実行できます。
    • 豊富なモジュール: Apache 設定やパッケージ管理など多くのモジュールが標準で提供されています。
    • テンプレート機能: Jinja2 というテンプレートエンジンを使い設定ファイルの雛形から動的にファイルを生成できます。これが非常に強力です!

検証手順

それでは実際に Ansible を使って Apache のバーチャルホスト設定を管理する手順を説明します。

前提条件

  • OS: Amazon Linux 2023 (EC2 インスタンス)
  • Web サーバー: Apache (httpd)
  • 構成管理ツール: Ansible (ローカル PC や踏み台サーバーで実行)
  • EC2 インスタンス: 1台起動済み SSH 接続が可能であること。セキュリティグループで HTTP (80) および SSH (22) のインバウンド通信が許可されていること。

1. ローカル環境の準備

  • お使いのローカル PC または Ansible を実行するサーバーへ Ansible をインストールします。
    # macOS (例)
    brew install ansible
    # Ubuntu (例)
    sudo apt update && sudo apt install -y ansible
    

2. Ansible プロジェクト作成

次に Ansible で Apache を設定するためのプロジェクトを作成します。ローカル環境で作業ディレクトリを作成し以下のファイル群を配置します。
(※検証用に簡易的なファイル構成としています)

ansible-apache-vhosts/
├── ansible.cfg
├── inventory.ini
├── playbook.yml
├── templates/
│   └── vhost.conf.j2
└── vars/
    └── domains.yml

2.1. Ansible 設定ファイル作成(ansible.cfg)

Ansible の基本的な動作を設定します。

[defaults]
inventory = inventory.ini
remote_user = ec2-user
private_key_file = ~/.ssh/your-key-pair.pem # ご自身のキーペアファイルパスに置き換えてください
host_key_checking = False
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
  • inventory: 使用するインベントリファイルを指定します。
  • remote_user: SSH 接続時に使用するデフォルトのユーザー名を指定します。
  • private_key_file: SSH 接続時に使用する秘密鍵のパスを指定します。ご自身のキーペアファイルの正しいパスに置き換えてください。
  • host_key_checking: 初回接続時のホストキーチェックを無効にします。設定する際はセキュリティリスクを考慮してください。
  • privilege_escalation セクション: become (権限昇格) に関するデフォルト設定です。これにより Playbook 内で毎回 become: yes と書く必要がなくなる場合もありますが明示的にPlaybookに記述する方が分かりやすいこともあります。

    この ansible.cfg を配置することで ansible-playbook コマンド実行時のオプションを減らし設定をプロジェクト単位で管理できます!

2.2. インベントリファイル作成 (inventory.ini)

管理対象の EC2 インスタンス情報を記述します。

[webservers]
# EC2インスタンスのパブリックIP または Public DNS名
your_ec2_instance_ip

[webservers:vars]
# Amazon Linux 2023 / RHEL 系
apache_package = httpd
apache_conf_dir = /etc/httpd/conf.d
apache_service = httpd
apache_user = apache # 必要に応じて定義
  • your_ec2_instance_ip を実際の EC2 インスタンスの IP アドレス等に置き換えてください。
    インベントリファイルが正しく記述され Ansible が対象ホストを認識できる準備が整いました。

2.3. 変数ファイル作成 (vars/domains.yml)

管理したいドメインの情報を YAML 形式で定義します。

apache_vhosts:
  - server_name: test1.example.com
    document_root: /var/www/html/test1
  - server_name: test2.example.com
    document_root: /var/www/html/test2
  - server_name: myapp.example.org
    document_root: /var/www/html/myapp
    server_admin: webmaster@myapp.example.org # オプション

管理対象ドメインの情報が変数として定義されました。このファイルを変更するだけでドメインの追加や変更に対応できます!

2.4. バーチャルホスト用テンプレートファイル作成 (templates/vhost.conf.j2)

Apache のバーチャルホスト設定の雛形を Jinja2 テンプレートとして作成します。

# Generated by Ansible - DO NOT EDIT MANUALLY
<VirtualHost *:80>
    ServerAdmin {{ item.server_admin | default('webmaster@localhost') }}
    ServerName {{ item.server_name }}
    {% if item.server_alias is defined %}
    ServerAlias {{ item.server_alias }}
    {% endif %}
    DocumentRoot {{ item.document_root }}
    <Directory {{ item.document_root }}>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    ErrorLog logs/{{ item.server_name }}-error_log
    CustomLog logs/{{ item.server_name }}-access_log combined
</VirtualHost>

このテンプレートファイルをもとに各ドメイン用の設定ファイルが自動生成されます! これで設定の統一性が保たれます。

2.5. Playbook 作成 (playbook.yml)

Playbook では Apache のインストールから設定ファイルの配置サービスの管理までを一連の流れとして記述します。

---
- hosts: webservers
  vars_files:
    - vars/domains.yml

  tasks:
    - name: Install Apache web server ({{ apache_package }})
      package:
        name: "{{ apache_package }}"
        state: present

    - name: Ensure Apache service is enabled and started
      service:
        name: "{{ apache_service }}"
        state: started
        enabled: yes

    - name: Create document root directories
      file:
        path: "{{ item.document_root }}"
        state: directory
        owner: apache
        group: apache
        mode: '0755'
      loop: "{{ apache_vhosts }}"

    - name: Create dummy index.html for each vhost
      copy:
        content: "<h1>Welcome to {{ item.server_name }}</h1>"
        dest: "{{ item.document_root }}/index.html"
        owner: apache
        group: apache
        mode: '0644'
      loop: "{{ apache_vhosts }}"

    - name: Deploy Apache virtual host configurations from template
      template:
        src: templates/vhost.conf.j2
        dest: "{{ apache_conf_dir }}/{{ item.server_name }}.conf"
        owner: root
        group: root
        mode: '0644'
      loop: "{{ apache_vhosts }}"
      notify: Restart Apache

  handlers:
    - name: Restart Apache
      service:
        name: "{{ apache_service }}"
        state: restarted

この Playbook が Apache の設定作業を自動で行う心臓部です! 冪等性も確保されているため何度実行しても安心です。

3. Ansible 実行

準備が整いました。ローカル環境から Ansible Playbook を実行して EC2 インスタンスに Apache を設定します。コマンド一つで実行できる手軽さも魅力です!

ansible-playbook -i inventory.ini playbook.yml

実行結果↓

y-ishikawa@y-ishikawa1:~/ansible-apache-vhosts$ ansible-playbook -i inventory.ini playbook.yml

PLAY [webservers] **********************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************
[WARNING]: Platform linux on host 18.182.116.139 is using the discovered Python interpreter at /usr/bin/python3.9, but future installation of another Python
interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.18/reference_appendices/interpreter_discovery.html for more
information.
ok: [18.182.116.139]

TASK [Install Apache web server (httpd)] ***********************************************************************************************************************
ok: [18.182.116.139]

TASK [Ensure Apache service is enabled and started] ************************************************************************************************************
ok: [18.182.116.139]

TASK [Create document root directories] ************************************************************************************************************************
changed: [18.182.116.139] => (item={'server_name': 'test1.example.com', 'document_root': '/var/www/html/test1'})
changed: [18.182.116.139] => (item={'server_name': 'test2.example.com', 'document_root': '/var/www/html/test2'})
ok: [18.182.116.139] => (item={'server_name': 'myapp.example.org', 'document_root': '/var/www/html/myapp'})

TASK [Create dummy index.html for each vhost] ******************************************************************************************************************
changed: [18.182.116.139] => (item={'server_name': 'test1.example.com', 'document_root': '/var/www/html/test1'})
changed: [18.182.116.139] => (item={'server_name': 'test2.example.com', 'document_root': '/var/www/html/test2'})
changed: [18.182.116.139] => (item={'server_name': 'myapp.example.org', 'document_root': '/var/www/html/myapp'})

TASK [Deploy Apache virtual host configurations from template] *************************************************************************************************
changed: [18.182.116.139] => (item={'server_name': 'test1.example.com', 'document_root': '/var/www/html/test1'})
changed: [18.182.116.139] => (item={'server_name': 'test2.example.com', 'document_root': '/var/www/html/test2'})
changed: [18.182.116.139] => (item={'server_name': 'myapp.example.org', 'document_root': '/var/www/html/myapp'})

RUNNING HANDLER [Restart Apache] *******************************************************************************************************************************
changed: [18.182.116.139]

PLAY RECAP *****************************************************************************************************************************************************
18.182.116.139             : ok=7    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

y-ishikawa@y-ishikawa1:~/ansible-apache-vhosts$ 

4. 動作確認

最後に EC2 インスタンス上で設定が正しく反映されているか確認します。

4.1. EC2 インスタンスへ SSH 接続して確認

  • EC2 インスタンスへ SSH ログインします。
  • Apache の設定ファイルが conf.d 配下へ正しく配置されているか確認します。
    ls -l /etc/httpd/conf.d/
    

    以下のように test1.example.com.conftest2.example.com.conf といったファイルが作成されていればOKです。

    [root@ip-10-0-1-204 ~]# ls -l /etc/httpd/conf.d/
    total 28
    -rw-r--r--. 1 root root  400 Jul 30  2024 README
    -rw-r--r--. 1 root root 2916 Jul 30  2024 autoindex.conf
    -rw-r--r--. 1 root root  440 May 18 02:12 myapp.example.org.conf
    -rw-r--r--. 1 root root  440 May 18 02:12 test1.example.com.conf
    -rw-r--r--. 1 root root  440 May 18 02:12 test2.example.com.conf
    -rw-r--r--. 1 root root 1252 Jul 30  2024 userdir.conf
    -rw-r--r--. 1 root root  653 Jul 30  2024 welcome.conf
    [root@ip-10-0-1-204 ~]# 
    
  • 生成された設定ファイルの中身を確認します。 (例: test1.example.com.conf)
    cat /etc/httpd/conf.d/test1.example.com.conf
    

    テンプレートから変数が展開され正しい設定ファイルが生成されていることが確認できます。

    [root@ip-10-0-1-204 ~]# cat /etc/httpd/conf.d/test1.example.com.conf
    # Generated by Ansible - DO NOT EDIT MANUALLY
    
        ServerAdmin webmaster@localhost
        ServerName test1.example.com
            DocumentRoot /var/www/html/test1
    
    
            Options Indexes FollowSymLinks
            AllowOverride All
            Require all granted
    
    
        ErrorLog logs/test1.example.com-error_log
        CustomLog logs/test1.example.com-access_log combined
    [root@ip-10-0-1-204 ~]# 
    
  • Apache の設定構文をチェックします。
    [root@ip-10-0-1-204 ~]# sudo apachectl configtest
    Syntax OK
    [root@ip-10-0-1-204 ~]# 
    
  • 各ドキュメントルートにダミーの index.html が作成されているか確認します。
    [root@ip-10-0-1-204 ~]# ls -l /var/www/html/test1/
    total 4
    -rw-r--r--. 1 apache apache 37 May 18 02:12 index.html
    [root@ip-10-0-1-204 ~]# 
    
  • curl コマンドで各バーチャルホストへアクセスできるか確認します。 (ホスト名を指定)
    [root@ip-10-0-1-204 ~]# curl -H "Host: test1.example.com" localhost
    <h1>Welcome to test1.example.com</h1>
    [root@ip-10-0-1-204 ~]# 
    [root@ip-10-0-1-204 ~]# curl -H "Host: test2.example.com" localhost
    <h1>Welcome to test2.example.com</h1>
    [root@ip-10-0-1-204 ~]# 
    [root@ip-10-0-1-204 ~]# curl -H "Host: myapp.example.org" localhost
    <h1>Welcome to myapp.example.org</h1>
    [root@ip-10-0-1-204 ~]# 
    

    Welcome to (各ドメイン名) という内容が表示され各バーチャルホストが正しく動作していることが確認できます。

4.2. ローカル PC から確認

ローカル PC の /etc/hosts ファイルを書き換え、ブラウザからアクセスを確認します。

ブラウザ経由でも各バーチャルホストが機能していることが確認できます。

期待される効果

この Ansible を用いた管理方法を導入することで以下の効果が期待できます。

  • 作業効率の大幅な向上: 面倒な手作業から解放されます!
  • 設定ミスリスクの低減: 人為的なミスを防ぎます。
  • 設定の標準化と再現性の確保: いつでも誰でも同じ環境を構築できます!
  • 手順のドキュメント化: Playbook 自体が設定手順のドキュメントとして機能します。

まとめ

この記事では AWS EC2 上の Apache で複数ドメインのバーチャルホスト設定を Ansible を使って自動化し効率的に管理する手順を紹介しました。

手作業による煩雑な設定管理から解放されコードによるインフラ管理 (Infrastructure as Code) のメリットを享受できます! この方法を導入することで Apache の運用がよりスマートにそして確実になるはずです。ぜひ試してみてください。

参考ドキュメント

Ansible
https://docs.ansible.com/ansible/latest/index.html

Jinja2 (Ansibleで使われるテンプレートエンジン)
https://jinja.palletsprojects.com/en/latest/templates/