ワンダウォールのWeb構築レギュレーションはこれだ(というものを考え中)

新たにWebサイトを立ち上げる際、AWSであれば簡単にサーバーを稼働させることができるが、コストの無駄を見直したり、新たに役割を担わせようとするとAWSも日々いろいろと改良されていることに気がつく。ここではワンダウォールがWeb構築仕事を受けた時は、こういうセットアップを行なっていくというレギュレーションを考えつつ、情報をまとめてみる。

WebサーバーのSSL化(ELBをやめてCloudFrontへ)

WebサーバーのSSL化、これは個人的には必須だと思っている。GoogleはSSL化されたWebサイトの順位を優遇するSEO施策を行なっているし、AppleはATS(App Transport Security)という考えに基づき、iOSアプリケーションがSSL化されていないサイトやサーバーにアクセスするのに制限をかけつつある。GoogleはWebから、AppleはAppから世界中のSSL化を進めようとしているという構図だ。

しかしSSL対応のためには証明書の購入が必要になる。最近は安くなってきたとはいえ、1FQDNあたり年間1,000円ほどかかる。wonderwall.netは3つほどサブドメインを切っているので年間3,000円のコスト。まぁ安いと言えば安いけど、設定したり更新したりする手間を考えるとフリーハンドに越したことはない。

そこで、AWSで簡単にSSL/TLS証明書を発行できるAWS Certificate Managerを使うことにした。これなら証明書発行は無料だし、Amazonが発行しているということで証明書の信頼性も高い。ただ落とし穴があり、AWSのWebサーバーであるEC2には証明書をアタッチできず、ELB(ロードバランサー)とCloudFront(コンテンツ配信ネットワーク)だけとなる。

先月はELBに証明書をアタッチして運用してみた。そもそもそんなにアクセス負荷が高くないサイトなので、単純にhttps://でアクセス可能にするためだけの処置なわけだが、ELBは$0.027/h(月額$19.4)のコストがかかる。証明書発行だけなら年間3,000円(3FQDN)に対して、ELBは月に2,000円強。まったく割に合わなかった。

そこで、もうひとつの選択肢であるCloudFrontに切り替えてみた。CloudFrontは時間単位ではなく転送量とリクエスト数で料金が決まる。ELBとの比較は一概にできないが、たぶんうちのサーバーであれば月に$1.65くらい? 年間20ドル弱なのでELBの12分の1だし、FQDNが2つを超える場合は証明書もこちらの方が安い。

これで対ELBで毎月$17ドルほど、コスト削減。

CloudFrontの各種設定

CloudFrontには他にもメリットがある。世界中にあるAmazonのエッジサーバーにコンテンツがキャッシュされ、アクセスされるとユーザーの一番近くにあるエッジサーバーに誘導されるため、Webサーバーがt2.microと低性能なものであっても、Webの表示スピードは高速化する。また、wonderwall.net の実サーバーはエッジサーバーの奥に隠蔽されるので、DDoS攻撃を受けにくくなるなどのメリットもある。

では簡単にCloudFrontの設定を説明する。WordPressなどWebコンソールが起動している場合、それらへのアクセスはエッジサーバーを経由しないように設定しなければならないので、そこが話のポイント。

1) General
ここは普通に設定する。「デリバリーメソッド(Delivery Method)」はWebにし、ACMで生成した証明書を設定する。

CloudFront 2) Origin
エッジサーバーがアクセスするWebサーバーの設定では、Origin Domain Nameの欄にEC2のパブリックDNS (ec2-で始まるアドレス)を入力。エッジサーバーはHTTPSのアクセスを受け付けるが、AWS内部におけるエッジからWebサーバーへのアクセスは従来どおりHTTPとなるので、Origin Protocol Policyの欄はHTTP Onlyとする。

CloudFront

3) Behavior
Webサーバーのコンテンツ・ディレクトリーごとに振る舞い(Behavior)を個別設定していく。Viewer Protocol Policyはユーザーからアクセスされる際のプロトコルを設定する。URLにhttp://と書かれても自動的にhttps://にリダイレクトしてほしいので「Redirect HTTP to HTTPS」に設定。

CloudFront

CloudFront

一方、WordPressコンソールが稼働するURLパスである wp-admin/* 以下がキャッシュを通るを避けるためにhttpもhttpsも許容。TTL(Time to live)も0秒に設定しておく。

続いてCache Based on Selected Request HeadersはNoneに。Query String Forwarding and Cachingはすべてフォワードするように。この設定でWordPressコンソールにアクセスできる…はずだがどうにも上手くいかず、URLを叩くと大量のリダイレクトが発生してブラウザに怒られたり、それを解決してもCloudFrontからのエラーが表示されたり大変だった。

そこで、WordPressのwp-config.phpを以下のように書き換えた。以下の最初の行は、wp-config.phpの最後の行ということになる。

/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */

/** Absolute path to the WordPress directory. */

if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');

define('FORCE_SSL_ADMIN', true);
$_SERVER['HTTPS']='on';
$_ENV[ "HTTPS" ] = 'on';

$_SERVER['HTTP_HOST'] = 'wonderwall.net';
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];

/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');

これでようやくコンソールも起動したし、WebサイトのコンテンツはCloudFrontのキャッシュにヒットしするようになった。ブラウザのキャッシュが効かない状態で、以前は2秒ほどかかっていたはずのところ、CloudFrontのキャッシュが効くことで 秒にまで短縮された。サーバーは東京リージョンにあるが、海外でも速度向上されているのだろう。これによるコスト削減は、アクセスする側のユーザー体験に紐付くのでプライスレス。

なお、CloudFrontを使ったこの方法でWordPressを稼働させた際に、記事の投稿編集画面でビジュアル・エディターが表示されなくなることがある。WordPressはビジュアル・エディターを表示するための判定としてWebブラウザーのUser Agentを確認するが、この方法だとWordPressが認識するUser Agentが「CloudFront」になるため、通常の設定だとビジュアル・エディターが見えなくなる。

解決方法は、wp-includes/functions.php に下記のコードを追加する(場所は末尾とかでOK)。

function dtbaker_wp_cloudfront(){
    add_filter('user_can_richedit','__return_true');
}
add_action( 'init', 'dtbaker_wp_cloudfront' , 9 );

コンテンツ・ファイルの定期バックアップ

以下のスクリプトをセットして、cronによる定期バックアップを行う。

#!/bin/sh

DATE=`date +%Y%m%d`
BACKUPDIR=/www-contents/backups
SUFFIX=PRODUCTION
tar cvzf ${BACKUPDIR}/$DATE-contents-$SUFFIX.tar.gz /www-contents/htdocs
mysqldump -u root -p7CXEpdvsexon --all-databases | gzip > ${BACKUPDIR}/$DATE-mysqldump-$SUFFIX.gz

cd ${BACKUPDIR}
DELDAY=`date --date '7 days ago' '+%Y%m%d'`

for LINE in `find -iname "*.gz"`
do
  FILEDATE=`echo "$LINE" | sed -e 's/[^0-9]//g'`
  if [ $DELDAY -gt $FILEDATE ] ; then
    echo "delete $LINE"
    rm -f $LINE
  fi
done

このスクリプトによるバックアップ・ルールは次の通りとなっている。

  1. バックアップしたファイルは /www-contents/backups ディレクトリーに保存される。
  2. バックアップしたファイルはgzip形式で圧縮される。
  3. バックアップの間隔は1時間に1回で、同じ日付内でのバックアップの場合は上書き保存される。
  4. バックアップしたファイルは最長7日間保存され、7日を過ぎると自動削除。
  5. 自動バックアップのスケジュールは cron コマンドにて定義。

余談:t1.microからt2.microへの移行

4年前に立ち上げたサーバーなだけに、旧世代のt1.microインスタンスというのをずっと使ってきた。2014年夏にローンチされたt2の方が性能がアップしているしインスタンスの稼働コストも安い。東京リージョンの場合t1.microが$0.026/h(月額$18.7)に対してt2.microは$0.0152/h(月額$10.9)と、かなり安い(共に2017年12月時点の価格)。

しかし、t1とt2では仮想化の仕様が違うため、AWSお得意のWebコンソールでちゃちゃっと移行ってわけにはいかないのだ。具体的にはt1の仮想化技術であるPV (paravirtual)からt2のHVM (Hardware-assited VM)へ、コマンドラインでコツコツと変換する必要がある。

今動いているサーバー(CentOS+もろもろ)の状態をそのままにPVをHVMへ変換し、新たに用意したt2.micro(1年間の無料枠あり)インスタンスで起動し直すという作業を行うわけだが、Qiitaのこの記事がとてもよくまとまっているし、コマンドをコピペするだけで進められるので、とても助かった。

https://qiita.com/cs_sonar/items/21dbb3462708e146a426 とは言っても一発で完了するほど甘くはなく、最後まで操作しても新インスタンスは起動せず最初からやり直し。それでも起動せず…三度目の正直でようやくうまくいった。何が悪かったか判然としてないが、ここかな?と思える部分は以下の引用箇所。

ボリュームのサイズは既存のPV AMIのディスク以上であればOKです(同じでいいと思います。) 「同じでいい」という言葉に頼れば良かったのだが、既存のPV AMIのディスクよりも数GB盛って作って失敗。同じにしたら上手くいった。

作業には1日かかったが、これで毎月8ドルほどのコスト削減。本日は、これまで。