エクストーンの豊田です。エクストーンではWebアプリケーションの開発とともにインフラの構築や運用等も行っています。どのような構成でサービスを実現するかはサービスの種類や機能、規模等によって異なるのですが、今日はシンプルなウェブサイトをホスティングする際にエクストーンでどのようにインフラ構築を行っているかをお話ししたいと思います。
インフラ構築で主に利用しているサービス・技術スタック
ほぼ前回の記事のおさらいになりますが、改めて記載しておきます。
- API Server, 定時処理ワーカー
- AWS
- EC2, ECS Fargate (Ruby on Rails, express)
- Lambda (TypeScript)
- Google Cloud
- Cloud Functions (express)
- Cloud Run (Ruby on Rails, express)
- その他
- Heroku (Ruby on Rails)
- AWS
- Webホスティング、コンテンツ配信
- AWS
- S3 (HTML, React) + CloudFront (CloudFront Functions, Lambda@Edge)
- Google Cloud
- Cloud Storage (HTML, React) + Cloud Functions (express)
- その他
- Vercel (Next.js), Cloudinary (画像特化)
- AWS
- DB
- AWS
- Amazon Aurora (MySQL, PostgreSQL), DynamoDB
- Google Cloud
- Cloud SQL (MySQL, PostgreSQL), firestore
- AWS
- イベントキュー・通知
- AWS
- SQS, SNS
- Google Cloud
- Pub/Sub
- AWS
- ログ収集・検索、エラー検知
- AWS
- CloudWatch Logs + S3 + Athena
- CloudWatch Logs + Elasticache (Kibana)
- Google Cloud
- Cloud Logging + BigQuery
- その他
- Bugsnag, Sentry
- AWS
- CI/CD
- AWS
- Code Build, Code Deploy, Code Pipeline
- Google Cloud
- Cloud Build
- その他
- CircleCI, GitHub Actions, Bitrise
- AWS
ホスティングのインフラ構築の実例
DB等を必要としないシンプルなWebサイトの場合は、バックエンドで動的な処理を行う必要がほとんど無いため、基本的にはHTMLやJavaScriptのコードをS3等のWebホスティングが可能なストレージサービスにアップロードします。HTTPSに対応するため、CloudFront等のコンテンツ配信ネットワークサービスと組み合わせて使います。
React等を用いてSPA(Single Page Application)としてサイトを構築した場合、SNSでのシェア時にページの情報が共有されるような仕組みが必須になるケースがあります。その場合、SNSのクローラーにサイトをクロールして貰い、metaタグ等の情報を読んでもらう必要があるのですが、多くのクローラー(TwitterやFacebookのbotなど)はJavaScriptを理解できないため、SPAで構築される情報をサーバー側でレンダリングして返してあげる必要があります。
シンプルなWebサイトの場合、真面目にサーバーサイドレンダリングの仕組みを構築する余裕がない場合はCloudFront Functionをクローラーからのアクセスに対してトリガーさせるようにし、metaタグのみを返すような処理を実装することでこの問題に対応することが多いです。
AWSを用いた構築例
AWSを用いた場合、以下の図のような構成になります。やることはシンプルでS3にHTMLファイルやJavaScript等、ブラウザで閲覧させたい情報をアップロードするだけです。それに加え、バックエンドで動的な処理が必要な箇所はCloudFront Functionsで記述します。
CloudFront FunctionsはCloudFrontのエッジロケーション(キャッシュファイルを保持している世界中に分散しているWebサーバー)で実行されるため、実行時のオーバーヘッドが低い特徴があります。URLの書き換えやBASIC認証など、nginxやapacheなどのWebサーバーの設定で記載していたような処理を実行させるのに適しています。
エッジロケーションへのアクセス毎、あるいはオリジンへのアクセス毎など、関数実行のタイミングを細かく制御できる点がCloudFront + CloudFunctionsの利点と考えています。
- GitHub: ソースコード管理
- CircleCI / GitHub Actions / Code Build: ビルドとデプロイ処理
- S3: ビルド後のコンテンツを保持。Webサイトのオリジン情報となる
- CloudFront: ユーザーからアクセスされるエンドポイント。HTTPS対応もここで行う
- CloudFront Functions: 動的な処理が必要な箇所の実装
- ステージング環境の認証処理
- サーバーサイドレンダリングの処理
- その他、リクエストやレスポンスの書き換え等の処理など
Google Cloudを用いた構成例
Google Cloudを用いた場合は以下の図のような構成になります。ほとんどがAWSと同等の機能が提供されているため、ほぼ変わらない構成が実現できます。
Google CloudではIdentity-Aware Proxyを用いたアクセス制限を行うことが出来ます。特定のGoogleアカウントに対してアクセス権限を与えることが容易に実現できるため、よりきめ細かいアクセス制限を簡単に実装したい場合はGoogle Cloudで実現することを考慮します。
一方でエッジロケーションで任意の関数を実行させることは出来ないため、プロキシ的に動的な処理を実行したい場合はパフォーマンス面ではAWSに軍配が上がると考えています。
- GitHub: ソースコード管理
- CircleCI / GitHub Actions / Cloud Build: ビルドとデプロイ処理
- Cloud Storage: ビルド後のコンテンツを保持。Webサイトのオリジン情報となる
- Cloud Load Balancing: ユーザーからアクセスされるエンドポイント。HTTPS対応もここで行う
- Cloud Functions: 動的な処理が必要な箇所の実装
- サーバーサイドレンダリングの処理
- その他、リクエストやレスポンスの書き換え等の処理など
- Identity-Aware Proxy
- IAM情報を用いた認可
おわりに
この記事ではシンプルなWebホスティングを行う際に、AWSやGoogle Cloudでどのように構築するかについて書いていきました。シンプルな構成ではありますが、単一障害点を無くしたり、メンテナンスコストを抑えたりと、サイト構築に必要な要素は一通り含まれているかと思います。
実際に自分もサイト構築後に「TwitterやFacebookでシェアした際にサイトの情報が正しく表示されない」と言われ、そもそもインフラがサイトの要件を満たしていなかったという経験があり、シンプルなWebホスティングでも必ずサーバーサイドでの動的処理を入れる余地を残すように心がけています。
インフラ関連についてはまだまだエクストーンで利用しているサービスがたくさんあるので、また近いうちにこの場で紹介できればと考えています。