この記事は
YAMAP エンジニア Advent Calendar 2020 の 18 日目になる予定です。
頑張るぞ。
tl;dr
WordPress で構築されたサイトをいくつか管理していますが、WordPress や WordPress のバージョン更新に追従できておらず、気付いたら、セキュリティパッチの適用等の作業が後回しになってしまう状況になっておりました。
このような状況を解消すべく、シェルスクリプトと CircleCI のスケジュールジョブを利用して、バージョンアップに追従していく仕組みを作ったので、紹介させて頂きたいと思います。
課題
複数の WordPress 環境を自前で運用するにあたり、以下のような課題を抱えておりました。
WordPress 環境を構築する度に、「どうしようかなあ」と思いつつ、手つかずのままで今日に至ります。
こうしてみた
幸い
WordPress 環境は Docker (AWS の ECS 上で) で運用される為、WordPress のバージョンやプラグイン、Web サーバーの設定等の WordPress 環境の構成は Dockerfile に記述されていました。
以下、Dockerfile のサンプルです。
Dockerfile
FROM wordpress:5.6.0 ... 略 ... # 依存するコマンドをインストール RUN apt-get update RUN apt-get -y install wget unzip # プラグインファイルの一時ダウンロード先へ移動 WORKDIR /tmp/wp-plugins # プラグインファイルをダウンロード RUN wget https://downloads.wordpress.org/plugin/all-in-one-seo-pack.3.7.1.zip RUN wget https://downloads.wordpress.org/plugin/jetpack.9.2.1.zip ...
更に ECS 環境へのデプロイは git push をトリガーとして CircleCI を利用して行われるように実装されていました。CircleCI 上で CI/CD が管理されていることにより、CircleCI に用意されている各種機能、特に今回は、定期実行 (ワークフローのスケジュール実行) を利用して定期的に WordPress のバージョンやプラグインのバージョンの更新をチェックすることが出来ました。
WordPress やプラグインの最新バージョン情報を取得する
WordPress 本体や、プラグインの最新バージョンの情報は WordPress.org が提供する REST API を利用して取得出来ます。jq 等を絡めて実行されることを想定しています。
# WordPress 本体の最新バージョンを取得する $ curl -s https://api.wordpress.org/core/version-check/1.7/ | jq -r '.offers|first|.version' # プラグインの最新バージョン情報を取得する $ curl -s https://api.wordpress.org/plugins/info/1.0/${PLUGIN_NAME}.json | jq -r .version
以下、実行例です。
$ curl -s https://api.wordpress.org/core/version-check/1.7/ | jq -r '.offers|first|.version' 5.6 $ curl -s https://api.wordpress.org/plugins/info/1.0/jetpack.json | jq -r .version 9.2.1
稼働しているバージョンと比較する
こちらのブログ記事を参考にさせて頂き、REST API を利用して、最新のバージョンを取得出来ることが判りました。後は、既に稼働している WordPress やプラグインのバージョンを取得して、最新のバージョンとの比較を行う必要があります。
先述の通り、既に稼働している WordPress 環境は Dockerfile 記述されている為、grep
や awk
等を駆使して Dockerfile から稼働中のバージョン情報を取得出るので、以下のようなシェルスクリプトを書いて、最新のバージョンと稼働中のバージョンを比較します。
ver-check
#!/bin/bash SLACK_USERNAME="${CIRCLE_PROJECT_REPONAME:-WordPress Version Check}" rm -f /tmp/version_checked && touch /tmp/version_checked WP_CURRENT_VERSION=$(cat ${DOCKER_FILE_PATH} | grep "FROM wordpress:" | awk -F: '{print $NF}' | grep -o -E "([0-9]+\.){1}[0-9]?") PLUGINS=$(cat ../docker/Dockerfile | grep download | awk -F/ '{print $NF}') WP_LATEST_VERSION=$(curl -s https://api.wordpress.org/core/version-check/1.7/ |jq -r '.offers|first|.version') diff -u -i <(echo $WP_CURRENT_VERSION) <(echo $WP_LATEST_VERSION) if [ "$?" != "0" ];then printf "WordPress $WP_CURRENT_VERSION $WP_LATEST_VERSION\n" >> /tmp/version_checked fi for plugin in ${PLUGINS} do name=$(echo $plugin | awk -F. '{print $1}') current_version=$(echo $plugin | grep -o -E "(\.[0-9]+|\.[0-9]+\.){1}[0-9]+(\.[0-9]+)?" | sed 's/.//') latest_version=$(curl -s https://api.wordpress.org/plugins/info/1.0/"${name}".json | jq -r .version) # printf "$name $current_version $latest_version\n" diff -u -i <(echo $current_version) <(echo $latest_version) if [ "$?" != "0" ];then printf "$name $current_version -> $latest_version\n" >> /tmp/version_checked fi done SIZE=$(wc -c < /tmp/version_checked) if [ $SIZE -gt 0 ];then echo "There are differences between some of the versions." cat /tmp/version_checked JSON_MESSAGE="{ \"icon_emoji\": \":wordpress:\", \"username\": \"${SLACK_USERNAME}\", \"text\": \"There are differences between some of the versions.\n\`\`\`$(cat /tmp/version_checked)\`\`\`\" }" curl -s -X POST -H "Content-Type: application/json" \ "${SLACK_WEBHOOK_URL}" \ -d "${JSON_MESSAGE}" > /dev/null else echo "All versions are up to date." fi
だいぶん、雑に書いてしまっているので、色々とツッコミどころはあります。ご容赦下さい :bow:
以下、スクリプトを実行したら、WordPress 本体とプラグインに差分が発生した (稼働中のバージョンよりも新しいバージョンが提供されていた) 状況です。
$ ./ver-check --- /dev/fd/63 2020-12-18 16:44:28.000000000 +0900 +++ /dev/fd/62 2020-12-18 16:44:28.000000000 +0900 @@ -1 +1 @@ -5.3 +5.6 --- /dev/fd/63 2020-12-18 16:44:33.000000000 +0900 +++ /dev/fd/62 2020-12-18 16:44:33.000000000 +0900 @@ -1 +1 @@ -9.2.0 +9.2.1 There are differences between some of the versions. WordPress 5.3 -> 5.6 jetpack 9.2.0 -> 9.2.1
そして、以下のように Slack に通知が飛んできます。
そして、自動化へ。。。
差分チェックは一日に一回実行します。実行する基盤として、冒頭に記載した通り、CircleCI のワークフローのスケジュール実行を利用します。
以下のように .circleci/config.yml を設定しました。
.circleci/config.yml
version: 2.1 ... 略 ... jobs: vercheck: executor: default steps: - checkout - install_dependencies - run: name: Check WordPress, Plugins Versions command: | cd bin && ./ver-check ... 略 ... workflows: version: 2 ver-check: triggers: - schedule: cron: "0 0 * * *" filters: branches: only: - master jobs: - vercheck
午前 9 時に実行されるように設定しています。
CircleCI の回し者ではありませんが、YAML をシュッと書いて、簡単に定期処理を設定出来るのは良いですね!
苦労したところ
唯一、苦労したのが、プラグインバージョン文字列のパースです。
当初、以下のようなバージョン文字列しか想定していませんでした。
plugin-name.{x}.{y}.{z}.zip
この文字列から、x.y.z
のみを取り出したい場合、取り出し方は色々とパターンがあると思いますが、当初は、正規表現を使って、以下のように書きました。
grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?"
以下、実行例です。
$ echo 'plugin-name.1.2.3.zip' | grep -o -E "([0-9]+\.){1}[0-9]+(\.[0-9]+)?" 1.2.3
とーころが、プラグインのバージョン番号が、先述のような規則に則っていないプラグインがありました。
plugin-name.YYYYMMDD.zip
おいおい、更に、以下のようにプラグイン名の末尾に数字が含まれているプラグインもありました。
plugin-name-7.{x}.{y}.{z}.zip
これらのパターンだと、先述の正規表現パターンにはマッチしません。。。orz ということで、最終的に以下のように対応しました。
$ echo 'plugin-name.12345678.zip' | grep -o -E "(\.[0-9]+|\.[0-9]+\.){1}[0-9]+(\.[0-9]+)?" .12345678 $ echo 'plugin-name-7.1.2.3.zip' | grep -o -E "(\.[0-9]+|\.[0-9]+\.){1}[0-9]+(\.[0-9]+)?" .1.2.3
ただ、これでは不十分で、先頭の .
を sed 's/.//'
で削除する処理を追加しています。
何を隠そう、このあたりの処理について、自分は特に不得意で試行錯誤の結果となります。もっと、こうやったらシンプルだよなどのありましたら助言を頂けると嬉しいです。
通知が届いた後のフロー
ということで、この仕込んだバージョンチェックの通知が届いたら、以下のように対応するようにしています。
- バージョンアップ対象のリリースノートを確認して、バージョンアップの内容を確認
- Dockerfile を修正し、開発環境にデプロイ
- 開発環境で動作確認
- 開発環境での動作確認で問題なければ、本番環境にリリース
このようなフローが取れるのは、以下のような仕込みが既に入っていたことが大きいと考えています。
- Docker 化されていること
- Github と CircleCI でデプロイ出来るようになっていること
先人の努力に本当に感謝です。
以上
最近、ギョームでやったこととして、WordPress 運用をちょっと工夫してみた内容について書いてみました。この施策により、手数 (てかず) 自体は減ってはいませんが、古いバージョンのアプリケーションが動き続けるという好ましくない状況を放置するということは無くなったと思います。