2026年9月13日

2026年9月13日

Ansibleのロール(Role)の作成と活用|WordPressサーバーを役割別のロールで組み立てる実践例

Ansible のロールは、作れること自体より「どこで分けるか」「ロール同士をどうつなぐか」で使いやすさが決まります。この記事では、WordPress サーバーを common・nginx・php_fpm・mariadb・wordpress の5つのロールに分けて構築する例を通して、ロール間の依存の扱い、import_role と include_role の使い分け、タグでの部分実行、引数の検証、秘密情報の管理までを説明します。読み終えると、既存のプレイブックをロールに分割し、他のサーバーにも使い回せる形に整理できるようになります。

例は Ubuntu 24.04 と ansible-core 2.16 以降を想定しています。モジュールの引数やパッケージ名はバージョンで変わることがあるので、実行前に各モジュールのドキュメントを確認してください(2026年10月現在)。本番サーバーに適用する前に、検証用のサーバーで動作を確かめ、データベースのバックアップを取っておくことを前提とします。

ロールをどこで分けるか

分け方の基準は「単独で入れ替えたり、別のサーバーで使い回したりする単位か」です。WordPress サーバーなら、Web サーバー、PHP、データベース、アプリケーションはそれぞれ独立して変更されることが多いため、別々のロールにします。

roles/
  common/      # タイムゾーン、基本パッケージ、ファイアウォール
  nginx/       # Nginx 本体とバーチャルホスト
  php_fpm/     # PHP-FPM とプールの設定
  mariadb/     # MariaDB 本体、データベースとユーザー
  wordpress/   # WordPress の配置と wp-config.php

逆に、1つのタスクしかないものや、特定のサーバーでしか使わない設定まで細かくロールにすると、ファイルを行き来する手間が増えるだけです。ひな形は ansible-galaxy role init roles/nginx で作れますが、使わないディレクトリは削除してかまいません。

ロールの中身を書く

php_fpm ロールを例にします。外から変えたい値は defaults/main.yml に置きます。defaults は優先度が最も低い変数なので、プレイブックやインベントリの変数で上書きできます。

# roles/php_fpm/defaults/main.yml
php_version: "8.3"
php_fpm_pm_max_children: 10
php_fpm_memory_limit: "256M"
# roles/php_fpm/tasks/main.yml
- name: PHP-FPM と拡張をインストールする
  ansible.builtin.apt:
    name:
      - "php{{ php_version }}-fpm"
      - "php{{ php_version }}-mysql"
      - "php{{ php_version }}-xml"
      - "php{{ php_version }}-mbstring"
      - "php{{ php_version }}-curl"
      - "php{{ php_version }}-gd"
      - "php{{ php_version }}-zip"
      - "php{{ php_version }}-intl"
    state: present
    update_cache: true

- name: WordPress 用のプールを配置する
  ansible.builtin.template:
    src: www.conf.j2
    dest: "/etc/php/{{ php_version }}/fpm/pool.d/www.conf"
    owner: root
    group: root
    mode: "0644"
  notify: Restart php-fpm
# roles/php_fpm/handlers/main.yml
- name: Restart php-fpm
  ansible.builtin.service:
    name: "php{{ php_version }}-fpm"
    state: restarted

ハンドラーは、通知したタスクが「changed」になったときだけ、プレイの最後にまとめて実行されます。設定を変えていない実行でサービスが再起動されないため、本番に繰り返し適用しても影響が少なくなります。

引数を検証する(argument_specs)

ロールに必須の変数があるなら、meta/argument_specs.yml に書いておくと、ロールの実行前に型や必須かどうかが検証されます。変数の渡し忘れで、途中まで設定が進んでから失敗する事態を防げます。

# roles/wordpress/meta/argument_specs.yml
argument_specs:
  main:
    short_description: WordPress を配置して wp-config.php を作成する
    options:
      wp_domain:
        type: str
        required: true
        description: サイトのドメイン名
      wp_db_name:
        type: str
        default: wordpress
        description: データベース名
      wp_db_password:
        type: str
        required: true
        no_log: true
        description: データベースユーザーのパスワード

ロール同士の依存をどう扱うか

wordpress ロールは、PHP とデータベースがないと動きません。これを meta/main.yml の dependencies に書くと、wordpress ロールを呼ぶだけで php_fpm と mariadb が先に実行されます。

# roles/wordpress/meta/main.yml
dependencies:
  - role: php_fpm
  - role: mariadb

便利ですが、依存を深く重ねると「どのロールがどの順に、何回実行されるか」が読みにくくなります。同じ引数の依存ロールは通常1回だけ実行されますが、引数が異なると複数回実行されることもあります。サーバー構築のように実行順が明確な場合は、依存は書かずにプレイブックで順番を並べるほうが分かりやすく、この記事の例でもそうしています。依存は「単独で呼ばれても成り立つ必要があるロール」に限ると整理しやすくなります。

プレイブックから呼び出す

# site.yml
- name: WordPress サーバーを構築する
  hosts: wordpress
  become: true
  roles:
    - role: common
      tags: [common]
    - role: nginx
      tags: [nginx]
    - role: php_fpm
      tags: [php]
    - role: mariadb
      tags: [db]
    - role: wordpress
      tags: [wordpress]

ロールにタグを付けておくと、ansible-playbook site.yml --tags nginx のように一部だけを適用できます。Nginx の設定だけ直したいときに、データベースのタスクまで走らせずに済みます。

タスクの途中でロールを呼びたい場合は import_role か include_role を使います。違いは処理のタイミングです。

  • **import_role(静的)**: プレイブックを読み込んだ時点で展開されます。付けたタグや when は、ロール内のすべてのタスクに引き継がれます。--list-tasks で中身が見えるので、通常はこちらが扱いやすいです。
  • **include_role(動的)**: 実行時に読み込まれます。loop で複数回呼んだり、前のタスクの結果で呼ぶロールを変えたりできます。ただしタグはロール内のタスクに自動では引き継がれないため、引き継ぎたい場合は apply を使います。
- name: サイトごとに wordpress ロールを適用する
  ansible.builtin.include_role:
    name: wordpress
  vars:
    wp_domain: "{{ item.domain }}"
    wp_db_name: "{{ item.db_name }}"
    wp_db_password: "{{ item.db_password }}"
  loop: "{{ wp_sites }}"
  loop_control:
    label: "{{ item.domain }}"

wp_sites は、グループ変数などで定義したサイトの一覧(domain・db_name・db_password を持つ辞書のリスト)です。loop_control.label を指定しているのは、ループの各回の出力にパスワードを含む辞書全体が表示されないようにするためです。

秘密情報は Vault で分ける

データベースのパスワードや WordPress の認証用キーは、平文でリポジトリに置かないようにします。グループ変数を通常のファイルと暗号化したファイルに分けると管理しやすくなります。

# group_vars/wordpress/vars.yml(平文)
wp_domain: example.com
wp_db_password: "{{ vault_wp_db_password }}"

# group_vars/wordpress/vault.yml(ansible-vault encrypt で暗号化する)
vault_wp_db_password: "your-db-password"
ansible-vault encrypt group_vars/wordpress/vault.yml
ansible-playbook site.yml --ask-vault-pass

パスワードを扱うタスク(community.mysql.mysql_user など)には no_log: true を付け、実行ログに値が出ないようにします。community.mysql コレクションは ansible-core には含まれないため、ansible-galaxy collection install community.mysql で入れておき、対象サーバーには Python 用の MySQL ドライバー(Ubuntu なら python3-pymysql)が必要です。

適用前に確かめる

ロールに分けたら、次の順で確認すると安全です。

  1. ansible-lint で書き方の問題を拾う
  2. ansible-playbook site.yml --check --diff --limit staging で、検証用サーバーに対する変更内容を事前に確認する(コマンド実行系のタスクはチェックモードで正確に再現できないことがあります)
  3. 検証用サーバーに実際に適用し、サイトの表示と管理画面へのログインを確認する
  4. もう一度同じプレイブックを実行し、changed が 0 になること(冪等性)を確認する

4 で毎回 changed になるタスクがあれば、creates の指定やモジュールの選び方を見直します。毎回変更扱いになるタスクがあると、ハンドラーが無駄に動き、本当の変更を見落とす原因になります。

まとめ

  • ロールは「単独で入れ替える・使い回す単位」で分ける。細かすぎる分割は避ける
  • 変更してよい値は defaults に置き、必須の変数は argument_specs で実行前に検証する
  • 依存(meta の dependencies)は便利だが深くしない。実行順が明確ならプレイブックで並べる
  • 通常は import_role、ループや条件で呼び分けたいときは include_role。タグの引き継ぎの違いに注意する
  • 秘密情報は Vault で暗号化し、no_log でログへの出力を防ぐ
  • --check --diff、検証用サーバーでの適用、2回目の実行で changed 0 を確認してから本番に適用する

お気軽にご相談ください

お見積りへ お問い合わせへ
▲