Nire.com の Web サーバ周りビフォーアフター。あくまで個人での AWS 構成ですが、マネージドサービスを使いすぎると割高になるので、自前にかなり戻しましたというお話。

やりたいこと / 要件

  • マネージドどっぷりの WordPress 構成が高い。docker ベースですべて作り直したい。
  • CDN あり。静的コンテンツは S3。WordPress 2台分 (ドメイン 2つ分) を docker コンテナ x 2 に移し替える。あと Alpine Linux でがんばる。

Before 構成

Nire.com は自前ですべてを構成して運用しています。(今どきオンプレではなく AWS ですが)

昨年末の段階では、この構成で動いていました。
AWS 上でマネージドサービスをふんだんに使った構成。

Nire.com ネットワーク構成図 2020/12
  • CDN … CloudFront をかぶせて、アプリケーションサーバに負荷が直撃しにくくする。静的コンテンツは S3 へ、WordPress 管理の URL は ALB へ。
  • S3 … 静的な画像ファイルと、WordPress 移行する以前の静的 html ファイル群 (山田家とか) はすべて S3 に置く。
  • ロードバランサ … CloudFront の背後に Application Load Balancer (ALB) を立てる。2台あるアプリケーションサーバのロードバランスと、WAF によるサーバ保護。
  • アプリケーションサーバ … ALB 背後に WordPress をインストールした EC2 インスタンスが 2台の冗長構成。1台が落ちても OK。
  • DB … Aurora serverless で、使った分だけ従量制で課金。
    (serverless v2 というのがありますが、まだ東京リージョンには来ていませんでした。)

Before 構成の課題

マネージドサービスで、運用負荷を軽減しつつ、可用性も確保して、コストも最低限に…という企業ユースでよくある構成で、まあ楽は楽なのですが、コストはちっとも安くないという結論に。

  • ALB … 最低でも月額 $25 程度かかるし、1台で複数ドメインを代表させることができない。自分で冗長構成を組んでファイアウォールを入れるとたしかに面倒ではあるのですが。
  • DB … これが最大の誤算。serverless なので、CloudFront でページキャッシュされてしまえば、DB にはそこまでアクセスは来ない -> 月額固定料金でインスタンスを立てるより安いだろう、と予想したのですが、クエリキャッシュをある程度挟んでも、月額 $76 ほど課金されてしまいます。

アプリケーションサーバだけ docker で、ALB や DB はマネージドでというのも考えましたが、結局 CDN 以外は、コスト優先で docker 上ですべてを作り直すことにしました。

After 構成

現在の構成は以下の通りです。

Nire.com ネットワーク構成図 2021/01
  • 全体に docker 上にすべてを構築し直した。ECS は使わない。自前で EC2 による docker ホストで最低限の構成。
  • ロードバランサ … HAProxy による reverse proxy, 死活監視, フェイルオーバー。
  • アプリケーションサーバ
    • Alpine Linux ベースの公式 WordPress docker イメージからスタート
    • プラグイン … docker イメージに作成時に、公式リポジトリから最新のものを引っ張ってくる
    • カスタマイズしたプラグインとテーマは、S3 に置いて docker イメージ作成時に引っ張ってくる
  • DB
    • MySQL DB の公式 docker イメージ
    • docker 的に永続化しているので、docker ホストに再起動では揮発しない
    • 定期的に backup したりするあたりは、自分でバッチサーバを構成

実際に動いている様子

まずロードバランサ HAProxy の stats 画面から。

HAProxy Stats

HAProxy backend に kusanagi とあったり、backend の死活が色々で messy ですが、最近まで kusanagi ベースの EC2 インスタンスが動いていたのを、徐々に docker ベースのアプリケーションサーバに移行していった名残です。

docker 後は kusanagi (Runs on Docker) を使用していません。

そして起動中の docker の一覧を出してみるとこんな感じです。

$ docker-compose ps
     Name                   Command               State                    Ports
--------------------------------------------------------------------------------------------------
nirecom_app1_1   docker-entrypoint.sh php-fpm     Up      xxxxx/tcp
nirecom_app2_1   docker-entrypoint.sh php-fpm     Up      xxxxx/tcp
nirecom_bat_1    tail -f /dev/null                Up
nirecom_db_1     docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, yyyyy/tcp
nirecom_web_1    /docker-entrypoint.sh ngin ...   Up      0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp

(ポート番号は適当にぼかしました)

web (nginx) が 1コンテナあって、URL でマップ、後ろで WordPress サーバが 2コンテナ動いています。

WordPress が 2つなのは、運用がまったく異なる https://www.nire.com/ と https://kurashibit.nire.com/ を同居させるためです。独立性を高めるために、プラグインもテーマもそれぞれ別管理です。

DB コンテナは 1台で table のあまり中をいじりたくないので、バックアップ・リストア等のメンテは別立てでバッチ用のコンテナを立てます。

web, app1, app2, bat の各コンテナはすべて軽量化のため、Alpine Linux ベースで構成しています。

なお、アプリケーションサーバの起動は terraform apply で立てられるようにしてあります。

運用してみて苦労した点

WordPress を docker に載せるのが面倒! 🙂

WordPress を docker 化してみた…というブログ記事をよく見かけます。WordPress 公式 docker イメージを pull してきて 1コンテナ立てるなら一発なのですが、問題はその後です。

運用に載せるには、カスタマイズしなくていいから最新のものを取得するべきプラグインと、カスタマイズした自作プラグインを別々のところから引っ張ってくる必要があります。テーマもしかりです。

AWS CLI v2 は Alpine Linux をサポートしない

カスタマイズしたテーマやプラグインは、外から見えない S3 バケットから引っ張ってくるようにしたいが、そのためにコンテナ内に AWS CLI が必要です。

しかし、AWS CLI は Alpine Linux を公式サポートしていないため詰みます。そのため、glibc を別にインストールするとか hack する必要があります。

Ubuntu で apt install awscli 一撃か、AWS 公式 zip 持ってくるなりすりゃいいじゃん…と思うかもしれませんが、負けた気がするじゃないですか 🙂

複数 WordPress コンテナの実例がない

web – wordpress – db が 1:1:1 台構成だったら、先人の知恵で docker-compose.yml が一杯挙がっていますが、うちは wordpress が 2台、ドメインによって振り分けになっているために、参考になる例がほとんどありません。

結局、ほぼ自前で Dockerfile と docker-compose.yml を書き起こす羽目になりました。

DB は永続化したいがホストに依存したくない

Docker コンテナを再起動しても、DB に書かれた記事が消滅しないようにするには、DB volume を永続化する必要があります。

一番楽な手段は、docker ホスト自身のディレクトリに bind mount することですが、ポータビリティないんですよね。ホストから環境を分離独立させるために docker 化したのだから、volume も論理的には独立させるべきなので、docker 管理の volume としてマウントしています。

どうやって DB を初期化するか問題

まあ entrypoint を書いているんですが。最初 nire.com の全記事を格納した dump を S3 から落として初期化するようにしたところ、1回の docker 起動に時間がかかってしょうがない 🙁 ので、とりあえず WordPress DB は構成したが記事は空っぽのスタブ版 dump で起動し、後からガチ記事 dump を流し込む 2段構え構成に変えました。

まとめ

WordPress の docker 化運用はイバラの道!
だけど docker-compose up 一撃で動くと楽しいです。