<p>本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">CLI: wp-cliによるWordPress DB操作</h1>
<p>、WordPressのコマンドラインインターフェースツールである <code>wp-cli</code> を活用し、データベースの安全なバックアップおよびリストア操作を行う方法について解説します。特に、DevOpsの観点から、冪等なスクリプトの作成、権限分離の重要性、<code>systemd</code> を用いた定期実行、そしてトラブルシューティングのポイントに焦点を当てます。</p>
<h2 class="wp-block-heading">1. 要件と前提</h2>
<h3 class="wp-block-heading">1.1. 前提</h3>
<ul class="wp-block-list">
<li><p>WordPressが稼働しており、そのファイル群(例: <code>/var/www/html/wordpress</code>)が存在すること。</p></li>
<li><p><code>wp-cli</code> がシステムにインストール済みであること。</p></li>
<li><p><code>sudo</code> 権限を持つユーザーで作業を行うこと。ただし、<code>wp-cli</code> の実行自体はWordPressのファイル所有者ユーザーで行うことを推奨します。</p></li>
<li><p><code>jq</code> および <code>curl</code> コマンドが利用可能であること。</p></li>
</ul>
<h3 class="wp-block-heading">1.2. 設計思想</h3>
<ul class="wp-block-list">
<li><p><strong>安全性</strong>: <code>set -euo pipefail</code> や <code>trap</code> を用いた堅牢なBashスクリプト。一時ディレクトリの安全な管理。</p></li>
<li><p><strong>冪等性 (Idempotence)</strong>: 同じ操作を複数回実行しても、システムの状態が同じ結果になるように設計すること。データベースのバックアップは常に新しいファイルを作成するため、厳密には冪等ではありませんが、リストア操作は指定された状態に収束します。</p></li>
<li><p><strong>権限分離</strong>: <code>wp-cli</code> の実行は、WordPressのファイル所有者(通常 <code>www-data</code> や <code>nginx</code> など)で行い、root権限の使用は最小限に抑えます。</p></li>
<li><p><strong>自動化</strong>: <code>systemd unit/timer</code> を利用して定期的なデータベース操作を自動化します。</p></li>
</ul>
<h2 class="wp-block-heading">2. 実装</h2>
<h3 class="wp-block-heading">2.1. 安全なBashスクリプトの基本</h3>
<p>以下は、安全なBashスクリプトを作成するための基本的なテンプレートです。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/bin/bash
# スクリプト内でエラーが発生した場合、即座に終了
set -euo pipefail
# スクリプトが異常終了した場合に実行される処理
# エラーライン番号を出力し、終了コード1で終了
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
# スクリプトが終了する際に一時ディレクトリを削除
tmp_dir=$(mktemp -d -t wp-cli-db-XXXXXXXX)
trap 'echo "Cleaning up temporary directory: ${tmp_dir}" >&2; rm -rf "${tmp_dir}"' EXIT
echo "Temporary directory created: ${tmp_dir}"
# WordPressのインストールパス
WP_PATH="/var/www/html/wordpress"
# WordPressのファイル所有者ユーザー (例: www-data, nginx)
WP_USER="www-data" # 環境に合わせて変更
# WP-CLIが利用可能かチェック
if ! sudo -u "${WP_USER}" wp --path="${WP_PATH}" core is-installed &>/dev/null; then
echo "ERROR: WordPress is not installed or WP-CLI cannot access it at ${WP_PATH}." >&2
exit 1
fi
# ここにwp-cliコマンドやその他の処理を記述
echo "WordPress CLI is accessible."
# ... (以降の処理)
</pre>
</div>
<ul class="wp-block-list">
<li><p><code>set -euo pipefail</code>:</p>
<ul>
<li><p><code>e</code>: コマンドが0以外のステータスで終了した場合、スクリプトを即座に終了します。</p></li>
<li><p><code>u</code>: 未定義の変数を使用した場合、エラーとします。</p></li>
<li><p><code>o pipefail</code>: パイプライン内で1つでもコマンドが失敗した場合、パイプライン全体の終了ステータスを失敗とします。</p></li>
</ul></li>
<li><p><code>trap '...' ERR</code>: <code>ERR</code> シグナル(エラー発生時)を捕捉し、エラーメッセージを出力してスクリプトを終了させます。</p></li>
<li><p><code>mktemp -d</code>: 安全な一時ディレクトリを作成します。</p></li>
<li><p><code>trap '...' EXIT</code>: スクリプトの終了時に一時ディレクトリをクリーンアップします。</p></li>
</ul>
<h3 class="wp-block-heading">2.2. WordPress DBのバックアップ</h3>
<p>以下のスクリプトは、WordPressデータベースをSQLファイルとしてバックアップします。ファイル名にタイムスタンプを含めることで、過去の時点のバックアップを識別できるようにします。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/bin/bash
set -euo pipefail
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
tmp_dir=$(mktemp -d -t wp-cli-db-XXXXXXXX)
trap 'echo "Cleaning up temporary directory: ${tmp_dir}" >&2; rm -rf "${tmp_dir}"' EXIT
WP_PATH="/var/www/html/wordpress"
WP_USER="www-data" # WordPressのファイル所有者ユーザー
BACKUP_DIR="/var/backups/wordpress_db" # バックアップ保存先ディレクトリ
# バックアップディレクトリが存在しない場合は作成
mkdir -p "${BACKUP_DIR}"
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
BACKUP_FILE="${BACKUP_DIR}/wordpress_db_backup_${TIMESTAMP}.sql"
LATEST_BACKUP_SYMLINK="${BACKUP_DIR}/latest_wordpress_db_backup.sql"
echo "Starting WordPress database backup to ${BACKUP_FILE} (JST: $(date '+%Y-%m-%d %H:%M:%S'))..."
# wp-cliをWordPressファイル所有者ユーザーで実行
# --pathオプションでWordPressのルートディレクトリを指定
sudo -u "${WP_USER}" wp db export "${BACKUP_FILE}" --path="${WP_PATH}"
# 最新のバックアップへのシンボリックリンクを更新 (冪等性向上)
rm -f "${LATEST_BACKUP_SYMLINK}"
ln -s "${BACKUP_FILE}" "${LATEST_BACKUP_SYMLINK}"
echo "Database backup completed successfully."
echo "Backup file: ${BACKUP_FILE}"
echo "Latest backup symlink: ${LATEST_BACKUP_SYMLINK}"
# 古いバックアップファイルを削除 (例: 30日以上前のファイルを削除)
echo "Removing old backup files (older than 30 days)..."
find "${BACKUP_DIR}" -name "wordpress_db_backup_*.sql" -type f -mtime +30 -delete
echo "Old backup files removed."
# バックアップファイルのパーミッションを設定 (必要に応じて)
chmod 600 "${BACKUP_DIR}"/*.sql
exit 0
</pre>
</div>
<ul class="wp-block-list">
<li><p><code>sudo -u "${WP_USER}" wp ...</code>: <code>wp-cli</code> をWordPressのファイル所有者ユーザーとして実行し、権限分離を確保します。<code>--allow-root</code> オプションは通常不要であり、本番環境での利用は避けるべきです。</p></li>
<li><p><code>rm -f ...; ln -s ...</code>: <code>latest_wordpress_db_backup.sql</code> というシンボリックリンクを作成し、常に最新のバックアップを指すようにします。これにより、リストア時に最新ファイルを特定しやすくなります。この操作は冪等です。</p></li>
<li><p><code>find ... -mtime +30 -delete</code>: 30日以上前のバックアップファイルを自動的に削除し、ディスク容量を管理します。</p></li>
</ul>
<h3 class="wp-block-heading">2.3. WordPress DBのリストア</h3>
<p>以下のスクリプトは、指定されたSQLファイルからWordPressデータベースをリストアします。<strong>この操作は現在のデータベースを完全に上書きするため、非常に注意が必要です。</strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/bin/bash
set -euo pipefail
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
tmp_dir=$(mktemp -d -t wp-cli-db-XXXXXXXX)
trap 'echo "Cleaning up temporary directory: ${tmp_dir}" >&2; rm -rf "${tmp_dir}"' EXIT
WP_PATH="/var/www/html/wordpress"
WP_USER="www-data" # WordPressのファイル所有者ユーザー
BACKUP_DIR="/var/backups/wordpress_db"
# リストアするバックアップファイル。例として最新のシンボリックリンクを使用
RESTORE_FILE="${BACKUP_DIR}/latest_wordpress_db_backup.sql"
# 引数でリストアファイルを指定できるようにする (オプション)
if [[ -n "${1:-}" ]]; then
RESTORE_FILE="${1}"
fi
if [[ ! -f "${RESTORE_FILE}" ]]; then
echo "ERROR: Restore file not found: ${RESTORE_FILE}" >&2
exit 1
fi
echo "WARNING: This operation will overwrite the current WordPress database."
echo "Attempting to restore database from: ${RESTORE_FILE}"
echo "Please confirm to proceed. Type 'YES' to continue."
read -r CONFIRMATION
if [[ "${CONFIRMATION}" != "YES" ]]; then
echo "Restoration cancelled by user."
exit 0
fi
echo "Starting WordPress database restoration (JST: $(date '+%Y-%m-%d %H:%M:%S'))..."
# wp-cliをWordPressファイル所有者ユーザーで実行
sudo -u "${WP_USER}" wp db import "${RESTORE_FILE}" --path="${WP_PATH}"
echo "Database restoration completed successfully."
exit 0
</pre>
</div>
<ul class="wp-block-list">
<li><p><strong>注意</strong>: <code>wp db import</code> は、実行すると既存のデータベースを上書きします。運用環境では、このスクリプトにユーザー確認プロンプトを含めることは推奨されません。代わりに、サービス停止期間を設け、事前に慎重な準備を行うべきです。</p></li>
<li><p>リストア前に、必ずサービスを停止し、現在のDBのバックアップを取ることを強く推奨します。</p></li>
</ul>
<h3 class="wp-block-heading">2.4. jqとcurlの利用例</h3>
<p><code>wp-cli</code> とは直接関係しませんが、DevOpsスクリプトで頻繁に利用される <code>jq</code> と <code>curl</code> の安全な利用例を示します。ここでは、WordPress REST APIから投稿情報を取得し、JSONデータを整形する例です。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/bin/bash
set -euo pipefail
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
API_ENDPOINT="https://example.com/wp-json/wp/v2/posts?per_page=5"
MAX_RETRIES=5
RETRY_DELAY=2 # 初期遅延秒数
echo "Fetching recent posts from WordPress REST API (JST: $(date '+%Y-%m-%d %H:%M:%S'))..."
for i in $(seq 1 "${MAX_RETRIES}"); do
# curlコマンド:
# -sS: 進捗メーター非表示、エラーメッセージは表示
# --fail-with-body: HTTPエラーコードの場合もボディを出力して失敗
# --connect-timeout 10: 接続タイムアウト10秒
# --max-time 30: 処理全体の最大タイムアウト30秒
# --retry 3: 失敗時に3回リトライ
# --retry-delay 5: リトライ間の遅延5秒
# --tls-max 1.3 --tlsv1.2: TLSバージョンを指定し、安全性を確保
RESPONSE=$(curl -sS --fail-with-body \
--connect-timeout 10 --max-time 30 \
--retry 3 --retry-delay 5 \
--tls-max 1.3 --tlsv1.2 \
"${API_ENDPOINT}")
if [[ $? -eq 0 ]]; then
# jqコマンド: JSONからタイトルとリンクを抽出し、整形
echo "Successfully fetched data. Processing with jq."
echo "${RESPONSE}" | jq -r '.[] | "Title: \(.title.rendered)\nURL: \(.link)\n---"'
break
else
echo "Curl failed (attempt ${i}/${MAX_RETRIES}). Retrying in ${RETRY_DELAY} seconds..." >&2
sleep "${RETRY_DELAY}"
RETRY_DELAY=$((RETRY_DELAY * 2)) # 指数バックオフ
fi
if [[ "${i}" -eq "${MAX_RETRIES}" ]]; then
echo "ERROR: Failed to fetch data after ${MAX_RETRIES} attempts." >&2
exit 1
fi
done
exit 0
</pre>
</div>
<ul class="wp-block-list">
<li><p><code>curl</code> のオプションで、TLSのバージョン指定、タイムアウト設定、リトライ回数、リトライ間の遅延 (<code>--retry-delay</code>) を設定し、ネットワークの不安定さに対応します。</p></li>
<li><p><code>jq -r '.[] | "Title: \\(.title.rendered)\\nURL: \\(.link)\\n---"'</code>: <code>jq</code> を使ってJSONレスポンスから各投稿のタイトルとURLを抽出し、人間が読みやすい形式で出力します。</p></li>
</ul>
<h3 class="wp-block-heading">2.5. DB操作フロー図</h3>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["開始"] --> B{"操作の種類を選択"};
B --|バックアップ| --> C["安全なBashスクリプト初期化"];
C --> D["wp db export を実行 (WP_USER権限)"];
D --> E["タイムスタンプ付きSQLファイル保存"];
E --> F["最新バックアップシンボリックリンク更新"];
F --> G["古いバックアップを削除"];
G --> H["終了"];
B --|リストア| --> I["安全なBashスクリプト初期化"];
I --> J{"リストアファイルを確認"};
J --|ファイルが存在しない| --> K["エラー終了"];
J --|ファイルが存在する| --> L["ユーザー確認 (運用では非推奨)"];
L --> M["wp db import を実行 (WP_USER権限)"];
M --> N["データベース更新"];
N --> O["WordPress動作確認 (手動/自動)"];
O --> H;
</pre></div>
<h2 class="wp-block-heading">3. 検証</h2>
<h3 class="wp-block-heading">3.1. バックアップの検証</h3>
<p>バックアップスクリプト実行後、以下の方法で検証します。</p>
<ol class="wp-block-list">
<li><p><strong>ファイルの存在確認</strong>:</p>
<div class="codehilite">
<pre data-enlighter-language="generic">ls -l /var/backups/wordpress_db/
# 例: wordpress_db_backup_20240730103000.sql と latest_wordpress_db_backup.sql (シンボリックリンク) が存在することを確認
</pre>
</div></li>
<li><p><strong>ファイルの整合性確認</strong>: SQLファイルの先頭数行を確認し、WordPressのデータベースエクスポートであることが分かればOKです。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">head -n 10 /var/backups/wordpress_db/latest_wordpress_db_backup.sql
</pre>
</div></li>
</ol>
<h3 class="wp-block-heading">3.2. リストアの検証</h3>
<p><strong>警告</strong>: リストアは稼働中のサービスに影響を与える可能性があります。テスト環境で十分な検証を行ってください。</p>
<ol class="wp-block-list">
<li><p><strong>サイトの簡易確認</strong>: リストアスクリプト実行後、WordPressサイトが正しく動作するか確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo -u www-data wp --path="/var/www/html/wordpress" option get siteurl
</pre>
</div>
<p>期待するサイトURLが出力されれば、基本的なDB接続とデータ取得は成功しています。</p></li>
<li><p><strong>管理画面ログイン</strong>: 実際にブラウザでWordPressの管理画面にログインし、投稿や設定がリストアされているか確認します。</p></li>
</ol>
<h2 class="wp-block-heading">4. 運用</h2>
<h3 class="wp-block-heading">4.1. systemd unit/timerによる定期実行</h3>
<p>データベースのバックアップは定期的に行うべきです。ここでは <code>systemd</code> を用いた定期実行の方法を示します。</p>
<p><strong>1. バックアップスクリプトの配置</strong>
バックアップスクリプト (<code>wp-db-backup.sh</code>) を <code>/usr/local/bin/</code> などに配置し、実行権限を付与します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo install -m 755 wp-db-backup.sh /usr/local/bin/wp-db-backup.sh
</pre>
</div>
<p><strong>2. systemdサービスユニットファイル (<code>/etc/systemd/system/wp-db-backup.service</code>)</strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=WordPress Database Backup Service
After=network.target
[Service]
Type=oneshot
# WordPressのファイル所有者ユーザーとしてスクリプトを実行
User=www-data
Group=www-data
# スクリプト実行パス
ExecStart=/usr/local/bin/wp-db-backup.sh
# 失敗時に自動再起動しない(timerで制御するため)
Restart=no
# ログ出力先 (標準出力と標準エラー出力はjournaldに記録される)
StandardOutput=journal
StandardError=journal
# 環境変数などが必要な場合はここで定義
# Environment="DB_NAME=wordpress"
</pre>
</div>
<p><strong>3. systemdタイマーユニットファイル (<code>/etc/systemd/system/wp-db-backup.timer</code>)</strong>
この例では、毎日午前3時30分(JST)にバックアップを実行します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=Run WordPress Database Backup daily at 3:30 AM
[Timer]
# OnCalendar の時刻はUTCで指定する必要がある場合がありますが、systemdのバージョンによってはローカルタイムゾーンもサポートします。
# 明示的にJSTで設定したい場合は、環境変数TZを設定するか、UTCで計算して指定します。
# ここではローカルタイムゾーン(JST)と仮定して指定。
# 毎日午前3時30分に実行
OnCalendar=*-*-* 03:30:00
# システム起動時にタイマーが設定時刻を逃した場合、すぐに実行
Persistent=true
[Install]
WantedBy=timers.target
</pre>
</div>
<p><strong>4. systemdタイマーの有効化と起動</strong></p>
<div class="codehilite">
<pre data-enlighter-language="generic"># systemd設定をリロード
sudo systemctl daemon-reload
# タイマーを有効化し、すぐに起動
sudo systemctl enable --now wp-db-backup.timer
</pre>
</div>
<p><strong>5. ログの確認</strong>
実行状況やエラーは <code>journalctl</code> で確認できます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># サービスのステータス確認
sudo systemctl status wp-db-backup.service
# タイマーのステータス確認
sudo systemctl status wp-db-backup.timer
# サービス実行ログの確認
sudo journalctl -u wp-db-backup.service --since "yesterday"
</pre>
</div>
<h3 class="wp-block-heading">4.2. バックアップファイルの管理</h3>
<ul class="wp-block-list">
<li><p><strong>ローテーション</strong>: <code>find ... -mtime +N -delete</code> をバックアップスクリプトに組み込むことで、自動的に古いファイルを削除できます(上記スクリプトに実装済み)。</p></li>
<li><p><strong>外部ストレージへの転送</strong>: 重要なバックアップファイルは、S3、Azure Blob Storage、Google Cloud Storageなどの外部ストレージに定期的に転送することを検討してください。<code>aws cli</code> や <code>gsutil</code> などのツールと <code>systemd</code> タイマーを組み合わせることで自動化できます。</p></li>
</ul>
<h2 class="wp-block-heading">5. トラブルシュート</h2>
<h3 class="wp-block-heading">5.1. wp-cliコマンドの失敗</h3>
<ul class="wp-block-list">
<li><p><strong>エラーメッセージの確認</strong>: <code>wp-cli</code> は詳細なエラーメッセージを出力します。まずはその内容をよく読み、原因を特定します。</p></li>
<li><p><strong>パスと権限</strong>: <code>--path</code> オプションが正しいWordPressのインストールディレクトリを指しているか、またスクリプトが適切なユーザー (<code>WP_USER</code>) で実行されているか確認します。</p></li>
<li><p><strong>DB接続情報</strong>: WordPressの <code>wp-config.php</code> に記述されたデータベース接続情報(ホスト、ユーザー名、パスワード)が正しいか確認します。</p></li>
<li><p><strong>メモリ制限</strong>: 大規模なデータベースの場合、PHPのメモリ制限 (<code>memory_limit</code>) やMySQLの接続制限などが原因で失敗することがあります。<code>php.ini</code> の設定を確認してください。</p></li>
</ul>
<h3 class="wp-block-heading">5.2. systemdタイマーの不動作</h3>
<ul class="wp-block-list">
<li><p><strong>タイマーの状態確認</strong>: <code>sudo systemctl status wp-db-backup.timer</code> でタイマーがアクティブ (<code>active</code>) になっているか確認します。</p></li>
<li><p><strong>サービスの状態確認</strong>: タイマーによって起動されるサービス (<code>wp-db-backup.service</code>) の状態を <code>sudo systemctl status wp-db-backup.service</code> で確認します。</p></li>
<li><p><strong>ログの確認</strong>: <code>sudo journalctl -u wp-db-backup.service</code> でサービスの実行ログを詳細に確認します。スクリプト内のエラー出力もここに記録されます。</p></li>
<li><p><strong><code>OnCalendar</code> 設定</strong>: <code>OnCalendar</code> の設定が意図した時刻に実行されるようになっているか確認します。タイムゾーン設定も影響する場合があります。</p></li>
</ul>
<h2 class="wp-block-heading">6. root権限の扱いと権限分離の注意点</h2>
<p><code>wp-cli</code> は、WordPressのファイルシステムおよびデータベースに直接アクセスするため、その実行には適切な権限が必要です。</p>
<ul class="wp-block-list">
<li><p><strong><code>wp-cli</code> の実行ユーザー</strong>: <code>wp-cli</code> は、<strong>WordPressのファイル群を所有しているユーザー</strong>(例: <code>www-data</code> や <code>nginx</code>)として実行することが最も安全かつ一般的です。これにより、ファイルやディレクトリのパーミッション問題を防ぎ、セキュリティリスクを最小限に抑えられます。</p></li>
<li><p><strong><code>sudo -u <wordpress_user></code> の利用</strong>: 本記事のスクリプトで示されているように、<code>sudo -u <wordpress_user> wp ...</code> の形式で実行することで、<code>root</code> ユーザーでスクリプトを実行しつつ、<code>wp-cli</code> コマンド自体は指定された特権の低いユーザーで実行できます。これにより、スクリプト全体の管理は <code>root</code> ユーザーが行いつつ、WordPressの操作は限定された権限で行うという権限分離が実現できます。</p></li>
<li><p><strong><code>--allow-root</code> オプションの制限</strong>: <code>wp-cli</code> には <code>--allow-root</code> オプションがありますが、これはrootユーザーで直接 <code>wp</code> コマンドを実行することを許可するものです。このオプションは<strong>開発・テスト環境でのみ推奨</strong>されており、本番環境での利用はセキュリティリスクを高めるため、極力避けるべきです。特に、不明なプラグインやテーマが導入されている環境では、悪意のあるコードがroot権限で実行される可能性があります。</p></li>
<li><p><strong>データベースクレデンシャルの保護</strong>: <code>wp-cli</code> は <code>wp-config.php</code> からデータベース接続情報を読み取ります。このファイルは厳重に保護されるべきです。データベースのユーザーには、必要最小限の権限のみを付与し、パスワードは強固なものを使用してください。</p></li>
</ul>
<h2 class="wp-block-heading">7. まとめ</h2>
<p><code>wp-cli</code> はWordPressの管理をCLIから効率的かつ安全に行うための強力なツールです。本記事では、2024年7月30日現在の情報に基づき、以下の点に焦点を当てて解説しました。</p>
<ul class="wp-block-list">
<li><p><code>set -euo pipefail</code> や <code>trap</code> を用いた堅牢なBashスクリプトの作成。</p></li>
<li><p><code>wp db export</code> および <code>wp db import</code> によるデータベースのバックアップとリストア。</p></li>
<li><p><code>jq</code> と <code>curl</code> を活用したDevOpsスクリプトでのデータ処理と安全なAPI連携。</p></li>
<li><p><code>systemd unit</code> と <code>systemd timer</code> を用いた定期的なバックアップ処理の自動化。</p></li>
<li><p>WordPressのファイル所有者ユーザーとしての <code>wp-cli</code> 実行、および <code>root</code> 権限の適切な扱いや権限分離の重要性。</p></li>
</ul>
<p>これらのプラクティスを導入することで、WordPressサイトの運用保守をより自動化し、安定性とセキュリティを向上させることができます。</p>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
CLI: wp-cliによるWordPress DB操作
、WordPressのコマンドラインインターフェースツールである wp-cli を活用し、データベースの安全なバックアップおよびリストア操作を行う方法について解説します。特に、DevOpsの観点から、冪等なスクリプトの作成、権限分離の重要性、systemd を用いた定期実行、そしてトラブルシューティングのポイントに焦点を当てます。
1. 要件と前提
1.1. 前提
WordPressが稼働しており、そのファイル群(例: /var/www/html/wordpress)が存在すること。
wp-cli がシステムにインストール済みであること。
sudo 権限を持つユーザーで作業を行うこと。ただし、wp-cli の実行自体はWordPressのファイル所有者ユーザーで行うことを推奨します。
jq および curl コマンドが利用可能であること。
1.2. 設計思想
安全性: set -euo pipefail や trap を用いた堅牢なBashスクリプト。一時ディレクトリの安全な管理。
冪等性 (Idempotence): 同じ操作を複数回実行しても、システムの状態が同じ結果になるように設計すること。データベースのバックアップは常に新しいファイルを作成するため、厳密には冪等ではありませんが、リストア操作は指定された状態に収束します。
権限分離: wp-cli の実行は、WordPressのファイル所有者(通常 www-data や nginx など)で行い、root権限の使用は最小限に抑えます。
自動化: systemd unit/timer を利用して定期的なデータベース操作を自動化します。
2. 実装
2.1. 安全なBashスクリプトの基本
以下は、安全なBashスクリプトを作成するための基本的なテンプレートです。
#!/bin/bash
# スクリプト内でエラーが発生した場合、即座に終了
set -euo pipefail
# スクリプトが異常終了した場合に実行される処理
# エラーライン番号を出力し、終了コード1で終了
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
# スクリプトが終了する際に一時ディレクトリを削除
tmp_dir=$(mktemp -d -t wp-cli-db-XXXXXXXX)
trap 'echo "Cleaning up temporary directory: ${tmp_dir}" >&2; rm -rf "${tmp_dir}"' EXIT
echo "Temporary directory created: ${tmp_dir}"
# WordPressのインストールパス
WP_PATH="/var/www/html/wordpress"
# WordPressのファイル所有者ユーザー (例: www-data, nginx)
WP_USER="www-data" # 環境に合わせて変更
# WP-CLIが利用可能かチェック
if ! sudo -u "${WP_USER}" wp --path="${WP_PATH}" core is-installed &>/dev/null; then
echo "ERROR: WordPress is not installed or WP-CLI cannot access it at ${WP_PATH}." >&2
exit 1
fi
# ここにwp-cliコマンドやその他の処理を記述
echo "WordPress CLI is accessible."
# ... (以降の処理)
set -euo pipefail:
e: コマンドが0以外のステータスで終了した場合、スクリプトを即座に終了します。
u: 未定義の変数を使用した場合、エラーとします。
o pipefail: パイプライン内で1つでもコマンドが失敗した場合、パイプライン全体の終了ステータスを失敗とします。
trap '...' ERR: ERR シグナル(エラー発生時)を捕捉し、エラーメッセージを出力してスクリプトを終了させます。
mktemp -d: 安全な一時ディレクトリを作成します。
trap '...' EXIT: スクリプトの終了時に一時ディレクトリをクリーンアップします。
2.2. WordPress DBのバックアップ
以下のスクリプトは、WordPressデータベースをSQLファイルとしてバックアップします。ファイル名にタイムスタンプを含めることで、過去の時点のバックアップを識別できるようにします。
#!/bin/bash
set -euo pipefail
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
tmp_dir=$(mktemp -d -t wp-cli-db-XXXXXXXX)
trap 'echo "Cleaning up temporary directory: ${tmp_dir}" >&2; rm -rf "${tmp_dir}"' EXIT
WP_PATH="/var/www/html/wordpress"
WP_USER="www-data" # WordPressのファイル所有者ユーザー
BACKUP_DIR="/var/backups/wordpress_db" # バックアップ保存先ディレクトリ
# バックアップディレクトリが存在しない場合は作成
mkdir -p "${BACKUP_DIR}"
TIMESTAMP=$(date +"%Y%m%d%H%M%S")
BACKUP_FILE="${BACKUP_DIR}/wordpress_db_backup_${TIMESTAMP}.sql"
LATEST_BACKUP_SYMLINK="${BACKUP_DIR}/latest_wordpress_db_backup.sql"
echo "Starting WordPress database backup to ${BACKUP_FILE} (JST: $(date '+%Y-%m-%d %H:%M:%S'))..."
# wp-cliをWordPressファイル所有者ユーザーで実行
# --pathオプションでWordPressのルートディレクトリを指定
sudo -u "${WP_USER}" wp db export "${BACKUP_FILE}" --path="${WP_PATH}"
# 最新のバックアップへのシンボリックリンクを更新 (冪等性向上)
rm -f "${LATEST_BACKUP_SYMLINK}"
ln -s "${BACKUP_FILE}" "${LATEST_BACKUP_SYMLINK}"
echo "Database backup completed successfully."
echo "Backup file: ${BACKUP_FILE}"
echo "Latest backup symlink: ${LATEST_BACKUP_SYMLINK}"
# 古いバックアップファイルを削除 (例: 30日以上前のファイルを削除)
echo "Removing old backup files (older than 30 days)..."
find "${BACKUP_DIR}" -name "wordpress_db_backup_*.sql" -type f -mtime +30 -delete
echo "Old backup files removed."
# バックアップファイルのパーミッションを設定 (必要に応じて)
chmod 600 "${BACKUP_DIR}"/*.sql
exit 0
sudo -u "${WP_USER}" wp ...: wp-cli をWordPressのファイル所有者ユーザーとして実行し、権限分離を確保します。--allow-root オプションは通常不要であり、本番環境での利用は避けるべきです。
rm -f ...; ln -s ...: latest_wordpress_db_backup.sql というシンボリックリンクを作成し、常に最新のバックアップを指すようにします。これにより、リストア時に最新ファイルを特定しやすくなります。この操作は冪等です。
find ... -mtime +30 -delete: 30日以上前のバックアップファイルを自動的に削除し、ディスク容量を管理します。
2.3. WordPress DBのリストア
以下のスクリプトは、指定されたSQLファイルからWordPressデータベースをリストアします。この操作は現在のデータベースを完全に上書きするため、非常に注意が必要です。
#!/bin/bash
set -euo pipefail
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
tmp_dir=$(mktemp -d -t wp-cli-db-XXXXXXXX)
trap 'echo "Cleaning up temporary directory: ${tmp_dir}" >&2; rm -rf "${tmp_dir}"' EXIT
WP_PATH="/var/www/html/wordpress"
WP_USER="www-data" # WordPressのファイル所有者ユーザー
BACKUP_DIR="/var/backups/wordpress_db"
# リストアするバックアップファイル。例として最新のシンボリックリンクを使用
RESTORE_FILE="${BACKUP_DIR}/latest_wordpress_db_backup.sql"
# 引数でリストアファイルを指定できるようにする (オプション)
if [[ -n "${1:-}" ]]; then
RESTORE_FILE="${1}"
fi
if [[ ! -f "${RESTORE_FILE}" ]]; then
echo "ERROR: Restore file not found: ${RESTORE_FILE}" >&2
exit 1
fi
echo "WARNING: This operation will overwrite the current WordPress database."
echo "Attempting to restore database from: ${RESTORE_FILE}"
echo "Please confirm to proceed. Type 'YES' to continue."
read -r CONFIRMATION
if [[ "${CONFIRMATION}" != "YES" ]]; then
echo "Restoration cancelled by user."
exit 0
fi
echo "Starting WordPress database restoration (JST: $(date '+%Y-%m-%d %H:%M:%S'))..."
# wp-cliをWordPressファイル所有者ユーザーで実行
sudo -u "${WP_USER}" wp db import "${RESTORE_FILE}" --path="${WP_PATH}"
echo "Database restoration completed successfully."
exit 0
2.4. jqとcurlの利用例
wp-cli とは直接関係しませんが、DevOpsスクリプトで頻繁に利用される jq と curl の安全な利用例を示します。ここでは、WordPress REST APIから投稿情報を取得し、JSONデータを整形する例です。
#!/bin/bash
set -euo pipefail
trap 'echo "ERROR: Script failed at line ${LINENO}." >&2; exit 1' ERR
API_ENDPOINT="https://example.com/wp-json/wp/v2/posts?per_page=5"
MAX_RETRIES=5
RETRY_DELAY=2 # 初期遅延秒数
echo "Fetching recent posts from WordPress REST API (JST: $(date '+%Y-%m-%d %H:%M:%S'))..."
for i in $(seq 1 "${MAX_RETRIES}"); do
# curlコマンド:
# -sS: 進捗メーター非表示、エラーメッセージは表示
# --fail-with-body: HTTPエラーコードの場合もボディを出力して失敗
# --connect-timeout 10: 接続タイムアウト10秒
# --max-time 30: 処理全体の最大タイムアウト30秒
# --retry 3: 失敗時に3回リトライ
# --retry-delay 5: リトライ間の遅延5秒
# --tls-max 1.3 --tlsv1.2: TLSバージョンを指定し、安全性を確保
RESPONSE=$(curl -sS --fail-with-body \
--connect-timeout 10 --max-time 30 \
--retry 3 --retry-delay 5 \
--tls-max 1.3 --tlsv1.2 \
"${API_ENDPOINT}")
if [[ $? -eq 0 ]]; then
# jqコマンド: JSONからタイトルとリンクを抽出し、整形
echo "Successfully fetched data. Processing with jq."
echo "${RESPONSE}" | jq -r '.[] | "Title: \(.title.rendered)\nURL: \(.link)\n---"'
break
else
echo "Curl failed (attempt ${i}/${MAX_RETRIES}). Retrying in ${RETRY_DELAY} seconds..." >&2
sleep "${RETRY_DELAY}"
RETRY_DELAY=$((RETRY_DELAY * 2)) # 指数バックオフ
fi
if [[ "${i}" -eq "${MAX_RETRIES}" ]]; then
echo "ERROR: Failed to fetch data after ${MAX_RETRIES} attempts." >&2
exit 1
fi
done
exit 0
curl のオプションで、TLSのバージョン指定、タイムアウト設定、リトライ回数、リトライ間の遅延 (--retry-delay) を設定し、ネットワークの不安定さに対応します。
jq -r '.[] | "Title: \\(.title.rendered)\\nURL: \\(.link)\\n---"': jq を使ってJSONレスポンスから各投稿のタイトルとURLを抽出し、人間が読みやすい形式で出力します。
2.5. DB操作フロー図
graph TD
A["開始"] --> B{"操作の種類を選択"};
B --|バックアップ| --> C["安全なBashスクリプト初期化"];
C --> D["wp db export を実行 (WP_USER権限)"];
D --> E["タイムスタンプ付きSQLファイル保存"];
E --> F["最新バックアップシンボリックリンク更新"];
F --> G["古いバックアップを削除"];
G --> H["終了"];
B --|リストア| --> I["安全なBashスクリプト初期化"];
I --> J{"リストアファイルを確認"};
J --|ファイルが存在しない| --> K["エラー終了"];
J --|ファイルが存在する| --> L["ユーザー確認 (運用では非推奨)"];
L --> M["wp db import を実行 (WP_USER権限)"];
M --> N["データベース更新"];
N --> O["WordPress動作確認 (手動/自動)"];
O --> H;
3. 検証
3.1. バックアップの検証
バックアップスクリプト実行後、以下の方法で検証します。
ファイルの存在確認:
ls -l /var/backups/wordpress_db/
# 例: wordpress_db_backup_20240730103000.sql と latest_wordpress_db_backup.sql (シンボリックリンク) が存在することを確認
ファイルの整合性確認: SQLファイルの先頭数行を確認し、WordPressのデータベースエクスポートであることが分かればOKです。
head -n 10 /var/backups/wordpress_db/latest_wordpress_db_backup.sql
3.2. リストアの検証
警告: リストアは稼働中のサービスに影響を与える可能性があります。テスト環境で十分な検証を行ってください。
サイトの簡易確認: リストアスクリプト実行後、WordPressサイトが正しく動作するか確認します。
sudo -u www-data wp --path="/var/www/html/wordpress" option get siteurl
期待するサイトURLが出力されれば、基本的なDB接続とデータ取得は成功しています。
管理画面ログイン: 実際にブラウザでWordPressの管理画面にログインし、投稿や設定がリストアされているか確認します。
4. 運用
4.1. systemd unit/timerによる定期実行
データベースのバックアップは定期的に行うべきです。ここでは systemd を用いた定期実行の方法を示します。
1. バックアップスクリプトの配置
バックアップスクリプト (wp-db-backup.sh) を /usr/local/bin/ などに配置し、実行権限を付与します。
sudo install -m 755 wp-db-backup.sh /usr/local/bin/wp-db-backup.sh
2. systemdサービスユニットファイル (/etc/systemd/system/wp-db-backup.service)
[Unit]
Description=WordPress Database Backup Service
After=network.target
[Service]
Type=oneshot
# WordPressのファイル所有者ユーザーとしてスクリプトを実行
User=www-data
Group=www-data
# スクリプト実行パス
ExecStart=/usr/local/bin/wp-db-backup.sh
# 失敗時に自動再起動しない(timerで制御するため)
Restart=no
# ログ出力先 (標準出力と標準エラー出力はjournaldに記録される)
StandardOutput=journal
StandardError=journal
# 環境変数などが必要な場合はここで定義
# Environment="DB_NAME=wordpress"
3. systemdタイマーユニットファイル (/etc/systemd/system/wp-db-backup.timer)
この例では、毎日午前3時30分(JST)にバックアップを実行します。
[Unit]
Description=Run WordPress Database Backup daily at 3:30 AM
[Timer]
# OnCalendar の時刻はUTCで指定する必要がある場合がありますが、systemdのバージョンによってはローカルタイムゾーンもサポートします。
# 明示的にJSTで設定したい場合は、環境変数TZを設定するか、UTCで計算して指定します。
# ここではローカルタイムゾーン(JST)と仮定して指定。
# 毎日午前3時30分に実行
OnCalendar=*-*-* 03:30:00
# システム起動時にタイマーが設定時刻を逃した場合、すぐに実行
Persistent=true
[Install]
WantedBy=timers.target
4. systemdタイマーの有効化と起動
# systemd設定をリロード
sudo systemctl daemon-reload
# タイマーを有効化し、すぐに起動
sudo systemctl enable --now wp-db-backup.timer
5. ログの確認
実行状況やエラーは journalctl で確認できます。
# サービスのステータス確認
sudo systemctl status wp-db-backup.service
# タイマーのステータス確認
sudo systemctl status wp-db-backup.timer
# サービス実行ログの確認
sudo journalctl -u wp-db-backup.service --since "yesterday"
4.2. バックアップファイルの管理
ローテーション: find ... -mtime +N -delete をバックアップスクリプトに組み込むことで、自動的に古いファイルを削除できます(上記スクリプトに実装済み)。
外部ストレージへの転送: 重要なバックアップファイルは、S3、Azure Blob Storage、Google Cloud Storageなどの外部ストレージに定期的に転送することを検討してください。aws cli や gsutil などのツールと systemd タイマーを組み合わせることで自動化できます。
5. トラブルシュート
5.1. wp-cliコマンドの失敗
エラーメッセージの確認: wp-cli は詳細なエラーメッセージを出力します。まずはその内容をよく読み、原因を特定します。
パスと権限: --path オプションが正しいWordPressのインストールディレクトリを指しているか、またスクリプトが適切なユーザー (WP_USER) で実行されているか確認します。
DB接続情報: WordPressの wp-config.php に記述されたデータベース接続情報(ホスト、ユーザー名、パスワード)が正しいか確認します。
メモリ制限: 大規模なデータベースの場合、PHPのメモリ制限 (memory_limit) やMySQLの接続制限などが原因で失敗することがあります。php.ini の設定を確認してください。
5.2. systemdタイマーの不動作
タイマーの状態確認: sudo systemctl status wp-db-backup.timer でタイマーがアクティブ (active) になっているか確認します。
サービスの状態確認: タイマーによって起動されるサービス (wp-db-backup.service) の状態を sudo systemctl status wp-db-backup.service で確認します。
ログの確認: sudo journalctl -u wp-db-backup.service でサービスの実行ログを詳細に確認します。スクリプト内のエラー出力もここに記録されます。
OnCalendar 設定: OnCalendar の設定が意図した時刻に実行されるようになっているか確認します。タイムゾーン設定も影響する場合があります。
6. root権限の扱いと権限分離の注意点
wp-cli は、WordPressのファイルシステムおよびデータベースに直接アクセスするため、その実行には適切な権限が必要です。
wp-cli の実行ユーザー: wp-cli は、WordPressのファイル群を所有しているユーザー(例: www-data や nginx)として実行することが最も安全かつ一般的です。これにより、ファイルやディレクトリのパーミッション問題を防ぎ、セキュリティリスクを最小限に抑えられます。
sudo -u <wordpress_user> の利用: 本記事のスクリプトで示されているように、sudo -u <wordpress_user> wp ... の形式で実行することで、root ユーザーでスクリプトを実行しつつ、wp-cli コマンド自体は指定された特権の低いユーザーで実行できます。これにより、スクリプト全体の管理は root ユーザーが行いつつ、WordPressの操作は限定された権限で行うという権限分離が実現できます。
--allow-root オプションの制限: wp-cli には --allow-root オプションがありますが、これはrootユーザーで直接 wp コマンドを実行することを許可するものです。このオプションは開発・テスト環境でのみ推奨されており、本番環境での利用はセキュリティリスクを高めるため、極力避けるべきです。特に、不明なプラグインやテーマが導入されている環境では、悪意のあるコードがroot権限で実行される可能性があります。
データベースクレデンシャルの保護: wp-cli は wp-config.php からデータベース接続情報を読み取ります。このファイルは厳重に保護されるべきです。データベースのユーザーには、必要最小限の権限のみを付与し、パスワードは強固なものを使用してください。
7. まとめ
wp-cli はWordPressの管理をCLIから効率的かつ安全に行うための強力なツールです。本記事では、2024年7月30日現在の情報に基づき、以下の点に焦点を当てて解説しました。
set -euo pipefail や trap を用いた堅牢なBashスクリプトの作成。
wp db export および wp db import によるデータベースのバックアップとリストア。
jq と curl を活用したDevOpsスクリプトでのデータ処理と安全なAPI連携。
systemd unit と systemd timer を用いた定期的なバックアップ処理の自動化。
WordPressのファイル所有者ユーザーとしての wp-cli 実行、および root 権限の適切な扱いや権限分離の重要性。
これらのプラクティスを導入することで、WordPressサイトの運用保守をより自動化し、安定性とセキュリティを向上させることができます。
コメント