2026年9月17日

2026年9月17日

AnsibleでWordPressサーバーのプロビジョニングを自動化する手順

Ansible を使うと、Nginx・PHP-FPM・MariaDB・WordPress の導入から初期設定までを1本のプレイブックにまとめ、新しいサーバーでも同じ手順を何度でも再現できます。この記事では、ディレクトリ構成、ロールの分け方、パスワードを Ansible Vault で守る方法、WP-CLI を使った WordPress のインストール、再実行しても壊れない書き方と確認方法までを、Ubuntu サーバーを例に説明します。

全体像と前提

Ansible は、手元の PC(コントロールノード)から SSH で対象サーバーにつなぎ、YAML で書いた「あるべき状態」に近づけるツールです。対象サーバーにエージェントを入れる必要はなく、SSH と Python があれば動きます。

この記事の例は次の前提で書いています。

  • 対象サーバー:Ubuntu 24.04 LTS(標準リポジトリの PHP は 8.3 系)
  • 構成:Nginx + PHP-FPM + MariaDB + WordPress(WP-CLI でインストール)
  • コントロールノード:ansible-core と、community.mysql コレクションを入れた環境

パッケージ名や PHP のバージョンはディストリビューションと版によって変わります(2026年10月現在)。別の OS を使う場合はパッケージ名を読み替えてください。また、最初は捨ててもよい検証用サーバーで流し、問題がないことを確認してから本番に使います。

# コントロールノードでの準備例
python3 -m pip install --user ansible-core
ansible-galaxy collection install community.mysql community.general

ディレクトリ構成

役割ごとに「ロール」に分けておくと、あとで別のサイトに流用したり、一部だけ実行したりしやすくなります。

wp-provision/
├── inventory.ini
├── site.yml
├── group_vars/
│   └── wordpress/
│       ├── vars.yml
│       └── vault.yml      # ansible-vault で暗号化
└── roles/
    ├── common/tasks/main.yml
    ├── nginx/{tasks,templates,handlers}/
    ├── php/tasks/main.yml
    ├── mariadb/tasks/main.yml
    └── wordpress/tasks/main.yml

インベントリには対象サーバーを書きます。

[wordpress]
web1 ansible_host=192.0.2.10 ansible_user=deploy

[wordpress:vars]
ansible_python_interpreter=/usr/bin/python3

秘密情報は Ansible Vault で暗号化する

DB のパスワードや WordPress の管理者パスワードを平文のまま YAML に書いて Git に入れるのは避けます。Ansible Vault で暗号化したファイルに置き、通常の変数ファイルからはそれを参照します。

ansible-vault create group_vars/wordpress/vault.yml
# vault.yml の中身(エディタで入力。保存時に暗号化される)
vault_db_password: "your-db-password"
vault_wp_admin_password: "your-admin-password"
# group_vars/wordpress/vars.yml
wp_domain: example.com
wp_path: /var/www/wordpress
wp_db_name: wordpress
wp_db_user: wpuser
wp_db_password: "{{ vault_db_password }}"
wp_admin_user: siteadmin
wp_admin_email: admin@example.com
wp_admin_password: "{{ vault_wp_admin_password }}"
php_version: "8.3"

vault 用の変数に vault_ を付け、普段使う変数名から参照する形にしておくと、どの値が暗号化されているかが一目で分かります。Vault のパスワード自体はリポジトリに入れず、実行時に --ask-vault-pass で入力するか、権限を絞ったファイルで渡します。

ロールを書く

common:基本パッケージとファイアウォール

# roles/common/tasks/main.yml
- name: パッケージ一覧を更新して基本ツールを入れる
  ansible.builtin.apt:
    name: [curl, unzip, ufw]
    state: present
    update_cache: true
    cache_valid_time: 3600

- name: SSH と HTTP/HTTPS を許可
  community.general.ufw:
    rule: allow
    port: "{{ item }}"
    proto: tcp
  loop: ["22", "80", "443"]

- name: ufw を有効化(既定は拒否)
  community.general.ufw:
    state: enabled
    policy: deny

ファイアウォールを有効にする前に SSH の許可を入れているのは、自分を締め出さないためです。SSH のポートを変えている場合はここも合わせます。

php と mariadb

# roles/php/tasks/main.yml
- name: PHP-FPM と WordPress に必要な拡張
  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 }}-zip"
      - "php{{ php_version }}-gd"
      - "php{{ php_version }}-intl"
    state: present
# roles/mariadb/tasks/main.yml
- name: MariaDB と Python 用ドライバ
  ansible.builtin.apt:
    name: [mariadb-server, python3-pymysql]
    state: present

- name: データベースを作成
  community.mysql.mysql_db:
    name: "{{ wp_db_name }}"
    encoding: utf8mb4
    state: present
    login_unix_socket: /run/mysqld/mysqld.sock

- name: WordPress 用ユーザーを作成
  community.mysql.mysql_user:
    name: "{{ wp_db_user }}"
    password: "{{ wp_db_password }}"
    host: localhost
    priv: "{{ wp_db_name }}.*:ALL"
    state: present
    login_unix_socket: /run/mysqld/mysqld.sock
  no_log: true

Ubuntu の MariaDB は root がソケット認証になっているため、login_unix_socket を指定して root 権限(become)で接続しています。community.mysql のモジュールは対象サーバー側に PyMySQL が必要で、入っていないと接続エラーになります。パスワードを扱うタスクに no_log: true を付けるのは、実行ログや -v の出力に値が残らないようにするためです。

nginx:テンプレートとハンドラー

# roles/nginx/tasks/main.yml
- name: Nginx を入れる
  ansible.builtin.apt:
    name: nginx
    state: present

- name: サイト設定を配置
  ansible.builtin.template:
    src: wordpress.conf.j2
    dest: /etc/nginx/sites-available/wordpress.conf
    mode: "0644"
    backup: true
  notify: reload nginx

- name: サイトを有効化
  ansible.builtin.file:
    src: /etc/nginx/sites-available/wordpress.conf
    dest: /etc/nginx/sites-enabled/wordpress.conf
    state: link
  notify: reload nginx

- name: 設定全体の構文を検査
  ansible.builtin.command: nginx -t
  changed_when: false
# roles/nginx/handlers/main.yml
- name: reload nginx
  ansible.builtin.service:
    name: nginx
    state: reloaded

テンプレート(wordpress.conf.j2)には server_name {{ wp_domain }}; や root {{ wp_path }};、PHP-FPM のソケット unix:/run/php/php{{ php_version }}-fpm.sock を変数で埋め込みます。ハンドラーは、設定が実際に変わったときだけ再読み込みを走らせる仕組みです。毎回 reload するより安全で、変更の有無も実行結果から読み取れます。ハンドラーはプレイの最後にまとめて実行されるため、その前に nginx -t のタスクで設定全体を検査しています。構文エラーがあればここで止まり、壊れた設定で reload されることを防げます。backup: true を付けておくと、上書き前のファイルが日時付きで残ります。

wordpress:WP-CLI でインストール

# roles/wordpress/tasks/main.yml
- name: WP-CLI を配置
  ansible.builtin.get_url:
    url: https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
    dest: /usr/local/bin/wp
    mode: "0755"

- name: ドキュメントルートを作成
  ansible.builtin.file:
    path: "{{ wp_path }}"
    state: directory
    owner: www-data
    group: www-data

- name: WordPress 本体をダウンロード
  ansible.builtin.command:
    cmd: wp core download --locale=ja --path={{ wp_path }}
    creates: "{{ wp_path }}/wp-includes/version.php"
  become_user: www-data

- name: wp-config.php を作成
  ansible.builtin.command:
    cmd: >
      wp config create --path={{ wp_path }}
      --dbname={{ wp_db_name }} --dbuser={{ wp_db_user }}
      --dbpass={{ wp_db_password }} --dbhost=localhost
    creates: "{{ wp_path }}/wp-config.php"
  become_user: www-data
  no_log: true

- name: インストール済みか確認
  ansible.builtin.command:
    cmd: wp core is-installed --path={{ wp_path }}
  become_user: www-data
  register: wp_installed
  failed_when: false
  changed_when: false
  check_mode: false

- name: WordPress をインストール
  ansible.builtin.command:
    cmd: >
      wp core install --path={{ wp_path }}
      --url=https://{{ wp_domain }} --title="My Site"
      --admin_user={{ wp_admin_user }}
      --admin_password={{ wp_admin_password }}
      --admin_email={{ wp_admin_email }} --skip-email
  become_user: www-data
  when: wp_installed.rc != 0
  no_log: true

command モジュールはそのままだと毎回実行され、毎回「変更あり」と表示されます。creates で「このファイルがあれば実行しない」と指定したり、wp core is-installed の結果で分岐したりして、再実行しても同じ状態に落ち着く(冪等な)書き方にしています。wp config create は既定で認証用のキーとソルトも生成します。www-data で WP-CLI を動かすのは、作成されるファイルの所有者をそろえて、あとのパーミッション問題を避けるためです。なお、コマンドライン引数に渡したパスワードは実行中にプロセス一覧から見える場合があります。共用のサーバーでは、インストール後に管理画面から管理者パスワードを変更するなどの運用も検討してください。

プレイブックを実行する

# site.yml
- name: WordPress サーバーを構築
  hosts: wordpress
  become: true
  roles:
    - common
    - php
    - mariadb
    - nginx
    - wordpress
# まず構文と接続を確認
ansible-playbook -i inventory.ini site.yml --syntax-check
ansible -i inventory.ini wordpress -m ansible.builtin.ping

# 変更内容を事前に確認(command タスクなど一部は実行されないため参考程度)
ansible-playbook -i inventory.ini site.yml --check --diff --ask-vault-pass

# 実行
ansible-playbook -i inventory.ini site.yml --ask-vault-pass

2回目の実行で changed=0 に近い結果になれば、冪等に書けている目安になります。毎回 changed になるタスクがあれば、creates や changed_when で条件を見直します。ansible-lint を使うと、モジュール名の書き方や非推奨の構文も指摘してくれます。

よくあるつまずき

  • become_user: www-data で一時ファイルの権限エラーが出る:root 以外のユーザーに切り替えるとき、対象サーバーに acl パッケージが無いと失敗することがあります。common ロールで acl を入れておくと解消します
  • MariaDB への接続エラー:PyMySQL の未導入、ソケットのパス違い、become の付け忘れを順に確認します
  • HTTPS 化:証明書の取得は DNS が向いてからでないと失敗します。Certbot の実行は別のタスクやプレイに分け、DNS の切り替え後に流すと扱いやすくなります

まとめ

  • ロールに分けて、OS 基本設定・PHP・DB・Nginx・WordPress を独立させる
  • パスワードは Ansible Vault に置き、扱うタスクには no_log: true を付ける
  • DB 操作は community.mysql のモジュールで行い、対象サーバーに PyMySQL を入れる
  • command は creates や状態確認で冪等にし、2回目の実行で変化が出ないことを確かめる
  • 設定ファイルはテンプレートとハンドラーで管理し、検証用サーバーで試してから本番へ

お気軽にご相談ください

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