はじめに
こんにちは、Slideflowのバックエンド開発を担当しているAです。
弊社では、パワポから簡単にWebサイトを作れるという Slideflowという サービスを提供していますが、以前は、弊社指定のサブドメインでの公開しかできませんでした。
しかし、サブドメインではなくお客様によっては、指定したドメインでのWebサイト公開をしたいという需要があり、先日、独自ドメインでもWebサイトが公開できるように対応しました。
以下は、その時のプレスリリースです。
https://prtimes.jp/main/html/rd/p/000000006.000083539.html
Wantedlyでもこの独自ドメインでのWebサイト公開を可能にしているシステムを紹介したいと思います。
SSL証明書動的読み込みシステム
システム構築経緯
お客様が作成したWebサイトを独自ドメインで公開するためには、通信経路での第三者による情報の盗聴や改ざんのリスクを防止するため、https化は必須で、そのためにはドメインごとにSSL証明書を発行する必要があります。弊社では、インフラにAWSを採用していますが、始めは、このSSL証明書をAWSリソースを使って発行・設置しようと思ってました。その方が、SSL証明書の更新や削除をAWS任せにできるためです。ところが、下記2点の制限があるため、
(Slideflowの公開page数は、理論上はいくらでも増やせる)
SSL証明書の発行は、Let's Encrypt を採用することにし、SSL終端にAWS リソースを使わないアーキテクチャーを再考することにしました。
システム目標
- page数の制限なく独自ドメインでの公開が行えること
- それなりのスループットを期待できること。
- 公開したwebサイトのtraffic増の際、スケールが簡単に行えること。
システムアーキテクチャ
システムを理解してもらうために、システム構成図を説明させていただきます。
まずは、SSL証明の取得・更新のシステムから。
[処理の流れ]
- 独自ドメイン設定
- Slideflowユーザーが、Slideflow上で独自ドメインの設定を行う。
- Slideflow systemは、Api Gateway経由で、Lambda cert-createにリクエストを投げる。
- cert-createでは、 yaacを利用し Let's Encrypt で証明書を発行し、SSL 証明書 (crt) と 秘密鍵 (key) を ElastiCache Redis とS3 に保存する。(S3に保存したデータは通常は使用しませんが、ElastiCache Redis 障害時の復旧用の永続データとして保存しています。)
- SSL証明書更新
- EventBridge (cron) で Lambda cert-update を定期実行する。
- Lambda cert-updateでは、SSL証明書の有効期限が30日以下の証明書のドメインリストを取得し、 Let's Encrypt で証明書を更新し、SSL 証明書 (crt) と 秘密鍵 (key) を ElastiCache Redis とS3 に保存する。
続いて、Webコンテンツ配信側のシステム構成図です。
[処理の流れ]
- エンドユーザーがSlideflowで公開されたコンテンツにアクセスすると、外部NLB経由で、Fargate cert-distributorにリクエストが投げられる。
- SSL終端であるFargate cert-distributorは、nginx + mrubyが動いていて、リクエストがあったドメインの、SSL 証明書 (crt) と 秘密鍵 (key) を ElastiCache Redis から取得し、取得した crt, key を元に SSL/TLS ハンドシェイクし、バックエンドの内部NLB経由のWeb serverへreverse proxyを行って、HTMLを表示する。
上記構成で、 SSL証明書の取得上限がなく、数十ミリ秒程度でレスポンスが返るシステムを構築することができました。また、Web serverのtraffic増の際には、Fargate Taskの増加・Elastic CacheのScaleUp・SchaleOutでスループットを増やすことが可能となっています。
Elastic Cache RedisのKey設計については、HMGETよりMGETの方が若干早かったので、crt, keyを単純なString型で保存するようにしました。
nginx + mrubyについては、基本的には ngx_mrubyをビルドしたイメージをECRリポジトリにPushし、そのイメージをECS Fargateで起動するようにしています。
はまったところ
- ECRの Docker imageがECS Fargateで使えなかった。
- AWS Fargateで起動する Docker imageが、 arm64ベースだったため。x86_64ベースのイメージにしたら、使えた。(但し、aws document によると、arm64にも対応してるようだ。)
- serverless frameworkを使用してdeployしているのだが、Fargate タスクの起動に失敗すると、すぐ失敗になるわけでもなく、いつまでたっても、deployが終わらない。待ちくたびれて、CloudFormationのスタックを削除するのだが、削除もそれなり時間がかかる。
- 一通り起動失敗する原因を潰した後は、Fargate タスクの起動に失敗することがなくなったので、気にならなくなった。
- 独自ドメインを使用したい場合は、お客様のドメインのAレコードに弊社の外部NLBのIPを設定してもらう必要があり、設定後、Lambda cert-createでチェックしているのだが、PHPの関数「dns_get_record」を使うと、下記のようなエラーがでて、チェック処理が失敗することがあった。
Warning Error: dns_get_record(): A temporary server error occurred. - php-digを使うことで回避した。
- 内部NLBにぶら下げたWeb ServerのHealth checkが通らなかった。
- NLBには、セキュリティグループが設定できないため、Web Serverのインバウンドには、セキュリティグループではなく、NLBのソースIPを指定する必要があった。
参考
システムの構築については、下記の記事を参考にしました。
- https://tech.medpeer.co.jp/entry/2021/08/03/090000
- https://qiita.com/akiray03/items/aeebc4de13bd961c1215
- https://tech.pepabo.com/2016/12/02/ngx-mruby-dynamic-cache/
- https://takumakume.tech/blog/2016-07-14-connection-recycle/
おまけ
ちなみに、弊社のインフラはAWSですが、インフラにGCPを採用している場合は、もっとシンプルな実装が可能となります。(笑)
(たいていのサービスであれば、100万個までの証明書に対応していれば、十分かと思われますので。)
以前からあったロードバランサのマネージド証明書では最大15個の制限がありましたが、Certificate Manager ではロードバランサごとに最大100万の証明書をサポートします(デフォルトでは 100 に制限されています)
さいごに
弊社では一緒に働いてくれるエンジニアを募集しています。
様々なスキルをもったメンバーとのチーム開発やプロダクトの改善に興味のある方、一緒にSlideflowを良くしていきませんか?