WordPressを個人で可能な限りAWS構成にしてみた

2018年11月22日

こんにちは、おてらです。

私の以前のブログもAWS上で動かしていたのですが、利用しているサービスはEC2だけだったのであまりAWSの恩恵を受けているとは言えない状況でした。

そこで、ブログを新しくしようと決めたときにせっかくなのでAWSのサービスをふんだんに使ってみようと思い、そのことをまとめます。

個人でAWS上にブログを立てる分にはこれくらいの構成を取ってもいいんじゃないかなと思うので、興味がある人は是非やってみてください。

スポンサーリンク

全体構成

まずは全体の構成を最初に。といっても、珍しいものは使っておらず、「AWS WordPress」とかでググれば出てくる構成になっています。

ガチガチの~という割にはそこまで使ってなくね?という気持ちにならなくもないですが、WordPressをAWSで動かすにはこれくらいで十分でしょう。

※RDS使えばそれらしい構成になるんですが、個人でRDSはお財布に大打撃なので…。

ざっくり解説

EC2の構築

まず最初に取り掛かったのはEC2の構築とWordPressのインストールです。AWSのEC2のドキュメントにチュートリアルがあるのをご存知でしょうか?

基本的にこの2つのチュートリアルに沿って構築すればAmazon Linux2上にWordPressを立てることが出来ます。

ちなみに今回チュートリアルと違うのはApacheではなくNginxを採用した点です。それ以外は全く同じ手順を踏んでいます。

また、注意点としてELBを利用してSSL化する場合、WordPressの管理画面にアクセス出来ないといった症状が発生するため、以下の設定をwp-config.phpに投入します。

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
$_SERVER['HTTPS'] = 'on';

参考文献

Nginxの設定

今回Nginxを触るのが初めてだったので色々な文献を読み漁って以下のような設定にしました。

user nginx;
worker_processes auto;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
    multi_accept off;
    use epoll;
}

http {

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;
    client_max_body_size 0;

    limit_conn_zone $binary_remote_addr zone=addr:5m;
    limit_conn addr 100;

    server_names_hash_bucket_size 64;
    server_name_in_redirect off;
    port_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log;

    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_static on;

    server {
        listen 80;
        server_name otera05.xyz;
        rewrite ^/(.*) https://www.otera05.xyz/$1 permanent;
    }

    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name www.otera05.xyz;
        root /home/otera05.xyz/wordpress;
        index index.php index.html index.htm;

        charset utf-8;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
        client_max_body_size 20M;

        location / {
            try_files $uri $uri/ @wordpress;
            rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml$ "/index.php?xml_sitemap=params=$2" last;
            rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml\.gz$ "/index.php?xml_sitemap=params=$2;zip=true" last;
            rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.html$ "/index.php?xml_sitemap=params=$2;html=true" last;
            rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.html.gz$ "/index.php?xml_sitemap=params=$2;html=true;zip=true" last;

            if (-f $request_filename) {
                expires 30d;
                break;
            }

            if (!-e $request_filename) {
                rewrite ^.+?($/wp-.*) $1 last;
                rewrite ^.+?(/.*\.php)$ $1 last;
                rewrite ^ /index.php last;
            }
        }

        location /wp-admin {
            set $do_not_cache 1;
        }

        location ~ \.php$ {
            try_files $uri @wordpress;
            fastcgi_index index.php;
            fastcgi_split_path_info ^(.+\.php)(.*)$;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_param SCRIPT_FILENAME /home/otera05.xyz/wordpress$fastcgi_script_name;
            include fastcgi_params;
        }

        location @wordpress {
            fastcgi_index index.php;
            fastcgi_split_path_info ^(.+\.php)(.*)$;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_param SCRIPT_FILENAME /home/otera05.xyz/wordpress/index.php;
            include fastcgi_params;
        }

        location ~ \.(htaccess|htpasswd|git|svn|sqlite3|sql|tpl|scss) {
            deny all;
        }

        error_page 404 /404.html;
        location = /40x.html {
            root /usr/share/nginx/html;
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        root /usr/share/nginx/html;
        }
    }
}

後述しますがSSL証明書の関係で、wwwなしでアクセスした場合はwww付きのURLにリダイレクトするようにしています。

Route 53の設定

ブログを新しくするにあたり、ドメイン名は覚えやすくかつ短いものにしたいと思い、新たにotera05.xyzのドメインをお名前.comで取得しました。

このドメインを使ってEC2やCloudFrontの設定を行う場合、Route 53でレコードの設定が出来た方が様々都合が良いのでRoute 53にゾーンを作成しました。

最初に作成したレコードはwwwだけで、その時はEC2のパブリックIPを指定して名前でWordPressにアクセスできるかを確認しました。(wwwの現在の値は後述するALBのエイリアス名です。)

NSレコードとSOAレコードはゾーンを作成したタイミングで自動的に生成されるので、お名前.comの管理画面よりネームサーバをここで表示されているNSレコードのValueを設定します。

※余談ですがお名前.comでドメインを取得した後、登録したメールアドレス宛に正常性確認のメールが配信されていて、そこに書いてあるリンクをクリックしないと名前解決できないことに気づくのにかなり時間がかかりました。この認証必要かなぁ?

レコード名 説明
cdn.otera05.xyz 画像ファイルなどをS3+CloudFront経由で配信するためのホスト名。向き先はCloudFrontです。
otera05.xyz wwwなしでアクセスする用のレコード。向き先はALBです。
www.otera05.xyz www付きでアクセスする用のレコード。向き先はALBです。

このほかに存在している2つのCNAMEのレコードは後述するACMで証明書を発行する際にドメイン認証を行ったためにAWSによって自動的に追加されたレコードです。

証明書の発行

今までのブログもSSL化はしていたのですが、Let’s Encryptによる証明書の自動発行&更新で対応していました。

AWSではAmazon Certificate ManagerというAWSのサービスに適用すれば無料で利用できるSSL証明書の発行サービスがあり、これを使うことにしました。

ACMをEC2で利用する場合、EC2に直接設定することは出来ないのでEC2の前段にELBを設置し、そのELBにACMで発行した証明書をインストールすることでサイトのSSL化が可能です。

手順はとても簡単です。

まずAmazon Certificate Managerから東京リージョンで新規証明書をリクエストします。

コメントにある通り、ワイルドカード証明書が発行できます。(しかも無料で!すごい!)

よって今回はワイルドカード証明書を利用しました。

続いて、リクエストしている証明書のドメインをリクエストした人物(または組織)がきちんと保有しているかを検証する必要があります。

検証方法は2種類あり、そのドメインのDNSゾーンに指定されたCNAMEを記述するか、ドメイン管理者のメールアドレス宛てに配信されるメールを用いた検証かのいずれかです。

otera05.xyzのドメインはRoute 53を利用しているのでDNSの検証が楽です。

DNSの検証を選択して確認ボタンを押すと、「このCNAMEをゾーンに登録してね」と表示されます。手動でも登録できますが、親切なことにRoute 53に該当のレコードを自動で登録してくれるボタンもあるのでそれを実行すれば一撃でレコードの登録が完了します。

Route 53にCNAMEレコードが生成されれば数分で検証が完了し、証明書が利用できるようになります。

 

また、CloudFrontで利用する証明書についてですが、「cdn.otera05.xyzもこのワイルドカード証明書でいけるじゃん」と思うかもしれませんが、CloudFrontに設定できるACMの証明書はバージニア北部リージョンに限定されます。

よって、リージョンをバージニア北部に変更したうえで、同様の手順を踏んでcdn.otera05.xyzの証明書を発行しました。

先述のRoute 53のレコードにCNAMEが2つあったのはこのためです。

ELB(ALB)の構築

さて、証明書を発行したのでELBを作ります。

ELBにはApplication Load BalancerとNetwork Load BalancerとClassic Load Balancerの3種類があります。

それぞれのロードバランサの違いについては公式ドキュメントやクラメソさんの記事を読んでみて下さい。(丸投げ)

Classic Load Balancerについては旧式のロードバランサなので新たに構築する場合、ALBかNLBのいずれかになるでしょう。

業務でALBを構築した経験があるのでALBにしました。

 

ALBを構築するにあたり、ロードバランサとEC2の通信についてちょっと理解しておく必要があります。

今回、サイトをSSL化しているわけですが、証明書は先ほど発行したワイルドカード証明書を利用します。この証明書はALBに設定することで使えるようになります。

クライアントからのルーティングとしては80番と443番でのアクセスが考えられるため、ALBで待ち受けるポートは80番と443番を指定します。

続いて証明書の設定画面になるので、ACMで作成したワイルドカード証明書を選択します。

なお、この時Global Signなどで購入済みの証明書があれば「IAMに証明書をアップロードする」を選択すればその証明書を利用することが可能です。

個人サイトなどの場合、ACMで発行した方がコスト的にも運用的にも圧倒的にメリットが大きいので個人的にはACMを推奨します。

セキュリティグループの設定としては、このロードバランサに対してはHTTPとHTTPSさえ解放できていればいいのでそのように設定したセキュリティグループをアタッチします。

 

ルーティングの設定で注意したいのは、「HTTPSでアクセスさせたいから443だ!」と勢いでやってしまいがちなのですが(私だけか?)、ターゲット(=アクセスするEC2)に対しては80番への通信でOKです。

ここでターゲットグループのプロトコルをHTTPS/443にしてしまうと、EC2も443で待ち受ける必要があり、それはつまりEC2にも証明書を設定しなければならなくなるということです。

「ALBから80番ポートに対してアクセスする」というターゲットグループを作ったので対象のEC2インスタンスを登録します。

下に稼働しているEC2インスタンスのリストが表示されているので該当のインスタンスを選択し、ポートを80として「登録済みに追加」ボタンを実行すると画面上部の「登録済みターゲット」欄に表示されます。

あとは内容を確認して作成し、デプロイが終わるのを待ちます。数分かかったと思います。これでALBの構築は完了です。

Route 53の設定その1

ALBの構築が完了した段階でいったん名前でWordPressにアクセス出来るようにしたいと思います。

Route 53の画面で、対象のドメインを選択し、Create Record Setで新しいレコードを登録します。

Nameにwwwを入力し、TypeをAレコードにします。AliasをYesにすると、Alias TargetにAWSのサービスから設定できるものの一覧が表示されます。

ALBのデプロイが完了されていればここに先ほど作ったALBの名前が表示されているのでそのALBの名前を選択し、レコードを作成します。

wwwなしの場合、Nameの欄を空欄にしてAlias TargetをALBにすればOKです。

これで名前でアクセス出来るようになりました!

画像アップロード用のS3バケットの作成

続いて画像ファイルの保存用にS3バケットを利用したいのでバケットを作成します。

バケット名はCloudFrontで利用するホスト名をそのまま設定します。

私の場合、https://cdn.otera05.xyz というURLで配信したかったので「cdn.otera05.xyz」というバケット名で作成しました。

※これはCloudFrontを経由しない場合、S3のバケットをパブリックにすることでバケット名でのアクセスが可能になるため、見かけ上「cdn.otera05.xyz」のホスト名でアクセス出来るようにするための手法です。よってCloudFrontを使ってカスタムドメインの設定を行う場合、厳密にはバケット名を配信したいホスト名にする必要はありません。

S3バケットを作ったらバケットの設定でStatic website hostingを有効にします。

IAMユーザの作成

WordPressからS3に画像ファイルをPUTするためには、EC2からS3に対するアクセス権限が必要です。

IAMから新規でユーザを作成し、S3FullAccessの権限を持たせます。ユーザ名は適当に「wordpress」としました。

この時発行される「アクセスキーID」と「シークレットアクセスキー」を忘れずにメモ(ダウンロード)しておきます。これらのキーをEC2に設定することでEC2からS3にアクセス出来るようになります。

CloudFrontの作成

続いてCloudFrontの作成です。ここは設定項目が多いので気合入れていきましょう。

いわゆるCDNのサービスで、画像ファイルなどをキャッシュサーバにキャッシュし、クライアントに対して高速に効率よく配信してくれるサービスです。

CloudFrontを作成するにあたり「Web」と「RTMP」がありますが、Webの方です。

まずはOriginの設定から以下のようにガシガシ設定していきます。

  • Origin Dmain Name:先ほど作成したS3バケットを指定します。
  • Origin Path:空欄で可
  • Origin ID:自動生成されるので特に変更の必要なし
  • Restrict Bucket Access:S3バケットに対するアクセスを、CloudFrontからのみに制限するかどうか。
    私はCloudFront以外からS3にアクセスさせる意味はないと判断し、Yesにしました。
  • Origin Access Identity:上記をYesにすると出現。新たにIdentityを作成します。
  • Comment:同上。デフォルトのままでOK。
  • Grant Read Permissions on Bucket:同上。Yesにし、「作成したIdentityを用いてS3にアクセス出来るようにする」というポリシーを対象のS3バケットに自動的に記述してもらう。

続いてBehaviorの設定です。

  • Viewer Protocol Policy:HTTPをHTTPSにリダイレクトさせたいので Redirect HTTP to HTTPS を選択。
  • Allowed HTTP Methods:画像ファイルの取得だけなのでGET,HEADを選択。

残りは基本的にデフォルトのままにしています。

最後にDistributionの設定です。

  • Price Class:デフォルトでOK。
  • AWS WAF Web ACL:AWS WAFは利用していないのでNone
  • Altenate Domain Names:配信したいカスタムドメイン名を指定します。
  • SSL Certificate:Custome SSL Certificateを選択して、ACMで作成したCloudFront用の証明書を選択します。
  • Custom SSL Client Support:SNIのみで。
  • Security Policy:デフォルト(推奨値)で。

続いて

  • Enable IPv6:Offにしました。

ひとまずここだけ設定を変え、あとはデフォルトのままです。

これでCloudFrontの設定は完了です。デプロイしましょう。

デプロイが完了するまで15分~30分ほどかかりますのでWordPressからAWSに接続するためのプラグインの準備を行います。

WordPressでプラグインの導入

必要なプラグインは以下の2つです。

Amazon Web Servicesを導入したら、「wp-config.php にアクセスキーIDとシークレットアクセスキーを書いてね」と指示があるので先ほど作ったIAMユーザのキー情報を以下のように書き込みます。

define( 'DBI_AWS_ACCESS_KEY_ID', '*********************' );
define( 'DBI_AWS_SECRET_ACCESS_KEY', '****************************************' );

設定後、サービスを再起動するとこんな表示に変わります。

これで、このWordPressからIAMユーザのアクセスキーを利用してS3にアクセス出来るようになりました。

続いてWP Offload Media Liteの設定です。

StorageとしてS3を選択し、Bucketに作成したS3バケットを指定します。Pathはデフォルトでも問題ないでしょう。

カスタムURLを設定する項目があるので配信したいホスト名を入力して設定完了です。

Route 53の設定その2

もう少しでゴールです。wwwやwwwなしのレコードを作った時と同様に、CloudFront + S3バケット用のレコードを作ります。

これですべての設定が完了です。

動作確認

試しに画像ファイルをWordPress上にアップロードしてみます。

すると、以下のようにS3バケットじょうにwp-contentというディレクトリが作成され、更に階層を掘り下げていくとちゃんと画像ファイルがS3バケットにアップロード出来ていることが分かります。

ファイルのURLをブラウザのアドレスバーに入力して画像がちゃんと表示されればCloudFront経由で配信出来ている証拠です。

終わりに

書いてみるとかなり長くなりましたがやってることとしては大分シンプルです。

AWSを使うとSSL化するのが簡単だったり恩恵はたくさんなので、せっかくだしやってみようという軽いノリでやってみました。

ベンダーロックインされるのが辛いかもしれませんが。

あとこうやってアウトプットするクセをつけるため、というのも理由の一つです。

今後も継続してアウトプットできるように頑張りまーす。

フォローする

スポンサーリンク