インフラエンジニアの金澤です。
今回は、AWS上のWordPressサーバーをスケールアウトするために行った手順について紹介いたします。
ランサーズで運用しているWordPressサービス
ランサーズでは10以上のWordPressサービスをAWSで運用しています。
ランサーズと比べてアクセス数が多くなく、負荷も低いのでこれらのサービスを1台のEC2で運用してきましたが、リソースが不足してきたため、複数台にスケールアウトできるように準備し始めました。
スケールアウトするために解決すべき問題
WordPressのEC2をスケールアウトするには、以下の問題を解決する必要があります。
MySQL
EC2のローカル上にMySQLがインストールされた状態では、複数台構成にできません。
MySQLを別サーバーに移動する必要があります。
uploadsディレクトリ
wp-content/uploadsディレクトリには、記事投稿時にアップロードされた写真等のメディアデータが格納されます。
EC2上にこのディレクトリがある状態では複数台構成にできません。
uploadsディレクトリを別サーバーに移動する必要があります。
pluginsディレクトリ
wp-content/pluginsディレクトリには、WordPressのプラグインが格納されています。
複数台構成にすると、管理画面からプラグインを追加しても、1台のサーバーにしか反映されません。
全サーバーにプラグインが配布されるような仕組みを作る必要があります。
ログ
複数台構成にすると、各サーバーにログが分散され、解析が困難になります。
アクセスログを解析したい場合は、別途ログを集約する仕組みを構築することになります。
スケールアウトするために行なった準備
以下、上記の問題を解決するために行った手順について紹介いたします。
スケールアウト前
元々は、1台のEC2にグローバルIPを付与して運用していました。
https通信については、必要なドメインのみSSL証明書を申請していました。
Git化
ランサーズでは、WordPressのプロジェクトは、全てGitHubで管理しています。
wp-content/plugins ディレクトリはGit化し、プラグインの追加もGitHubに反映しています。
プラグインの追加は本番サーバーの管理画面では行わず、開発環境で追加し、検証が完了したものをgit pullでリリースするフローにしています。
※wp-config.phpやwp-content/uploadsディレクトリはGit管理から外しています。
ELBを追加
スケールアウトの第一歩として、まずELB経由で通信する構成に変更します。
グローバルIPアドレスは必要なくなるため、EC2はプライベートVPCに移動させます。
このタイミングで、ELBの以下の機能を利用するように設定しました。
AWSのSSL証明書設定(https化)
AWSの無料SSL証明書が利用できるようになったため、サービスをhttps化しました。
SSL Termination
ELBのSSL Termination機能を利用することで、EC2内ではhttpのままで通信できます。
ELBアクセス時にhttp、httpsのどちらかでアクセスされたかを判定するために、X-Forwarded-Proto環境変数を利用するため、wp-config.php に以下の記述を追加します。
(wp-settings.phpの読み込み前に記述)
if (!empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {
$_SERVER['HTTPS']='on';
}
/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');
MySQLをRDSに移行
RDS for MySQLを作成し、EC2内のMySQLをRDSに移行します。
MyISAMのテーブルが含まれているとRDSの機能が制限されますので、特に理由がなければInnoDBに変換しておいたほうが良いでしょう。
uploadsディレクトリをS3に移行
wp-content/uploadsディレクトリのコンテンツはS3に格納します。
S3に格納するために必要なプラグインは以下の2つになります。
Amazon Web Servicesプラグイン
プラグインを追加後、wp-config.phpに以下の設定を追加します。
define( 'DBI_AWS_ACCESS_KEY_ID', 'XXXXXXXXXXXXXXXXXXXX' );
define( 'DBI_AWS_SECRET_ACCESS_KEY', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
WP Offload S3 Liteプラグイン
Webサイトホスティングを有効にしたS3バケットを作成し、プラグインを追加、設定します。
既存のuploadsディレクトリのコンテンツは、S3にアップしておく必要があります。
aws s3コマンドでS3にコピーします。
$ aws s3 sync ./wordpressディレクトリ名/wp-content/uploads s3://S3バケット名/wordpressディレクトリ名/wp-content/uploads
DB側のupload情報も修正が必要になります。
ここが一番面倒な作業になります。
※このブログの記事を参考にさせていただきました。
wp_postmetaテーブルにデータ追加
INSERT文をSQLで生成し、出力されたINSERT文を適用してデータを追加します。
SELECT
CONCAT(
'INSERT INTO wp_postmeta(post_id, meta_key, meta_value) VALUES (',
post_id,
', \'amazonS3_info\', \'a:3:{s:6:\"bucket\";s:13:\"S3バケット名\";s:3:\"key\";s:',
length(meta_value) + 29,
':\"wordpress/wp-content/uploads/',
meta_value,
'\";s:6:\"region\";s:14:\"ap-northeast-1\";}\');'
) AS record
FROM
wordpress.wp_postmeta
WHERE
meta_key = '_wp_attached_file'
;
※length(meta_value) + 29の29という数字はuploadsディレクトリまでのパスの文字数です。
例えば、wordpress/wp-content/uploads/ というパスであれば、パスの文字数が29になるので、29を加算します。
データ追加が上手く行けば、管理画面の「メディア」メニューにアップロードしたデータのURLが、S3WebサイトホスティングのURLになります。
wp_postテーブルのURL修正
wp_postテーブルや、その他、アップロード画像をURL参照しているテーブルのURLも、S3のURLに変更します。
この作業はバックアップも兼ねて、mysqldumpでテーブルデータをエクスポートしてから、エディタでURLを変更後、インポートし直すのが良いと思います。
リリースシステムとの連携
ここまで準備すれば、EC2をスケールアウト可能になります。
複数台構成のEC2にリリースを行うために、リリースシステムからリリースするフローに変更しました。
ランサーズには、専用のリリースシステムがあります。
このリリースシステムに、WordPress系プロジェクトのリリース機能を追加します。
リリースシステム上でgit pullして、ansible経由でWordPress全サーバーに同期します。
WordPressサーバー側はGit管理する必要がなくなるため、このタイミングで.gitディレクトリを削除します。
今後の課題
ログの集約
fluentd + S3プラグインでログをS3に集約します。
ログサーバーにも転送し、全WordPressサーバーのアクセスログは、ログサーバーで参照するようにします。
DBのスケールアウト
HyperDBプラグインを導入すると、参照系と更新系を分離できるようになります。
RDSのリードレプリカを作成し、参照系はHAProxy等で分散させるようにします。
最後に
実は、WordPressのEC2はまだ1台のままです。(笑
スケールアウトの準備をした過程で、リソースがELB、EC2、RDS、S3に分散された結果、もう少しだけ耐えられそうな見込みになったためです。
もう少しアクセスが増えたら、EC2をスケールアウトし始めることになると思います。
今回は、その準備ができましたというお話でした。