<h1 class="wp-block-heading">wp-cliによるWordPress管理自動化の効率化と堅牢化</h1>
<p><!--META
{
"title": "wp-cliによるWordPress管理自動化の効率化と堅牢化",
"primary_category": "DevOps",
"secondary_categories": ["WordPress","CLI"],
"tags": ["wp-cli", "systemd", "jq", "curl", "bash", "automation", "WordPress"],
"summary": "wp-cliを活用したWordPress管理自動化の堅牢な実装方法を、idempotentなBashスクリプト、systemdタイマー、jq/curlの利用例とともに解説します。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"wp-cliでWordPress管理を自動化!堅牢なBashスクリプト、systemdタイマー、jq/curlを使った実践的な手法を解説。DevOpsエンジニア必見です。 #wpcli
#DevOps","hashtags":["#wpcli","#DevOps"]},
"link_hints": ["https://wp-cli.org/docs/", "https://www.freedesktop.org/software/systemd/man/systemd.timer.html"]
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h2 class="wp-block-heading">要件と前提</h2>
<h3 class="wp-block-heading">要件</h3>
<ul class="wp-block-list">
<li><p>WordPress管理タスクの自動化(例:コア、プラグイン、テーマの更新、データベース最適化など)。</p></li>
<li><p>堅牢で冪等性(idempotent)のあるBashスクリプトの利用。</p></li>
<li><p><code>systemd unit/timer</code>によるスケジュール実行。</p></li>
<li><p><code>jq</code>と<code>curl</code>を用いた高度な処理(JSON解析、API連携)。</p></li>
<li><p>セキュリティと権限分離への配慮。</p></li>
</ul>
<h3 class="wp-block-heading">前提</h3>
<ul class="wp-block-list">
<li><p>Linuxサーバー環境(<code>systemd</code>が利用可能)。</p></li>
<li><p>WordPressが動作しており、<code>wp-cli</code>がインストール済み(推奨バージョンは2.9.0以降、2024年5月現在)。</p>
<ul>
<li>参考: <a href="https://wp-cli.org/docs/">wp-cli.org Handbook</a> (2024年5月15日 JST時点の情報に基づく)</li>
</ul></li>
<li><p><code>jq</code>と<code>curl</code>がシステムにインストール済み。</p></li>
<li><p>自動化スクリプトを実行する専用ユーザーが存在し、WordPressファイルの読み書き権限を持つこと。<code>root</code>権限を直接用いない。</p></li>
</ul>
<h2 class="wp-block-heading">実装</h2>
<h3 class="wp-block-heading">自動化ワークフローの概要</h3>
<p>自動化スクリプトの全体的な流れを以下のMermaidフローチャートで示します。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["systemd Timer"] --> B{"Bash Script 実行"};
B --> C["環境準備"];
C --> D{"wp-cli コマンド実行"};
D --> E["データベース最適化"];
D --> F["コア更新"];
D --> G["プラグイン/テーマ更新"];
G --> H{"結果判定"};
H -- |成功| --> I["成功通知 (curl)"];
H -- |失敗| --> J["失敗通知 (curl)"];
I --> K["一時ディレクトリクリーンアップ"];
J --> K;
</pre></div>
<h3 class="wp-block-heading">冪等性を持つBashスクリプトの作成</h3>
<p>以下の<code>wp-cli-maintain.sh</code>スクリプトは、WordPressの自動メンテナンスタスクを実行します。<code>set -euo pipefail</code>による厳格なエラーハンドリング、<code>trap</code>コマンドによるクリーンアップ、そして一時ディレクトリの利用により、堅牢性と冪等性を高めています。<code>wp-cli</code>コマンドは<code>sudo -u</code>を用いて、Webサーバーが利用するユーザー(例:<code>www-data</code>)として実行します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
#
# WordPress自動メンテナンススクリプト
# wp-cli, jq, curl を用いてWordPressのコア/プラグイン/テーマ更新、DB最適化、ヘルスチェック通知を行います。
#
# 実行ユーザー: webサーバーを実行するユーザー (例: www-data, nginx)
# 依存ツール: wp-cli, jq, curl
#
# --- 1. スクリプトの堅牢化設定 ---
# 実行中にエラーが発生した場合、スクリプトを即座に終了させる
set -euo pipefail
# 一時ディレクトリのパスを定義 (スクリプト実行ごとに固有のディレクトリを使用)
# mktemp -d コマンドは、一時ディレクトリを作成し、そのパスを返す
# -p /tmp は、/tmp ディレクトリ配下に一時ディレクトリを作成する指定
TMP_DIR=$(mktemp -d -p /tmp wp-cli-auto.XXXXXX)
# スクリプト終了時に一時ファイルをクリーンアップするトラップを設定
# ERRシグナルは、スクリプトがエラーで終了した場合に実行される
# EXITシグナルは、スクリプトが正常終了または異常終了した場合に常に実行される
trap 'rm -rf "$TMP_DIR"; echo "$(date +%Y-%m-%d_%H:%M:%S) : Temporary directory $TMP_DIR cleaned up." >&2' ERR EXIT
# WordPressのインストールパス
# 必要に応じて変更してください
WP_PATH="/var/www/html/wordpress"
# wp-cliコマンドを実行するユーザー (Webサーバーの実行ユーザーに合わせる)
# 例: Debian/Ubuntuなら www-data, RHEL/CentOSなら apache/nginx
WP_USER="www-data"
# SlackやMattermostなどへの通知用Webhook URL (任意)
# 通知が不要な場合は空欄にするか、curl部分を削除してください
WEBHOOK_URL="" # 例: https://hooks.slack.com/services/XXXXXXXXX/YYYYYYYYY/ZZZZZZZZZZZZZZZZZZZZZZZZ
# ログファイルパス
LOG_FILE="${TMP_DIR}/wp-cli-maintenance-$(date +%Y%m%d%H%M%S).log"
# --- 2. 関数定義 ---
# wp-cliコマンドを安全に実行するヘルパー関数
# root権限を直接使わず、WP_USERとして実行
run_wp_cli() {
echo "$(date +%Y-%m-%d_%H:%M:%S) : Executing wp-cli as user '$WP_USER': wp $*" | tee -a "$LOG_FILE"
# sudo -u ${WP_USER} は、指定されたユーザーとしてコマンドを実行
# -p ${WP_PATH} は、wp-cliがWordPressのインストールディレクトリを正しく認識できるようにする
sudo -u "${WP_USER}" wp --path="${WP_PATH}" "$@" 2>&1 | tee -a "$LOG_FILE"
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : ERROR: wp-cli command failed: wp $*" | tee -a "$LOG_FILE"
return 1
fi
return 0
}
# Webhook通知を送信する関数 (curlとjqを使用)
send_notification() {
local status="$1"
local message="$2"
if [[ -z "$WEBHOOK_URL" ]]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : INFO: Webhook URL is not set. Skipping notification." | tee -a "$LOG_FILE"
return 0
}
local payload
if [[ "$status" == "SUCCESS" ]]; then
payload=$(jq -n --arg msg "$message" '{"text": "[SUCCESS] WordPress自動メンテナンス: \($msg)", "color": "good"}')
else
payload=$(jq -n --arg msg "$message" '{"text": "[FAILURE] WordPress自動メンテナンス: \($msg)", "color": "danger"}')
fi
# curlを堅牢に実行:
# --max-time 10: 最大実行時間10秒
# --retry 5: リクエストが失敗した場合、最大5回再試行
# --retry-delay 5: 最初のリトライまでの待機時間5秒
# --retry-max-time 60: リトライ試行の合計最大時間60秒
# --connect-timeout 5: 接続確立のタイムアウト5秒
# -sS: サイレントモードでエラー時のみ表示
# -X POST: POSTリクエスト
# -H "Content-Type: application/json": JSON形式のContent-Typeヘッダー
# -d @-: 標準入力からリクエストボディを読み込む
echo "$payload" | curl --max-time 10 --retry 5 --retry-delay 5 --retry-max-time 60 \
--connect-timeout 5 -sS -X POST \
-H "Content-Type: application/json" \
-d @- "$WEBHOOK_URL" 2>&1 | tee -a "$LOG_FILE"
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : ERROR: Failed to send notification via curl." | tee -a "$LOG_FILE"
return 1
fi
echo "$(date +%Y-%m-%d_%H:%M:%S) : INFO: Notification sent ($status)." | tee -a "$LOG_FILE"
return 0
}
# --- 3. メイン処理 ---
echo "$(date +%Y-%m-%d_%H:%M:%S) : Starting WordPress automatic maintenance script." | tee -a "$LOG_FILE"
echo "Log file: $LOG_FILE" | tee -a "$LOG_FILE"
# WordPressのインストール状態を確認
if ! run_wp_cli core is-installed; then
send_notification "FAILURE" "WordPress installation not found or inaccessible at ${WP_PATH}."
exit 1
fi
maintenance_status="SUCCESS"
maintenance_message="すべてのタスクが正常に完了しました。"
# データベースの最適化とクリーンアップ
echo "$(date +%Y-%m-%d_%H:%M:%S) : Optimizing WordPress database..." | tee -a "$LOG_FILE"
if ! run_wp_cli db optimize; then
maintenance_status="FAILURE"
maintenance_message="データベースの最適化に失敗しました。"
fi
if ! run_wp_cli db clean; then
maintenance_status="FAILURE"
maintenance_message="データベースのクリーンアップに失敗しました。"
fi
# WordPressコアの更新
echo "$(date +%Y-%m-%d_%H:%M:%S) : Checking for WordPress core updates..." | tee -a "$LOG_FILE"
CORE_UPDATE_INFO=$(run_wp_cli core check-update --format=json || echo "[]")
if [ "$(echo "$CORE_UPDATE_INFO" | jq 'length')" -gt 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : WordPress core update available. Updating..." | tee -a "$LOG_FILE"
if ! run_wp_cli core update; then
maintenance_status="FAILURE"
maintenance_message="WordPressコアの更新に失敗しました。"
fi
else
echo "$(date +%Y-%m-%d_%H:%M:%S) : WordPress core is up to date." | tee -a "$LOG_FILE"
fi
# プラグインの更新
echo "$(date +%Y-%m-%d_%H:%M:%S) : Checking for plugin updates..." | tee -a "$LOG_FILE"
PLUGIN_UPDATES=$(run_wp_cli plugin list --update=available --format=json || echo "[]")
if [ "$(echo "$PLUGIN_UPDATES" | jq 'length')" -gt 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : Plugin updates available. Updating all plugins..." | tee -a "$LOG_FILE"
if ! run_wp_cli plugin update --all; then
maintenance_status="FAILURE"
maintenance_message="プラグインの更新に失敗しました。"
fi
else
echo "$(date +%Y-%m-%d_%H:%M:%S) : All plugins are up to date." | tee -a "$LOG_FILE"
fi
# テーマの更新
echo "$(date +%Y-%m-%d_%H:%M:%S) : Checking for theme updates..." | tee -a "$LOG_FILE"
THEME_UPDATES=$(run_wp_cli theme list --update=available --format=json || echo "[]")
if [ "$(echo "$THEME_UPDATES" | jq 'length')" -gt 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : Theme updates available. Updating all themes..." | tee -a "$LOG_FILE"
if ! run_wp_cli theme update --all; then
maintenance_status="FAILURE"
maintenance_message="テーマの更新に失敗しました。"
fi
else
echo "$(date +%Y-%m-%d_%H:%M:%S) : All themes are up to date." | tee -a "$LOG_FILE"
fi
# サイトヘルスチェック (任意)
echo "$(date +%Y-%m-%d_%H:%M:%S) : Running site health check..." | tee -a "$LOG_FILE"
HEALTH_STATUS=$(run_wp_cli site-health check status --format=json || echo "{}")
SITE_HEALTH_STATUS_CODE=$(echo "$HEALTH_STATUS" | jq -r '.status.status // "unknown"')
if [[ "$SITE_HEALTH_STATUS_CODE" != "good" ]]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : WARNING: Site health check reported status: $SITE_HEALTH_STATUS_CODE" | tee -a "$LOG_FILE"
# 自動更新の失敗とは異なるため、ここではmaintenance_statusをFAILUREに直接変更しない
fi
echo "$(date +%Y-%m-%d_%H:%M:%S) : WordPress automatic maintenance script finished with status: $maintenance_status." | tee -a "$LOG_FILE"
# --- 4. 結果通知 ---
send_notification "$maintenance_status" "$maintenance_message"
exit 0
</pre>
</div>
<h4 class="wp-block-heading">root権限の扱いと権限分離</h4>
<p><code>wp-cli</code>コマンドはWordPressのファイルやデータベースに直接アクセスするため、適切な権限で実行する必要があります。<strong><code>root</code>ユーザーで直接<code>wp-cli</code>を実行することは避けてください。</strong>
WordPressが動作するWebサーバーのユーザー(例:Debian/Ubuntuの<code>www-data</code>、RHEL/CentOSの<code>apache</code>または<code>nginx</code>)として実行するのが最も安全です。
上記のスクリプトでは、<code>sudo -u "${WP_USER}" wp ...</code>とすることで、スクリプト自体はより高い権限(通常は<code>root</code>または<code>sudo</code>グループのユーザー)で実行しつつ、<code>wp-cli</code>コマンドは指定された非特権ユーザー(<code>www-data</code>など)として実行されます。これにより、必要最小限の権限で操作を行えます。
<code>sudoers</code>ファイルに<code>NOPASSWD</code>設定を加えることで、パスワードなしで<code>sudo -u ${WP_USER}</code>を実行できますが、その場合はスクリプトのセキュリティ管理に細心の注意が必要です。</p>
<h3 class="wp-block-heading">systemd unit/timerの作成</h3>
<p>自動化スクリプトを定期的に実行するために、<code>systemd timer</code>を使用します。これにより、従来の<code>cron</code>よりも高度な機能(依存関係、リソース制御、詳細なロギング)が利用できます。
参考: <a href="https://www.freedesktop.org/software/systemd/man/systemd.timer.html">systemd.timer man page</a> (2024年4月10日 JST時点の情報に基づく)</p>
<h4 class="wp-block-heading">systemd Service Unitファイル (<code>/etc/systemd/system/wp-cli-maintain.service</code>)</h4>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=WordPress CLI Maintenance Service
Documentation=https://wp-cli.org/
# After=network.target は、ネットワークが利用可能になった後にサービスを起動することを示す。
# 例えば、通知のためのcurlコマンドがネットワーク接続を必要とする場合に有用。
After=network.target
[Service]
# Type=oneshot は、コマンドが一度実行され、終了したらサービスも終了するタイプであることを示す。
Type=oneshot
# User=root でスクリプトを実行し、スクリプト内で wp-cli コマンドを www-data ユーザーに切り替える。
# これにより、systemd timer を root で管理しつつ、wp-cli 操作は非特権ユーザーで行える。
User=root
# WorkingDirectory は、サービスが実行される作業ディレクトリ。
# スクリプトが参照するパスが相対パスの場合に重要。
WorkingDirectory=/var/www/html/wordpress
# ExecStart は、サービスが起動したときに実行されるコマンド。
# /usr/local/bin/wp-cli-maintain.sh は、先ほど作成したスクリプトのフルパス。
ExecStart=/usr/local/bin/wp-cli-maintain.sh
# StandardOutput と StandardError は、標準出力と標準エラーのログをjournalctlにリダイレクトする。
# journalctl -u wp-cli-maintain.service でログを確認できる。
StandardOutput=journal
StandardError=journal
# Restart=on-failure は、サービスが失敗した場合に再起動を試みる設定。
# Type=oneshotの場合、スクリプトの終了コードによって失敗と判定される。
Restart=on-failure
RestartSec=5
# PrivateTmp=true は、サービスに専用の一時ディレクトリを提供し、セキュリティを向上させる。
PrivateTmp=true
[Install]
# WantedBy=multi-user.target は、システムがマルチユーザーモードになったときにサービスが有効になることを示す。
# systemd timer から起動されるため、直接有効化されることは少ないが、良いプラクティスとして記述。
WantedBy=multi-user.target
</pre>
</div>
<h4 class="wp-block-heading">systemd Timer Unitファイル (<code>/etc/systemd/system/wp-cli-maintain.timer</code>)</h4>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=Run WordPress CLI Maintenance daily
# After=wp-cli-maintain.service は、このタイマーがサービスが起動した後に有効になることを示す。
# 必ずしも必要ではないが、関連付けることで依存関係を明確にする。
After=wp-cli-maintain.service
[Timer]
# OnCalendar は、タイマーのスケジュールを設定。
# "daily" は毎日実行。 "weekly" や "Mon *-*-* 03:00:00" (毎週月曜日の午前3時) なども設定可能。
# JST基準で毎日午前3時30分に実行。
OnCalendar=*-*-* 03:30:00
# Persistent=true は、タイマーが停止している間に予定された実行がスキップされた場合、
# タイマーが再度起動したときにすぐにその実行を試みるようにする (起動時のキャッチアップ)。
Persistent=true
[Install]
# WantedBy=timers.target は、システムがタイマー機能を有効にしたときにこのタイマーも有効になることを示す。
WantedBy=timers.target
</pre>
</div>
<h2 class="wp-block-heading">検証</h2>
<ol class="wp-block-list">
<li><p><strong>スクリプトの配置と実行権限付与:</strong>
上記のBashスクリプトを<code>/usr/local/bin/wp-cli-maintain.sh</code>として保存し、実行権限を付与します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo install -m 755 wp-cli-maintain.sh /usr/local/bin/
</pre>
</div></li>
<li><p><strong>手動でのスクリプト実行テスト:</strong>
<code>sudo</code>コマンドでスクリプトを手動実行し、期待通りに動作するか検証します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo /usr/local/bin/wp-cli-maintain.sh
</pre>
</div>
<p>実行後、スクリプトが出力したログファイル(一時ディレクトリ内)を確認し、WordPressの管理画面でコア、プラグイン、テーマが更新されているかを確認します。</p></li>
<li><p><strong>systemd Unit/Timerファイルの有効化と起動:</strong>
作成した<code>.service</code>と<code>.timer</code>ファイルを<code>/etc/systemd/system/</code>に配置し、以下のコマンドでsystemdに認識させ、タイマーを有効化・起動します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># systemdに新しいユニットファイルを認識させる
sudo systemctl daemon-reload
# タイマーを有効化 (システム起動時に自動起動するように設定)
sudo systemctl enable wp-cli-maintain.timer
# タイマーを起動
sudo systemctl start wp-cli-maintain.timer
</pre>
</div></li>
<li><p><strong>systemd Timerの状態確認:</strong>
タイマーが正しく設定され、次回実行日時が表示されるか確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo systemctl list-timers | grep wp-cli-maintain
# NEXT欄に次回実行日時 (JST) が表示されることを確認してください。
</pre>
</div></li>
<li><p><strong>systemd Serviceのログ確認:</strong>
タイマーが実行された後、サービスが正常に動作したかログを確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo journalctl -u wp-cli-maintain.service --since "today"
</pre>
</div>
<p><code>StandardOutput=journal</code>と<code>StandardError=journal</code>により、スクリプトの標準出力および標準エラー出力が<code>journalctl</code>に記録されます。</p></li>
</ol>
<h2 class="wp-block-heading">運用</h2>
<h3 class="wp-block-heading">監視</h3>
<ul class="wp-block-list">
<li><p><code>journalctl</code>で定期的に<code>wp-cli-maintain.service</code>のログを確認し、エラーが発生していないか監視します。</p></li>
<li><p>Webhook通知が設定されている場合は、通知システム(Slack, Mattermostなど)で正常に通知が届いているか確認します。</p></li>
<li><p>WordPressの管理画面で、サイトヘルスや利用可能な更新がないか、定期的に手動で確認することも重要です。</p></li>
</ul>
<h3 class="wp-block-heading">権限管理</h3>
<ul class="wp-block-list">
<li><p><code>WP_USER</code>に設定したユーザー(<code>www-data</code>など)が、WordPressのファイルディレクトリに対して適切な読み書き権限を持っていることを常に確認してください。</p></li>
<li><p><code>sudoers</code>ファイルを編集して<code>NOPASSWD</code>オプションを使用する場合は、そのスクリプトが改ざんされないよう厳重に管理し、必要最小限のコマンドのみに適用することを検討してください。</p></li>
</ul>
<h3 class="wp-block-heading">アップデートと保守</h3>
<ul class="wp-block-list">
<li><p><code>wp-cli</code>自体も定期的に更新されます。<code>wp cli update</code>コマンドで最新版に保つようにしてください。</p></li>
<li><p>スクリプト内の<code>WP_PATH</code>や<code>WEBHOOK_URL</code>などの設定は、環境変更に応じて適切に更新してください。</p></li>
<li><p>WordPressのメジャーバージョンアップ時など、自動更新が適切でない場合は、一時的にタイマーを無効化し、手動で更新と検証を行うことを検討してください。</p></li>
</ul>
<h2 class="wp-block-heading">トラブルシュート</h2>
<h3 class="wp-block-heading">スクリプトが実行されない</h3>
<ul class="wp-block-list">
<li><p><code>sudo systemctl list-timers --all</code> でタイマーが有効(<code>ACTIVATES</code>に<code>wp-cli-maintain.service</code>が表示される)で、次回実行日時(<code>NEXT</code>)が正しく設定されているか確認します。</p></li>
<li><p><code>sudo systemctl status wp-cli-maintain.timer</code> でタイマーユニットの状態を確認します。</p></li>
<li><p><code>sudo systemctl status wp-cli-maintain.service</code> でサービスユニットの状態を確認します。</p></li>
<li><p><code>sudo systemctl daemon-reload</code> が実行されているか確認します。</p></li>
</ul>
<h3 class="wp-block-heading">スクリプトが失敗する</h3>
<ul class="wp-block-list">
<li><p><code>sudo journalctl -u wp-cli-maintain.service --since "1 hour ago"</code> で詳細なログを確認します。特にエラーメッセージに注目します。</p></li>
<li><p><code>WP_PATH</code>が正しいか、<code>WP_USER</code>がWordPressファイルへの適切な権限を持っているか確認します。</p>
<ul>
<li><code>ls -l /var/www/html/wordpress</code> などで所有者と権限を確認し、必要であれば<code>sudo chown -R www-data:www-data /var/www/html/wordpress</code>などで修正します。</li>
</ul></li>
<li><p><code>wp-cli</code>コマンド自体が手動で<code>sudo -u www-data wp --path=/var/www/html/wordpress ...</code>として実行した際に成功するか確認します。</p></li>
<li><p><code>jq</code>や<code>curl</code>コマンドがパス通っているか、構文が正しいか確認します。</p></li>
<li><p>ネットワークの問題で<code>curl</code>による通知が失敗していないか、ファイアウォール設定を確認します。</p></li>
</ul>
<h3 class="wp-block-heading">一時ファイルが残る</h3>
<ul class="wp-block-list">
<li><p><code>trap</code>コマンドが正しく設定されているか確認します。<code>ERR</code>と<code>EXIT</code>シグナルが捕捉されていることを確認します。</p></li>
<li><p>スクリプトが異常終了した場合でも<code>trap</code>が機能するはずですが、強制終了(<code>kill -9</code>など)された場合はクリーンアップされないことがあります。</p></li>
</ul>
<h2 class="wp-block-heading">まとめ</h2>
<p>、<code>wp-cli</code>を活用したWordPress管理自動化の堅牢な実装方法をDevOpsエンジニアの視点から解説しました。
冪等性を意識したBashスクリプトは<code>set -euo pipefail</code>や<code>trap</code>を用いることで予期せぬエラーにも対応し、<code>sudo -u</code>による権限分離でセキュリティを確保します。
<code>systemd timer</code>は、<code>cron</code>に代わる高機能なスケジューリングツールとして、安定した自動運用を可能にします。
また、<code>jq</code>によるJSON処理と<code>curl</code>によるHTTP通信の堅牢な利用例は、通知システムとの連携や外部APIとの統合に役立ちます。</p>
<p>これらの技術を組み合わせることで、WordPressのメンテナンス作業を効率化し、システムの安定性とセキュリティを向上させることができます。しかし、自動化は万能ではありません。定期的な監視と手動での確認、そしてスクリプトやWordPress自体のアップデートを怠らないことが重要です。</p>
wp-cliによるWordPress管理自動化の効率化と堅牢化
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
要件と前提
要件
WordPress管理タスクの自動化(例:コア、プラグイン、テーマの更新、データベース最適化など)。
堅牢で冪等性(idempotent)のあるBashスクリプトの利用。
systemd unit/timerによるスケジュール実行。
jqとcurlを用いた高度な処理(JSON解析、API連携)。
セキュリティと権限分離への配慮。
前提
Linuxサーバー環境(systemdが利用可能)。
WordPressが動作しており、wp-cliがインストール済み(推奨バージョンは2.9.0以降、2024年5月現在)。
jqとcurlがシステムにインストール済み。
自動化スクリプトを実行する専用ユーザーが存在し、WordPressファイルの読み書き権限を持つこと。root権限を直接用いない。
実装
自動化ワークフローの概要
自動化スクリプトの全体的な流れを以下のMermaidフローチャートで示します。
graph TD
A["systemd Timer"] --> B{"Bash Script 実行"};
B --> C["環境準備"];
C --> D{"wp-cli コマンド実行"};
D --> E["データベース最適化"];
D --> F["コア更新"];
D --> G["プラグイン/テーマ更新"];
G --> H{"結果判定"};
H -- |成功| --> I["成功通知 (curl)"];
H -- |失敗| --> J["失敗通知 (curl)"];
I --> K["一時ディレクトリクリーンアップ"];
J --> K;
冪等性を持つBashスクリプトの作成
以下のwp-cli-maintain.shスクリプトは、WordPressの自動メンテナンスタスクを実行します。set -euo pipefailによる厳格なエラーハンドリング、trapコマンドによるクリーンアップ、そして一時ディレクトリの利用により、堅牢性と冪等性を高めています。wp-cliコマンドはsudo -uを用いて、Webサーバーが利用するユーザー(例:www-data)として実行します。
#!/usr/bin/env bash
#
# WordPress自動メンテナンススクリプト
# wp-cli, jq, curl を用いてWordPressのコア/プラグイン/テーマ更新、DB最適化、ヘルスチェック通知を行います。
#
# 実行ユーザー: webサーバーを実行するユーザー (例: www-data, nginx)
# 依存ツール: wp-cli, jq, curl
#
# --- 1. スクリプトの堅牢化設定 ---
# 実行中にエラーが発生した場合、スクリプトを即座に終了させる
set -euo pipefail
# 一時ディレクトリのパスを定義 (スクリプト実行ごとに固有のディレクトリを使用)
# mktemp -d コマンドは、一時ディレクトリを作成し、そのパスを返す
# -p /tmp は、/tmp ディレクトリ配下に一時ディレクトリを作成する指定
TMP_DIR=$(mktemp -d -p /tmp wp-cli-auto.XXXXXX)
# スクリプト終了時に一時ファイルをクリーンアップするトラップを設定
# ERRシグナルは、スクリプトがエラーで終了した場合に実行される
# EXITシグナルは、スクリプトが正常終了または異常終了した場合に常に実行される
trap 'rm -rf "$TMP_DIR"; echo "$(date +%Y-%m-%d_%H:%M:%S) : Temporary directory $TMP_DIR cleaned up." >&2' ERR EXIT
# WordPressのインストールパス
# 必要に応じて変更してください
WP_PATH="/var/www/html/wordpress"
# wp-cliコマンドを実行するユーザー (Webサーバーの実行ユーザーに合わせる)
# 例: Debian/Ubuntuなら www-data, RHEL/CentOSなら apache/nginx
WP_USER="www-data"
# SlackやMattermostなどへの通知用Webhook URL (任意)
# 通知が不要な場合は空欄にするか、curl部分を削除してください
WEBHOOK_URL="" # 例: https://hooks.slack.com/services/XXXXXXXXX/YYYYYYYYY/ZZZZZZZZZZZZZZZZZZZZZZZZ
# ログファイルパス
LOG_FILE="${TMP_DIR}/wp-cli-maintenance-$(date +%Y%m%d%H%M%S).log"
# --- 2. 関数定義 ---
# wp-cliコマンドを安全に実行するヘルパー関数
# root権限を直接使わず、WP_USERとして実行
run_wp_cli() {
echo "$(date +%Y-%m-%d_%H:%M:%S) : Executing wp-cli as user '$WP_USER': wp $*" | tee -a "$LOG_FILE"
# sudo -u ${WP_USER} は、指定されたユーザーとしてコマンドを実行
# -p ${WP_PATH} は、wp-cliがWordPressのインストールディレクトリを正しく認識できるようにする
sudo -u "${WP_USER}" wp --path="${WP_PATH}" "$@" 2>&1 | tee -a "$LOG_FILE"
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : ERROR: wp-cli command failed: wp $*" | tee -a "$LOG_FILE"
return 1
fi
return 0
}
# Webhook通知を送信する関数 (curlとjqを使用)
send_notification() {
local status="$1"
local message="$2"
if [[ -z "$WEBHOOK_URL" ]]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : INFO: Webhook URL is not set. Skipping notification." | tee -a "$LOG_FILE"
return 0
}
local payload
if [[ "$status" == "SUCCESS" ]]; then
payload=$(jq -n --arg msg "$message" '{"text": "[SUCCESS] WordPress自動メンテナンス: \($msg)", "color": "good"}')
else
payload=$(jq -n --arg msg "$message" '{"text": "[FAILURE] WordPress自動メンテナンス: \($msg)", "color": "danger"}')
fi
# curlを堅牢に実行:
# --max-time 10: 最大実行時間10秒
# --retry 5: リクエストが失敗した場合、最大5回再試行
# --retry-delay 5: 最初のリトライまでの待機時間5秒
# --retry-max-time 60: リトライ試行の合計最大時間60秒
# --connect-timeout 5: 接続確立のタイムアウト5秒
# -sS: サイレントモードでエラー時のみ表示
# -X POST: POSTリクエスト
# -H "Content-Type: application/json": JSON形式のContent-Typeヘッダー
# -d @-: 標準入力からリクエストボディを読み込む
echo "$payload" | curl --max-time 10 --retry 5 --retry-delay 5 --retry-max-time 60 \
--connect-timeout 5 -sS -X POST \
-H "Content-Type: application/json" \
-d @- "$WEBHOOK_URL" 2>&1 | tee -a "$LOG_FILE"
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : ERROR: Failed to send notification via curl." | tee -a "$LOG_FILE"
return 1
fi
echo "$(date +%Y-%m-%d_%H:%M:%S) : INFO: Notification sent ($status)." | tee -a "$LOG_FILE"
return 0
}
# --- 3. メイン処理 ---
echo "$(date +%Y-%m-%d_%H:%M:%S) : Starting WordPress automatic maintenance script." | tee -a "$LOG_FILE"
echo "Log file: $LOG_FILE" | tee -a "$LOG_FILE"
# WordPressのインストール状態を確認
if ! run_wp_cli core is-installed; then
send_notification "FAILURE" "WordPress installation not found or inaccessible at ${WP_PATH}."
exit 1
fi
maintenance_status="SUCCESS"
maintenance_message="すべてのタスクが正常に完了しました。"
# データベースの最適化とクリーンアップ
echo "$(date +%Y-%m-%d_%H:%M:%S) : Optimizing WordPress database..." | tee -a "$LOG_FILE"
if ! run_wp_cli db optimize; then
maintenance_status="FAILURE"
maintenance_message="データベースの最適化に失敗しました。"
fi
if ! run_wp_cli db clean; then
maintenance_status="FAILURE"
maintenance_message="データベースのクリーンアップに失敗しました。"
fi
# WordPressコアの更新
echo "$(date +%Y-%m-%d_%H:%M:%S) : Checking for WordPress core updates..." | tee -a "$LOG_FILE"
CORE_UPDATE_INFO=$(run_wp_cli core check-update --format=json || echo "[]")
if [ "$(echo "$CORE_UPDATE_INFO" | jq 'length')" -gt 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : WordPress core update available. Updating..." | tee -a "$LOG_FILE"
if ! run_wp_cli core update; then
maintenance_status="FAILURE"
maintenance_message="WordPressコアの更新に失敗しました。"
fi
else
echo "$(date +%Y-%m-%d_%H:%M:%S) : WordPress core is up to date." | tee -a "$LOG_FILE"
fi
# プラグインの更新
echo "$(date +%Y-%m-%d_%H:%M:%S) : Checking for plugin updates..." | tee -a "$LOG_FILE"
PLUGIN_UPDATES=$(run_wp_cli plugin list --update=available --format=json || echo "[]")
if [ "$(echo "$PLUGIN_UPDATES" | jq 'length')" -gt 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : Plugin updates available. Updating all plugins..." | tee -a "$LOG_FILE"
if ! run_wp_cli plugin update --all; then
maintenance_status="FAILURE"
maintenance_message="プラグインの更新に失敗しました。"
fi
else
echo "$(date +%Y-%m-%d_%H:%M:%S) : All plugins are up to date." | tee -a "$LOG_FILE"
fi
# テーマの更新
echo "$(date +%Y-%m-%d_%H:%M:%S) : Checking for theme updates..." | tee -a "$LOG_FILE"
THEME_UPDATES=$(run_wp_cli theme list --update=available --format=json || echo "[]")
if [ "$(echo "$THEME_UPDATES" | jq 'length')" -gt 0 ]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : Theme updates available. Updating all themes..." | tee -a "$LOG_FILE"
if ! run_wp_cli theme update --all; then
maintenance_status="FAILURE"
maintenance_message="テーマの更新に失敗しました。"
fi
else
echo "$(date +%Y-%m-%d_%H:%M:%S) : All themes are up to date." | tee -a "$LOG_FILE"
fi
# サイトヘルスチェック (任意)
echo "$(date +%Y-%m-%d_%H:%M:%S) : Running site health check..." | tee -a "$LOG_FILE"
HEALTH_STATUS=$(run_wp_cli site-health check status --format=json || echo "{}")
SITE_HEALTH_STATUS_CODE=$(echo "$HEALTH_STATUS" | jq -r '.status.status // "unknown"')
if [[ "$SITE_HEALTH_STATUS_CODE" != "good" ]]; then
echo "$(date +%Y-%m-%d_%H:%M:%S) : WARNING: Site health check reported status: $SITE_HEALTH_STATUS_CODE" | tee -a "$LOG_FILE"
# 自動更新の失敗とは異なるため、ここではmaintenance_statusをFAILUREに直接変更しない
fi
echo "$(date +%Y-%m-%d_%H:%M:%S) : WordPress automatic maintenance script finished with status: $maintenance_status." | tee -a "$LOG_FILE"
# --- 4. 結果通知 ---
send_notification "$maintenance_status" "$maintenance_message"
exit 0
root権限の扱いと権限分離
wp-cliコマンドはWordPressのファイルやデータベースに直接アクセスするため、適切な権限で実行する必要があります。rootユーザーで直接wp-cliを実行することは避けてください。
WordPressが動作するWebサーバーのユーザー(例:Debian/Ubuntuのwww-data、RHEL/CentOSのapacheまたはnginx)として実行するのが最も安全です。
上記のスクリプトでは、sudo -u "${WP_USER}" wp ...とすることで、スクリプト自体はより高い権限(通常はrootまたはsudoグループのユーザー)で実行しつつ、wp-cliコマンドは指定された非特権ユーザー(www-dataなど)として実行されます。これにより、必要最小限の権限で操作を行えます。
sudoersファイルにNOPASSWD設定を加えることで、パスワードなしでsudo -u ${WP_USER}を実行できますが、その場合はスクリプトのセキュリティ管理に細心の注意が必要です。
systemd unit/timerの作成
自動化スクリプトを定期的に実行するために、systemd timerを使用します。これにより、従来のcronよりも高度な機能(依存関係、リソース制御、詳細なロギング)が利用できます。
参考: systemd.timer man page (2024年4月10日 JST時点の情報に基づく)
systemd Service Unitファイル (/etc/systemd/system/wp-cli-maintain.service)
[Unit]
Description=WordPress CLI Maintenance Service
Documentation=https://wp-cli.org/
# After=network.target は、ネットワークが利用可能になった後にサービスを起動することを示す。
# 例えば、通知のためのcurlコマンドがネットワーク接続を必要とする場合に有用。
After=network.target
[Service]
# Type=oneshot は、コマンドが一度実行され、終了したらサービスも終了するタイプであることを示す。
Type=oneshot
# User=root でスクリプトを実行し、スクリプト内で wp-cli コマンドを www-data ユーザーに切り替える。
# これにより、systemd timer を root で管理しつつ、wp-cli 操作は非特権ユーザーで行える。
User=root
# WorkingDirectory は、サービスが実行される作業ディレクトリ。
# スクリプトが参照するパスが相対パスの場合に重要。
WorkingDirectory=/var/www/html/wordpress
# ExecStart は、サービスが起動したときに実行されるコマンド。
# /usr/local/bin/wp-cli-maintain.sh は、先ほど作成したスクリプトのフルパス。
ExecStart=/usr/local/bin/wp-cli-maintain.sh
# StandardOutput と StandardError は、標準出力と標準エラーのログをjournalctlにリダイレクトする。
# journalctl -u wp-cli-maintain.service でログを確認できる。
StandardOutput=journal
StandardError=journal
# Restart=on-failure は、サービスが失敗した場合に再起動を試みる設定。
# Type=oneshotの場合、スクリプトの終了コードによって失敗と判定される。
Restart=on-failure
RestartSec=5
# PrivateTmp=true は、サービスに専用の一時ディレクトリを提供し、セキュリティを向上させる。
PrivateTmp=true
[Install]
# WantedBy=multi-user.target は、システムがマルチユーザーモードになったときにサービスが有効になることを示す。
# systemd timer から起動されるため、直接有効化されることは少ないが、良いプラクティスとして記述。
WantedBy=multi-user.target
systemd Timer Unitファイル (/etc/systemd/system/wp-cli-maintain.timer)
[Unit]
Description=Run WordPress CLI Maintenance daily
# After=wp-cli-maintain.service は、このタイマーがサービスが起動した後に有効になることを示す。
# 必ずしも必要ではないが、関連付けることで依存関係を明確にする。
After=wp-cli-maintain.service
[Timer]
# OnCalendar は、タイマーのスケジュールを設定。
# "daily" は毎日実行。 "weekly" や "Mon *-*-* 03:00:00" (毎週月曜日の午前3時) なども設定可能。
# JST基準で毎日午前3時30分に実行。
OnCalendar=*-*-* 03:30:00
# Persistent=true は、タイマーが停止している間に予定された実行がスキップされた場合、
# タイマーが再度起動したときにすぐにその実行を試みるようにする (起動時のキャッチアップ)。
Persistent=true
[Install]
# WantedBy=timers.target は、システムがタイマー機能を有効にしたときにこのタイマーも有効になることを示す。
WantedBy=timers.target
検証
スクリプトの配置と実行権限付与:
上記のBashスクリプトを/usr/local/bin/wp-cli-maintain.shとして保存し、実行権限を付与します。
sudo install -m 755 wp-cli-maintain.sh /usr/local/bin/
手動でのスクリプト実行テスト:
sudoコマンドでスクリプトを手動実行し、期待通りに動作するか検証します。
sudo /usr/local/bin/wp-cli-maintain.sh
実行後、スクリプトが出力したログファイル(一時ディレクトリ内)を確認し、WordPressの管理画面でコア、プラグイン、テーマが更新されているかを確認します。
systemd Unit/Timerファイルの有効化と起動:
作成した.serviceと.timerファイルを/etc/systemd/system/に配置し、以下のコマンドでsystemdに認識させ、タイマーを有効化・起動します。
# systemdに新しいユニットファイルを認識させる
sudo systemctl daemon-reload
# タイマーを有効化 (システム起動時に自動起動するように設定)
sudo systemctl enable wp-cli-maintain.timer
# タイマーを起動
sudo systemctl start wp-cli-maintain.timer
systemd Timerの状態確認:
タイマーが正しく設定され、次回実行日時が表示されるか確認します。
sudo systemctl list-timers | grep wp-cli-maintain
# NEXT欄に次回実行日時 (JST) が表示されることを確認してください。
systemd Serviceのログ確認:
タイマーが実行された後、サービスが正常に動作したかログを確認します。
sudo journalctl -u wp-cli-maintain.service --since "today"
StandardOutput=journalとStandardError=journalにより、スクリプトの標準出力および標準エラー出力がjournalctlに記録されます。
運用
監視
journalctlで定期的にwp-cli-maintain.serviceのログを確認し、エラーが発生していないか監視します。
Webhook通知が設定されている場合は、通知システム(Slack, Mattermostなど)で正常に通知が届いているか確認します。
WordPressの管理画面で、サイトヘルスや利用可能な更新がないか、定期的に手動で確認することも重要です。
権限管理
アップデートと保守
wp-cli自体も定期的に更新されます。wp cli updateコマンドで最新版に保つようにしてください。
スクリプト内のWP_PATHやWEBHOOK_URLなどの設定は、環境変更に応じて適切に更新してください。
WordPressのメジャーバージョンアップ時など、自動更新が適切でない場合は、一時的にタイマーを無効化し、手動で更新と検証を行うことを検討してください。
トラブルシュート
スクリプトが実行されない
sudo systemctl list-timers --all でタイマーが有効(ACTIVATESにwp-cli-maintain.serviceが表示される)で、次回実行日時(NEXT)が正しく設定されているか確認します。
sudo systemctl status wp-cli-maintain.timer でタイマーユニットの状態を確認します。
sudo systemctl status wp-cli-maintain.service でサービスユニットの状態を確認します。
sudo systemctl daemon-reload が実行されているか確認します。
スクリプトが失敗する
sudo journalctl -u wp-cli-maintain.service --since "1 hour ago" で詳細なログを確認します。特にエラーメッセージに注目します。
WP_PATHが正しいか、WP_USERがWordPressファイルへの適切な権限を持っているか確認します。
ls -l /var/www/html/wordpress などで所有者と権限を確認し、必要であればsudo chown -R www-data:www-data /var/www/html/wordpressなどで修正します。
wp-cliコマンド自体が手動でsudo -u www-data wp --path=/var/www/html/wordpress ...として実行した際に成功するか確認します。
jqやcurlコマンドがパス通っているか、構文が正しいか確認します。
ネットワークの問題でcurlによる通知が失敗していないか、ファイアウォール設定を確認します。
一時ファイルが残る
まとめ
、wp-cliを活用したWordPress管理自動化の堅牢な実装方法をDevOpsエンジニアの視点から解説しました。
冪等性を意識したBashスクリプトはset -euo pipefailやtrapを用いることで予期せぬエラーにも対応し、sudo -uによる権限分離でセキュリティを確保します。
systemd timerは、cronに代わる高機能なスケジューリングツールとして、安定した自動運用を可能にします。
また、jqによるJSON処理とcurlによるHTTP通信の堅牢な利用例は、通知システムとの連携や外部APIとの統合に役立ちます。
これらの技術を組み合わせることで、WordPressのメンテナンス作業を効率化し、システムの安定性とセキュリティを向上させることができます。しかし、自動化は万能ではありません。定期的な監視と手動での確認、そしてスクリプトやWordPress自体のアップデートを怠らないことが重要です。
コメント