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回目の実行で変化が出ないことを確かめる- 設定ファイルはテンプレートとハンドラーで管理し、検証用サーバーで試してから本番へ