S3 の静的 Web サイトを Envoy でホスティング

| 4 min read
Author: shigeki-shoji shigeki-shojiの画像

S3 に静的なコンテンツを配置して公開する場合、よく見られる構成は CloudFront とを組み合わせるパターンです。ほとんどの場合、これで問題ありません。しかし、一部のパターン、例えばエンタープライズで利用されるフロントエンドの場合には、VPN を通じたアクセスのみを許可している場合があります。このような場合、世界中の多数のユーザに低レイテンシーな配信を目的としている CloudFront のような CDN が適切とは言い難いものとなります。

こうした場合、Nginx のような HTTP サーバ製品を利用することもできますが、より軽量な Envoy を利用する例をここでは説明します。

図

Envoy はとにかく軽量かつ高機能で、そのため Kubernetes 環境ではデータプレーンのサイドカーとして多用されている Proxy です。Envoy 自体は、通常のプロセスとしても、Docker や Pod のようなコンテナとしても実行することができ、そのため AWS App Mesh では、同社の Kubernetes 環境である EKS 以外に EC2 や ECS もサポート対象となっています。

S3 バケットの作成

#

まず、静的コンテンツを配置するための S3 バケットを作成します。プロダクション環境では、AWS PrivateLink を使って VPC 外から S3 へのアクセスを遮断すべきですが、ここでは簡単に試してみたいため、パブリックアクセスで読み取りを許可するようにします。

例では、バケット名を example-bucket として、バケットポリシーは下のようになります。

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::example-bucket/*"
}
]
}

作成したバケットに index.html をアップロードしておいてください。

確認のため、ブラウザで次の URL にアクセスしてみます。

https://example-bucket.s3.amazonaws.com/index.html

index.html が表示されることを確認したら、次のステップに進みます。

Envoy Proxy の定義ファイルの記述

#

front-envoy.yaml というファイルを作成します。

static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: AUTO
stat_prefix: ingress_http
route_config:
name: s3_route
virtual_hosts:
- name: static_web
domains:
- "*"
routes:
- match:
prefix: /
route:
cluster: s3
auto_host_rewrite: true
regex_rewrite:
pattern:
google_re2: {}
regex: '^(.*)\/$'
substitution: '\1/index.html'
http_filters:
- name: envoy.filters.http.router

clusters:
- name: s3
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: s3
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: example-bucket.s3.amazonaws.com
port_value: 443
hostname: example-bucket.s3.amazonaws.com
transport_socket:
name: envoy.transport_sockets.tls

admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9901

example-bucket.s3.amazonaws.com は作成したバケット名に合わせて修正してください。

ここでのポイントは次のようになります。

  1. S3 のエンドポイントの IP アドレスだけでは、バケット名が何かがわからないため、auto_host_rewrite: true を設定して、ホスト名が S3 に渡るようにしています。
  2. URL のパスが / の時に、/index.html にリライトするための regex_rewrite を設定しています。
  3. S3 の IP アドレスが変化することも想定して、type: LOGICAL_DNS に設定しています。
  4. S3 のエンドポイントは現時点では IPv4 のみと想定されるため、dns_lookup_family: V4_ONLY を指定しています。
  5. 1 と同じ理由で、hostname を設定しています。

実行

#

docker でも nerdctl でも実行可能です。ここでは、nerdctl でのコマンド例ですが、nerdctldocker に置き換えれば Docker でも同様に実行可能です。

nerdctl run -it --rm --name envoy -v `pwd`/front-envoy.yaml:/etc/front-envoy.yaml -p 8080:8080 -p 9901:9901 envoyproxy/envoy:v1.21-latest -c /etc/front-envoy.yaml --service-cluster front-proxy

Envoy Proxy が機能しているかを確認するためには、次の URL でできます。

http://localhost:8080/

Envoy Proxy を利用するメリット

#

Envoy Proxy を利用すると、ここで紹介した静的 Web サイトのホスティングだけでなく、以下のような機能やその他多くの機能を提供することができます。

  • OAuth2 や OpenID Connect 等の JWT を使うユーザ認証
  • Open Policy Agent 等を使った RBAC によるユーザ認可
  • A/B テスト、カナリア

最後に、ここで紹介した設定ファイル等が GitHub リポジトリ にあります。

参考

#
豆蔵デベロッパーサイト - 先週のアクセスランキング
  1. 自然言語処理初心者が「GPT2-japanese」で遊んでみた (2022-07-08)
  2. Tauri でデスクトップアプリ開発を始める (2022-07-08)
  3. Deno による Slack プラットフォーム(オープンベータ) (2022-09-27)
  4. Jest再入門 - 関数・モジュールモック編 (2022-07-03)
  5. ORマッパーのTypeORMをTypeScriptで使う (2022-07-27)
  6. 第1回 OpenAPI Generator を使ったコード生成 (2022-06-04)
  7. 直感が理性に大反抗!「モンティ・ホール問題」 (2022-07-04)
  8. Rust によるデスクトップアプリケーションフレームワーク Tauri (2022-03-06)
  9. 箱ひげ図で外れ値を確認する (2022-05-18)
  10. Nuxt3入門(第1回) - Nuxtがサポートするレンダリングモードを理解する (2022-09-25)