はじめに
はじめまして!TechBullコミュニティメンバーのいわき(@IwaTech1222)と申します!現在、安達さん(@adachin0817)のメンティーとして課題を進めており、学んだ内容をアウトプットしたいため、本記事をもって初ブログとさせていただきます。今回は、タイトルにある通り、AnsibleでWordPressの構築をインフラコード化するまでの流れを書いてみました。
自己紹介
初ブログということで簡単に自己紹介させていただきます。1999年生まれで今年でエンジニア歴4年目のインフラエンジニアです。ヘルプデスク、オンプレミスを1年ずつ経験しており、今年からAWSでデータ分析基盤の設計構築の案件に参画しています。TechBullに参加するまでは、AWSの案件に参画できるように主に資格の勉強をしていました。実際参画できて、次のステップとしてWeb系に行きたいということもあり、何か手を動かした方が思っていた時に、ちょうどエンジニア向けYouTubeメディアのTECH WORLDさん(@tech_world18))で安達さんのことを知り、TechBullに参加させていただきました。
Ansibleについて
サーバー構成管理やアプリケーションのデプロイを自動化するツールです。エージェントレスで動作し、対象サーバーには SSH 接続するだけで操作が可能です。Pythonで実装されており、シンプルな構文と豊富なモジュールが特徴です。構成の再現性・保守性を高めるために、IaCを実現する代表的なツールの一つです。
開発環境
- macOS 15.3
- Multipass 1.15.1+mac
- Visual Studio Code 1.99
- Ubuntu 24.04
- Ansible 2.18.5
本環境のディレクトリ構成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
ansible ┣━ ansible.cfg ┣━ playbook.yml ┣━ inventory.ini ┗━ roles/ ┣━ users/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ menta_sudoers.j2 ┗━ vars/ ┗━ main.yml ┣━ nginx/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ wp.techbull.cloud.conf.j2 ┣━ handlers/ ┗━ main.yml ┗━ files/ ┗━ nginx.conf ┣━ php/ ┣━ tasks/ ┗━ main.yml ┣━ handlers/ ┗━ main.yml ┗━ files/ ┗━ www.conf ┣━ mysql/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ root_my.cnf.j2 ┣━ handlers/ ┗━ main.yml ┣━ files/ ┗━ mysqld_custom.cnf ┗━ vars/ ┗━ main.yml ┣━ wordpress/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ wp-config.php.j2 ┗━ handlers/ ┗━ main.yml |
Ansibleのインストール
コントロールノードのMacにHomebrewでAnsibleをインストールする
1 |
brew install ansible |
Ansible使用前の事前準備
Ansibleはターゲットノードに対して、SSHで接続を行います。そのため、事前に手動で以下2点を設定します。
- ターゲットノードへ接続するためのユーザ作成
- SSH公開鍵認証の設定
まず、Multipassで立ててターゲットノードにログイン
1 2 3 |
multipass launch --name wp-server --cpus 4 --memory 8G --disk 32G multipass list multipass shell wp-server |
1. ターゲットノードへ接続するためのユーザ作成
- ユーザの作成
新規ユーザmentaを作成し、作成されているか確認をする。
1 2 |
$ sudo useradd menta $ sudo grep menta /etc/passwd |
- /etc/sudoersの設定
ユーザmentaでパスワードなしで全てのコマンドを実行できる設定をする。
※文字化け・画面崩れ防止のため、設定前にターミナルの種類をxtermに設定する。
1 2 3 4 |
$ export TERM=xterm $ sudo visudo menta ALL=(ALL) NOPASSWD:ALL $ sudo cat /etc/sudoers |
2. SSH公開鍵認証の設定
- ターミナルに戻り、ssh認証鍵を作成する
1 2 3 |
% cd ~/.ssh % ssh-keygen -t rsa |
- 以下のような出力がされるので、鍵ファイル名をwp-server、パスワードを設定せずにエンターを押下する。
1 2 3 4 |
Generating public/private rsa key pair. Enter file in which to save the key (/c/Users/PC_User/.ssh/id_rsa): #wp-serverと入力 Enter passphrase (empty for no passphrase): #何も入力せず、Enterを押下 Enter same passphrase again: #何も入力せず、Enterを押下 |
接続先情報をconfigに設定
1 2 3 4 5 6 7 |
vi ~/.ssh/config #以下を設定 Host wp.techbull.cloud Hostname 192.168.64.6 User menta Port 22 IdentityFile ~/.ssh/wp-server |
- リモートホストのssh鍵設定と公開鍵配置
リモートホスト側に後で先ほど作成したターミナル側の公開鍵の設定をするので、中身を控えておく。
1 |
% cat ~/.ssh/wp-server.pub |
- リモートホストにSSH接続し、sshd_configの設定
1 2 3 4 5 6 7 8 9 |
% multipass shell wp-server $ sudo su # vi /etc/ssh/sshd_config #以下に設定 PubkeyAuthentication yes PermitRootLogin yes AuthorizedKeysFile .ssh/authorized_keys PasswordAuthentication no |
- リモートホストにターミナル側の公開鍵配置
ユーザmentaにスイッチし、ターミナル環境で作成した公開鍵を配置する。
※スイッチ前にユーザmentaのホームディレクトリが作成されていないため、作成し、所有者・所有グループを設定する。
1 2 3 4 5 |
$ sudo mkdir -p /home/menta $ sudo chown menta:menta /home/menta $ sudo su - menta $ mkdir ~/.ssh $ vi ~/.ssh/authorized_keys #控えておいた公開鍵を貼り付ける |
上述の設定を反映させるため、sshdサービスを再起動する。
1 |
$ sudo systemctl restart ssh |
- リモートホストへssh接続
ターミナル環境に戻り、sshのconfigに設定したホスト名でリモートホストにSSH接続できることを確認する。
1 |
% ssh wp.techbull.cloud |
AnsibleでWordpress環境構築の自動化
上述より、Ansibleを利用できる状態ができました。いよいよAnsibleでのWordpressの環境構築に入ります。
その前にAnsibleを利用する時に出てくるキーワードを以下に整理いたします。
- コントロールノード
- 処理の指示を出すノード(本環境ではMac)。
- ターゲットノード
- 処理の対象となるノード(本環境ではMultipassで作成した仮想サーバ(WordPress環境))。
- インベントリ
- ターゲットノードをリストして記載するファイル。
- プレイブック
- ターゲットノード側で実行したい処理の流れを記載するファイル。
- モジュール
- コマンドラインやプレイブックから呼び出せる処理の単位、プレイブック内でモジュールを指定することで、処理をターゲットノードで実行可能。
- タスク
- ターゲットノードに実行させる1つ1つの具体的な操作のこと。プレイブック内で上から順に実行され、冪等性(べきとうせい)がある。
※冪等性:同じ処理を何回実行しても、結果が変わらないこと
- ターゲットノードに実行させる1つ1つの具体的な操作のこと。プレイブック内で上から順に実行され、冪等性(べきとうせい)がある。
- プレイ
- ターゲットノードグループごとに行うタスクのまとまりのこと。
- ロール
- プレイブックの記載を分割するためのコンポーネント。1つのプレイブックで多くのタスクを記載すると管理が難しく、可読性が悪くなるため、ロールでプレイブック内のタスクを分割できる。
ansible.cfg(設定ファイル)の作成
Anisbleの設定ファイル”ansible.cfg”をansibleディレクトリ直下に作成・設定します。設定内容は以下になります。
1 2 3 4 5 6 7 8 9 |
[defaults] become = true interpreter_python = /usr/bin/python3.12 inventory = ~/git/menta/ansible/inventory.ini private_key_file = ~/.ssh/wp-server retry_files_enabled = False log_path = ~/.ansible/ansible.log host_key_checking = False gathering = smart |
- [defaults]
- Ansibleのデフォルト設定のセクション。このセクションに記載された内容は全てのプレイブックのデフォルトで適用される。
- become = true
- 全タスクでsudoを使う設定。
-
interpreter_python = /usr/bin/python3.12
-
ターゲットノード上で使用するPythonインタープリタ。AnsibleではターゲットノードでPythonで書かれたモジュールを実行するため、この設定が必要。
-
-
inventory = ~/git/menta/ansible/inventory.ini
-
ターゲットノードを記載した一覧のインベントリファイルの場所を指定。
-
-
private_key_file = ~/.ssh/wp-server
-
SSH接続に使う秘密鍵の指定。
-
-
retry_files_enabled = False
-
再試行ファイル(.retryファイル)が作成されないための設定。プレイブックの実行に失敗した場合、カレントディレクトリに自動的に再試行ファイルが作成されるが、カレントディレクトリが散らかってしまうので、Falseに設定。
-
-
log_path = ~/.ansible/ansible.log
-
Ansible実行時のログファイルの保存先を指定
-
-
host_key_checking = False
-
SSHのホストキー確認を無効化する。
-
-
gathering = smart
-
ファクト収集(setupモジュールによるターゲット情報取得)のモードを指定。smartはデフォルトで、必要な時だけファクトを収集し、可能ならキャッシュを使うモード。
-
inventory.iniの作成
ターゲットノードを記載するinventort.iniを作成します。設定内容は以下になります。
1 2 |
[wp_servers] wp.techbull.cloud ansible_user=menta |
wp_serversというホストグループ名を指定します。プレイブック内でこのグループを指定するとグループに属しているターゲットノードに対してタスクが実行されます。その下にSSH接続するターゲットノードのホスト名とユーザ名を指定します。
playbook.yml(プレイブック)の作成
WordPress環境を構築するためのプレイブックを作成します。プレイブックはYAML形式で記述します。ターゲットノードに対して実行するタスクを分割したロールについては後述いたします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
--- - name: Setup users hosts: all vars_files: - ./.env.yml roles: - users - name: Setup Wordpress hosts: wp_servers vars_files: - ./.env.yml roles: - nginx - php - mysql - wordpress - db-backup |
hostsでインベントリファイルに記載されたホストグループを指定し、そのグループに属するホストに対してこちらのプレイブックに記載した内容を実行します。rolesで各ターゲットノードごとに実行するロールを指定します。
各ロールの作成
ロールを利用するには、Ansibleが認識できるディレクトリ構造を作成し、ファイルを配置する必要があります。
ディレクトリ配下のファイルは固定で”main.yml”が読み込まれる仕様になっています。
以下にAnsibleが認識できるディレクトリの種類をまとめます。
- defaultディレクトリ
- 変数の初期値を定義したYAMLファイルを配置するディレクトリです。例としては、ミドルウェアやOSの設定テンプレートに使用するデフォルト値などです。後述のvarsディレクトリ内に定義する変数との違いは、優先順位です。defaultディレクトリで定義した変数は一番優先度が低いため、varsディレクトリで同じ変数を定義していた場合、varsディレクトリの方で宣言した値で上書きされるので注意が必要です。
- filesディレクトリ
- copyモジュールなどのファイル操作関連のモジュールでターゲットノードに転送するファイルを配置するディレクトリです。このディレクトリにはmain.ymlを作成する必要がありません。
- handlersディレクトリ
- tasksディレクトリのmain.yml内でnotifyディレクティブで定義されたタスク名に紐づいたタスク名を呼び出すことができます。
- tasksディレクトリ
- このディレクトリ配下にmain.ymlを配置し、ターゲットノードに対するタスクを定義します。
- templatesディレクトリ
- templateモジュールで利用する、テンプレートファイルを配置するディレクトリです。filesディレクトリと同様にmain.ymlを作成する必要はなく、Jinja2形式のテンプレートを配置します。filesディレクトリとの使い分けとしては、templatesディレクトリ配下に作成したファイル内でvarsディレクトリなどで定義した変数を参照することができますが、filesディレクトリに配置したファイル内では変数の参照ができないので、変数を参照させたい場合に有効です。
usersロール
usersロールで手動で作成したmentaユーザ、グループ、SSH鍵認証の設定をします。usersロールのディレクトリ構成は以下になります。
1 2 3 4 5 6 7 8 |
roles/ ┣━ users/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ menta_sudoers.j2 ┗━ vars/ ┗━ main.yml |
各ディレクトリ配下のファイルは以下になります。
- roles/users/tasks/main.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
--- - name: Create New User become_user: root user: name: "{{ item.key }}" password: "{{ item.value.initial_password }}" update_password: on_create groups: "{{ item.value.groups }}" comment: "{{ item.value.comment }}" shell: /bin/bash #ログインシェル home: /home/{{ item.key }} state: present loop: "{{ new_users | dict2items }}" - name: Distribute authorized key ansible.posix.authorized_key: user: "{{ item.key }}" key: "{{ lookup('file', '~/.ssh/wp-server.pub') }}" state: present loop: "{{ new_users | dict2items }}" - name: Deploy temporary sudoers files template: src: "{{ item.value.priv }}_sudoers.j2" dest: "/etc/sudoers.d/{{ item.key }}" owner: root group: root mode: 0400 validate: 'visudo -c -f %s' loop: "{{ new_users | dict2items }}" |
ユーザの作成、公開鍵の配置、sudoersの配置をしています。ユーザ作成のタスクでupdate_passwordをon_createに設定することで作成済みのユーザに対してはパスワードを設定しないため、事前に手動で作成したユーザのパスワードとの差分をなくすことができます。
loopの中で利用する変数はリスト形式である必要があります。後述するvarsディレクトリ配下のmain.ymlで定義した変数はマッピング形式であるため、dict2itemsフィルタを使用することでマッピング形式で定義した変数をリスト形式に変換して利用します。
- roles/users/templates/menta_sudoers.j2
varsディレクトリ配下のman.ymlで定義したユーザに対してパスワードなしでsudoを実行できる権限を付与します。
1 |
{{ item.key }} ALL=(ALL) NOPASSWD: ALL |
- roles/users/vars/main.yml
1 2 3 4 5 6 7 8 9 |
--- new_users: menta: #作成するユーザ comment: "Add User menta" initial_password: "{{ lookup('env', 'MENTA_PASSWORD') | password_hash('sha512') }}" groups: menta add_authorized_keys: true add_sudoers: true priv: menta |
こちらで作成するユーザの各パラメータを設定しています。パスワードについては、lookupで環境変数を読み込む形にしています。
nginxロール
Nginxのインストールと設定ファイルの作成をします。nginxロールのディレクトリ構成は以下です。
1 2 3 4 5 6 7 8 9 10 |
roles/ ┣━ nginx/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ wp.techbull.cloud.conf.j2 ┣━ handlers/ ┗━ main.yml ┗━ files/ ┗━ nginx.conf |
各ディレクトリ配下のファイルは以下になります。
- roles/nginx/tasks/main.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
--- - name: Ensure Nginx is installed apt: name: nginx state: present - name: Ensure Nginx is running systemd: name: nginx state: started enabled: true - name: Create the document root directory for the virtual host file: path: "/var/www/{{ inventory_hostname }}" state: directory owner: www-data group: www-data mode: '0755' - name: Copy nginx conf copy: src: "{{ item.src }}" dest: "{{ item.dest }}" backup: no owner: root group: root mode: '0644' loop: - { src: roles/nginx/files/nginx.conf, dest: /etc/nginx/nginx.conf } - name: Create SSL directory file: path: /etc/nginx/ssl state: directory mode: '0755' - name: Copy SSL certificate copy: src: ~/certs/wp.techbull.cloud/wp.techbull.cloud.pem dest: /etc/nginx/ssl/wp.techbull.cloud.pem owner: root group: root mode: '0644' - name: Copy SSL key copy: src: ~/certs/wp.techbull.cloud/wp.techbull.cloud-key.pem dest: /etc/nginx/ssl/wp.techbull.cloud-key.pem owner: root group: root mode: '0600' - name: Copy Virtual Host conf template: src: "{{ item.src }}" dest: "{{ item.dest }}" backup: no owner: root group: root mode: '0644' loop: - { src: "{{ inventory_hostname }}.conf.j2", dest: "/etc/nginx/conf.d/{{ inventory_hostname }}.conf" } notify: restart nginx |
上記のタスクでは、Nginxのインストールと設定ファイル、https化するための証明書と秘密鍵の配置をしております。https化するにはmkcertをローカルにインストール後、証明書と秘密鍵を作成する流れになります。後述するVirtual Hostの設定ファイル内でこれらの証明書と秘密鍵のパラメータを設定します。
- roles/nginx/templates/wp.techbull.cloud.conf.j2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
server { listen 80; server_name {{ inventory_hostname }}; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name {{ inventory_hostname }}; ssl_certificate /etc/nginx/ssl/{{ inventory_hostname }}.pem; ssl_certificate_key /etc/nginx/ssl/{{ inventory_hostname }}-key.pem; root /var/www/{{ inventory_hostname }}/; index index.php index.html; access_log /var/log/nginx/{{ inventory_hostname }}.access.log custom_log; error_log /var/log/nginx/{{ inventory_hostname }}.error.log; location / { try_files $uri $uri/ =404; } location ~ \.php$ { include /etc/nginx/fastcgi_params; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } |
上記のVirtual Hostの設定ファイルにてWordPressのサイトをHTTPS化しています。80番ポートへのアクセス時はHTTPでのアクセスはHTTPSへリダイレクトするようにしています。443番ポートの設定箇所では、証明書と秘密鍵の設定、マジック変数”inventory_hostname”を使用してドキュメントルートやログのパスを指定しております。
マジック変数とはAnsibleがあらかじめ定義している変数になります。inventory_hostnameはinventory.iniに記載したホスト名を参照しています。マジック変数を使用することで、複数のサーバを管理しやすく、コードの可読性が上がります。PHPリクエストは php8.3-fpmに対して、UNIXドメインソケット経由でアクセスします。
- roles/nginx/handlers/main.yml
1 2 3 4 5 |
--- - name: restart nginx systemd: name: nginx state: restarted |
先に記載したタスク内でnotifyでNginxを再起動する設定をしています。notifyはあるタスクで変更があった場合だけ、後で特定の処理(ハンドラー)を実行するという仕組みで、タスク内でVirtual Hostの設定ファイルに変更が入った場合をトリガーに上記のハンドラーとしてNginxの再起動を実行するようにしております。
- roles/nginx/files/nginx.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
user www-data; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format custom_log '[nginx] time:$time_iso8601 ' 'server_addr:$server_addr ' 'host:$remote_addr ' 'method:$request_method' 'reqsize:$request_length ' 'uri:$uri ' 'query:$query_string ' 'status:$status ' 'size:$body_bytes_sent ' 'referer:$http_referer ' 'ua:$http_user_agent ' 'forwardedfor:$http_x_forwarded_for ' 'reqtime:$request_time ' 'apptime:$upstream_response_time'; access_log /var/log/nginx/access.log custom_log; sendfile on; #tcp_nopush on; keepalive_timeout 65; gzip on; include /etc/nginx/conf.d/*.conf; } |
上記はNginx全体の挙動について定義した設定ファイルになります。細かいパラメータの解説は割愛させていただきますが、ファイル内では先に記述したVirtual Hostの設定ファイルを異なり、変数を参照しておりません。そのため、filesディレクトリに配置して、タスク内でcopyモジュールでターゲットノードにコピーをしています。
phpロール
PHPのインストールと設定ファイルの作成をします。phpロールのディレクトリ構成は以下です。
1 2 3 4 5 6 7 8 |
roles/ ┣━ php/ ┣━ tasks/ ┗━ main.yml ┣━ handlers/ ┗━ main.yml ┗━ files/ ┗━ www.conf |
各ディレクトリ配下のファイルは以下になります。
- roles/php/tasks/main.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
- name: Install PHP 8.3 and PHP-FPM apt: name: - php8.3 - php8.3-fpm - php8.3-mysql - php8.3-cli - php8.3-curl - php8.3-mbstring - php8.3-xml state: present update_cache: yes - name: Ensure PHP-FPM is running and enabled systemd: name: php8.3-fpm state: started enabled: true - name: copy www.conf copy: src: "{{ item.src }}" dest: "{{ item.dest }}" backup: no owner: root group: root mode: '0644' loop: - { src: roles/php/files/www.conf, dest: /etc/php/8.3/fpm/pool.d/www.conf } notify: restart php-fpm |
上記タスクでは、PHP8.3、PHP8.3-FPMのインストールと関連するモジュールのインストールとPHP-FPMの設定ファイル www.conf を配置をしております。notifyにより、www.confに変更があった場合はハンドラによってPHP-FPMを再起動をします。
- roles/php/handlers/main.yml
1 2 3 4 |
- name: restart php-fpm systemd: name: php8.3-fpm state: restarted |
先に書いた通り、www.confに変更があった場合、PHP8.3-FPMを再起動するハンドラの設定になります。
- roles/php/files/www.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[www] user = www-data group = www-data listen = /run/php/php8.3-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 pm = static pm.max_children = 10 pm.max_requests = 100 php_admin_value[memory_limit] = 256M request_terminate_timeout = 180 |
詳細な解説は割愛させていただきますが、PHPの実行ユーザ、グループをNginxの実行ユーザ(先に記述したNginxの設定ファイルnginx.conf内のuserで指定したユーザ)と一致させる必要があります。また、NginxとPHPはUNIXドメインソケット経由で接続を行うため、上記www.conf内のlistenとVirtual Hostの設定ファイル内のfastcgi_passの値についても一致させる必要があります。
mysqlロール
MySQLのインストールと設定ファイルの作成をします。mysqlロールのディレクトリ構成は以下です。
1 2 3 4 5 6 7 8 9 10 11 12 |
roles/ ┣━ mysql/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ root_my.cnf.j2 ┣━ handlers/ ┗━ main.yml ┣━ files/ ┗━ mysqld_custom.cnf ┗━ vars/ ┗━ main.yml |
各ディレクトリ配下のファイルは以下になります。
- roles/mysql/tasks/main.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
- name: Install MySQL 8.0 apt: name: mysql-server state: present update_cache: yes - name: Install PyMySQL for MySQL support apt: name: python3-pymysql state: present - name: Create log file file: path: /var/log/mysql/error.log state: touch owner: mysql group: mysql mode: '0640' - name: Copy MySQL root client config template: src: root_my.cnf.j2 dest: /root/.my.cnf owner: root group: root mode: '0600' - name: Copy MySQL custom config for utf8mb4 copy: src: mysqld_custom.cnf dest: /etc/mysql/mysql.conf.d/mysqld_custom.cnf owner: root group: root mode: '0644' notify: Restart MySQL - name: Wait for MySQL socket to be ready wait_for: path: /var/run/mysqld/mysqld.sock state: present timeout: 30 - name: Set root user to use mysql_native_password mysql_user: name: root host: localhost password: "{{ mysql_root_password }}" login_unix_socket: /var/run/mysqld/mysqld.sock priv: "*.*:ALL,GRANT" plugin: mysql_native_password state: present - name: Create MySQL user for WordPress mysql_user: name: "{{ item.value.name }}" password: "{{ item.value.password }}" host: "{{ item.value.host }}" priv: "{{ item.value.priv }}" state: present login_unix_socket: /var/run/mysqld/mysqld.sock login_user: root loop: "{{ new_users | dict2items }}" - name: Create WordPress database mysql_db: name: wordpress_db encoding: utf8mb4 collation: utf8mb4_unicode_ci state: present login_user: root login_unix_socket: /var/run/mysqld/mysqld.sock - name: Grant all privileges to menta user on wordpress_db mysql_user: name: menta host: localhost password: "{{ MENTA_MYSQL_PASSWORD }}" priv: "wordpress_db.*:ALL" state: present login_user: root |
上記タスクで、MySQL8.0と関連モジュールのインストール、各種設定ファイルの配置、Wordpress用のユーザとDB作成、そのユーザに対する権限を設定しています。また、rootユーザーの認証方式をmysql_native_passwordをしています。MySQL8.0以降では、デフォルトの認証方式が従来のmysql_native_passwordからcaching_sha2_passwordに変更されたようです。
WordPressではcaching_sha2_passwordに対応していない場合があるみたいなので、WordPress環境では互換性を保つためにrootユーザーの認証プラグインをmysql_native_password に明示的に変更することが推奨されているそうです。ちなみにmysql_native_passwordは、MySQL 5.x 系までの標準的な認証方式のようで、クライアントがパスワードをハッシュ化してサーバーに送信し、サーバー側でも同じハッシュを使って認証を行う仕組みのようです。
- roles/mysql/templates/root_my.cnf.j2
1 2 3 |
[client] user=root password={{ mysql_root_password }} |
MySQLクライアント用の設定ファイルになります。この設定ファイルがあることで、コマンド実行時に自動でこの認証情報が使われます。パスワードは環境変数を定義した.env.ymlに記載してプレイブックを実行したときに読み込まれるようにします。また、こちらのファイルはタスク内でパーミッションを0600に設定する必要があります(そうしないと、MySQLはこのファイルを無視するため)。
- roles/mysql/handlers/main.yml
1 2 3 4 |
- name: Restart MySQL systemd: name: mysql state: restarted |
タスク内でmysqld_custom.cnfに変更があった場合、 MySQLを再起動するハンドラの設定になります。
- roles/mysql/files/mysqld_custom.cnf
1 2 3 4 5 6 7 8 9 |
[mysql] default-character-set = utf8 [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci socket=/var/run/mysqld/mysqld.sock log-error=/var/log/mysql/error.log pid-file=/run/mysqld/mysqld.pid |
MySQLクライアントとサーバの文字コードとログ、ソケットファイルなどを指定する カスタム設定ファイルになります。
- roles/mysql/vars/main.yml
1 2 3 4 5 6 7 8 |
--- new_users: menta: comment: "Add menta" name: menta password: "{{ lookup('env', 'MENTA_MYSQL_PASSWORD') }}" host: localhost priv: "wordpress.*:ALL" |
WordpressDB用のユーザ作成に必要な変数を定義しています。パスワードについてはlookupで.env.yml内に定義したパスワードを参照しています。
wordpressロール
WordPressのインストールと設定ファイルの作成をします。wordpressロールのディレクトリ構成は以下です。
1 2 3 4 5 6 7 8 |
roles/ ┣━ wordpress/ ┣━ tasks/ ┗━ main.yml ┣━ templates/ ┗━ wp-config.php.j2 ┗━ handlers/ ┗━ main.yml |
各ディレクトリ配下のファイルは以下になります。
- roles/wordpress/tasks/main.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
- name: Download latest WordPress get_url: url: https://wordpress.org/latest.tar.gz dest: /tmp/wordpress.tar.gz mode: '0755' - name: Extract WordPress unarchive: src: /tmp/wordpress.tar.gz dest: /var/www/ remote_src: yes creates: /var/www/wordpress - name: Check if documentroot exists stat: path: /var/www/{{ inventory_hostname }}/ register: documentroot_stat - name: Create documentroot if not exists file: path: /var/www/{{ inventory_hostname }}/ state: directory owner: www-data group: www-data mode: '0755' when: not documentroot_stat.stat.exists - name: Move WordPress files to web root command: > rsync -a /var/www/wordpress/ /var/www/{{ inventory_hostname }}/ args: creates: /var/www/{{ inventory_hostname }}/wp-config-sample.php - name: Set correct ownership for WordPress file: path: /var/www/{{ inventory_hostname }} recurse: yes owner: www-data group: www-data - name: Set directory permissions find: paths: /var/www/{{ inventory_hostname }} file_type: directory register: wordpress_dirs - name: Set permissions 755 for directories file: path: "{{ item.path }}" mode: '0755' loop: "{{ wordpress_dirs.files }}" - name: Remove temporary WordPress directory file: path: /var/www/wordpress state: absent - name: Set file permissions find: paths: /var/www/{{ inventory_hostname }} file_type: file register: wordpress_files - name: Set permissions 644 for files file: path: "{{ item.path }}" mode: '0644' loop: "{{ wordpress_files.files }}" - name: Copy wp-config.php template: src: wp-config.php.j2 dest: /var/www/{{ inventory_hostname }}/wp-config.php owner: www-data group: www-data mode: '0755' notify: - Restart PHP-FPM - Restart Nginx - Restart MySQL |
上記タスクでWordpressのダウンロードと展開、展開後、Virtual Hostの設定ファイルで設定したドキュメントルートへの展開したファイルの同期、各ファイルに対して、所有者・パーミッションを設定しています。こちらのタスク内でもドキュメントルートを指定する際は、マジック変数”inventory_hostname”を使用しています。Wordpressの設定ファイルのwp-config.phpに変更がある場合、ハンドラーでNginx、PHP-FPM、MySQLを再起動をするようにしています。
- roles/wordpress/templates/wp-config.php.j2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php define( 'DB_NAME', 'wordpress_db' ); define( 'DB_USER', 'menta' ); define( 'DB_PASSWORD', '{{MENTA_MYSQL_PASSWORD}}' ); define( 'DB_HOST', 'localhost' ); define( 'DB_CHARSET', 'utf8mb4' ); define( 'DB_COLLATE', '' ); $table_prefix = 'wp_'; define( 'WP_DEBUG', false ); if ( ! defined( 'ABSPATH' ) ) { define( 'ABSPATH', __DIR__ . '/' ); } require_once ABSPATH . 'wp-settings.php'; |
詳細は割愛させていただきますが、こちらのWordpressの設定ファイルでは、データベースへの接続情報、テーブルプレフィックス、デバッグモード、Wordpressの起動設定を設定しております。
- roles/wordpress/handlers/main.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- name: Restart PHP-FPM service: name: php8.3-fpm state: restarted - name: Restart Nginx service: name: nginx state: restarted - name: Restart MySQL service: name: mysql state: restarted |
先にも述べた通り、タスク内でwp-config.phpに変更があった場合のハンドラとしてNginx、PHP-FPM、MySQLを再起動します。
各ロールの作成まで完了しましたら、inventory.iniとplaybook.ymlが格納されているディレクトリへ移動し、プレイブックを実行して、Wordpress環境を自動構築します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
% ansible-playbook playbook.yml PLAY [Setup Server] ***************************************************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************************************************** ok: [dev.techbull.cloud] TASK [users : Create New User] ****************************************************************************************************************************************** ok: [dev.techbull.cloud] => (item={'key': 'menta', 'value': {'comment': 'Add User menta', 'initial_password': '$6$rounds=656000$FnbfRGH7kdZQs6VR$ArEe.f7uf6SH81iVYP7MGfVJkPI3OBLk3BYs4sjtd6kM5g1BqJ5lHUeNFyvWaiQ6HWF8hHI8xbkrPWO4OdOqa/', 'groups': 'menta', 'add_authorized_keys': True, 'add_sudoers': True, 'priv': 'menta'}}) TASK [users : Distribute authorized key] ******************************************************************************************************************************** ok: [dev.techbull.cloud] => (item={'key': 'menta', 'value': {'comment': 'Add User menta', 'initial_password': '$6$rounds=656000$w7mDE/MjK79ktrgK$0nvNYBNFmDDZ/DfxHHtwleV42.VeTFrE5Cs92MOdqr8pT/MHS3PLO8XinUK/5mimCpo8JQ6O5KhceV175bcfJ1', 'groups': 'menta', 'add_authorized_keys': True, 'add_sudoers': True, 'priv': 'menta'}}) TASK [users : Deploy temporary sudoers files] *************************************************************************************************************************** ok: [dev.techbull.cloud] => (item={'key': 'menta', 'value': {'comment': 'Add User menta', 'initial_password': '$6$rounds=656000$KVdM9tLPS8yTZDNe$Cg0fEJTb7VGabXSldNNXKyAB4GvXYukW9k13qW3vBVw0PvV1tRhDZXFsBGQwfLDc7ZLga9GGlL.JztgS8pfVc/', 'groups': 'menta', 'add_authorized_keys': True, 'add_sudoers': True, 'priv': 'menta'}}) PLAY [wp_servers] ******************************************************************************************************************************************************* TASK [nginx : Ensure Nginx is installed] ******************************************************************************************************************************** ok: [dev.techbull.cloud] TASK [nginx : Ensure Nginx is running] ********************************************************************************************************************************** ok: [dev.techbull.cloud] 〜(中略)〜 RUNNING HANDLER [wordpress : Restart PHP-FPM] *************************************************************************************************************************** changed: [dev.techbull.cloud] RUNNING HANDLER [wordpress : Restart Nginx] ***************************************************************************************************************************** changed: [dev.techbull.cloud] RUNNING HANDLER [wordpress : Restart MySQL] ***************************************************************************************************************************** changed: [dev.techbull.cloud] PLAY RECAP ************************************************************************************************************************************************************** dev.techbull.cloud : ok=42 changed=11 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 |
実行完了後、https://wp.techbull.cloudにアクセスします。初回のユーザやパスワードを設定し、ダッシュボードが表示されることを確認して完了になります。
まとめ
本記事では、WordPress環境の構築をAnsibleで自動化してみました。プレイブックを実行すれば、タスクの内容とボリュームにもよりますが、数十秒で環境構築できる点で、短時間で効率的に構築ができることがわかりました。
またコードの可読性をあげられるように他のAnsible変数やモジュールなどがたくさんあるので、これらを使用して環境構築することで、よりAnsibleへの理解を深めていきたいと思います。ここまで読んでいただきありがとうございました。
(参考)
書籍:https://book.impress.co.jp/books/1122101189
告知
6/4にTechBull主催のLT会があります!僕も参加しますので、ぜひ皆さん参加お待ちしております!

1999年生まれ、今年でエンジニア歴4年目のインフラエンジニア。ヘルプデスクとオンプレミス環境の運用をそれぞれ1年間経験。2025年よりAWSを活用したデータ分析基盤の設計構築案件に参画。AWS案件参画に向け、資格取得を中心に学習に励む。さらなるステップアップとしてWeb系エンジニアへの転身を視野に入れる中、YouTubeチャンネル「TECH WORLD」にて@adachin0817の活動を知り、TechBullに参加。実践的なスキル習得を目指し、新たな領域への挑戦を決意。