ここ最近このブログに続けて投稿した「さくらVPSを引っ越す」「さくらVPSを守る」の各シリーズ、実はどちらも下書きの作成から下書き投稿までを毎回Claude Codeにやらせています。今回はその裏側、つまり「Claude Codeがどうやって下書きをWordPressに投げ込んでいるか」という、ブログ運用そのものを支えるインフラの話です。ネタというより資料の色が強い回になりますが、同じようにWP-CLI経由の自動投稿を検討している人の参考になれば、というのが今回の狙いです。
まずは現状把握、WP-CLIは入っていなかった
このWordPress(blog.marochanet.org)はdocker composeで動いていて、db・php-fpm・nginxの3コンテナ構成です。php-fpm側は公式のwordpress:php8.4-fpmイメージで、ドキュメントルートはホスト側のディレクトリをbind mountしています。まず調べたのは、そもそもWP-CLIが使える状態になっているかどうかでした。結果は「未導入」。ホスト(jagha)にもwpバイナリはなく、稼働中のphp-fpmコンテナの中にもありません(公式PHP-FPMイメージにはWP-CLIは同梱されていない仕様です)。ただし面白いことに、公式のwordpress:cliイメージだけは以前pull済みで、docker imagesにちゃっかり残っていました。過去に検討だけして手を付けずに放置していたようです。
都度実行か、常設サービスか
WP-CLIを動かす入れ物のイメージは決まったので、次に考えたのは「そのコンテナをどう常用するか」です。選択肢は大きく2つありました。
- ①都度実行方式:
docker run --rmでコマンドの度に使い捨てのコンテナを起動する - ②常設サービス方式:docker-compose.ymlにwp-cliサービスを追記し、
docker compose execで呼び出す
比較した観点は以下の通りです。
| 観点 | ①都度実行 | ②常設サービス |
|---|---|---|
| 平常時の攻撃面 | 実行の瞬間だけ存在、終われば消える | DBに常時到達可能な橋頭堡が起動し続ける |
| SSH都度実行との相性 | ◎ 1ssh=1完結アクションで監査が明快 | △ execする入口が常に開いている |
| メモリ消費(2GB VPS) | 実行中のみ、完了後解放 | アイドルでも常時占有 |
| compose変更要否 | 不要。本番composeに手を入れずに済む | 追記が必要(破壊的変更に準じる) |
結論は①都度実行方式。理由は、SSH経由で1コマンド=1完結アクションという運用モデルと相性がいいこと、メモリ2GBのVPSで常駐コンテナを増やしたくないこと、そして慎重に育ててきた本番のdocker-compose.ymlに触れずに済むこと、の3つです。
実装で地味に嵌った3点
叩き台のコマンドはこんな形でした。
ssh jagha "sudo docker run --rm --network shared \
-v /path/to/blog:/var/www/html --user www-data \
-e WORDPRESS_DB_HOST=db -e WORDPRESS_DB_USER=webdata \
-e WORDPRESS_DB_PASSWORD=*** -e WORDPRESS_DB_NAME=wpdata \
wordpress:cli wp post create --post_title='...' --post_status=publish --path=/var/www/html"
ここから~/bin/wp-autopost.shというラッパースクリプトに仕立てる過程で、いくつか地味な壁がありました。
- 本文・タイトルをssh引数で渡すとクォート崩れやインジェクションのリスクがあるため、stdinでJSONを渡す方式に変更(複数行の本文にも安全に対応できる)
- ホスト側の
blog/ディレクトリはuid=33(www-data)所有だが、wordpress:cliイメージ内部のwww-dataはuid=82(Alpine系)でホストとズレている。--user www-dataではなく--user 33:33を明示指定することで権限不一致を回避した - wp-config.phpはDB接続情報を環境変数から読む実装のため、コンテナには
--env-fileでDB接続情報を渡す必要がある(wp-config.php自体に直書きはされていない)
最終的な呼び出し方はこうなりました。
cat <<'EOF' | ssh jagha 'bin/wp-autopost.sh'
{"title": "タイトル", "content": "本文\n複数行もOK", "status": "draft"}
EOF
標準出力に返るのは作成された投稿IDのみ。処理ログはすべてstderrに逃がしているので、呼び出し側は投稿IDだけを綺麗に受け取れます。あわせて投稿者(post_author)の扱いも決めました。WordPress側にclaudeという専用ユーザー(author権限)を作成し、スクリプトのデフォルト値として設定。JSON側でauthorキーを指定すれば上書きもできます。これでどの投稿がClaude作なのかが管理画面上でも一目瞭然になりました。
できあがった運用フロー
「Claude Codeが本文を作成 → SSH経由でwp-autopost.shに下書き投稿 → 管理画面でユーザーがレビュー・仕上げ・公開」という一連の流れがこれで機能するようになりました。ちなみにjaghaの~/.ssh/authorized_keysには元々14個の鍵(複数PC・複数クライアント分)が登録済みだったため、スクリプトと接続情報をjagha側だけに置く設計にしたことで、追加のSSH鍵作業なしでどのPCからでもこの仕組みを使えるようになったのは嬉しい誤算でした。
次回は、この「本文を作る」部分の周辺の話です。ネタをどうClaude Codeに渡すか、そして画像を投稿に混ぜようとして何度も転んだ顛末をお届けします。
> 過去に検討だけして手を付けずに放置していたようです。
ちゃっかりここだけDisってんじゃねーよヽ(`Д´)ノプンプン