通配符子域指向相应的S3 / CloudFront子目录

lev*_*von 5 dns wildcard-subdomain amazon-s3 amazon-cloudfront amazon-route53

我需要多个子域指向Amazon S3上的单个存储桶/子目录(同步到CloudFront发行版),在其中托管一些静态文件。

这样任何

SUBDOMAINNAME.example.com
Run Code Online (Sandbox Code Playgroud)

自动指向

s3.amazonaws.com/somebucket/SUBDOMAINNAME
Run Code Online (Sandbox Code Playgroud)

要么

somedistributionname.cloudfront.net/SUBDOMAINNAME
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以在不运行服务器进行重定向的情况下完成此操作?

是否可以在不更改每个新子域的DNS记录的情况下完成此操作,如果没有,则可以通过编程方式添加DNS规则?

就资源使用而言,最有效的方法是什么。(可能有数百个子域,每个子域每天都有数百个请求)

Mic*_*bot 6

更新:这个答案在编写时是正确的,下面描述的技术仍然完全可行,但可能不太理想,因为 Lambda@Edge 现在可以用来实现这个目标,正如我在我的回答中解释的那样为大量静态站点提供服务AWS 中的通配符域


不,没有办法自动执行此操作。

有没有办法在不运行服务器进行重定向的情况下完成此操作?

从技术上讲,它不是您需要的重定向来完成此操作。您需要重写路径,这就是为什么您最终问题的答案是“不”的原因——因为 Route 53(以及一般的 DNS)无法执行与路径相关的任何操作。

Route 53 确实支持通配符 DNS,但是如果 CloudFront 和/或 S3 不支持将 HTTP 请求中的主机标头放入路径中的机制(他们不支持),那么这种帮助有限。

现在,这可以在“零接触”模式下轻松完成,只需一个 Route 53*通配符条目,为 配置一个 CloudFront 分配*.example.com,一个或多个运行 HAProxy 的 EC2 实例执行请求路径重写和代理请求S3 存储桶。基本配置文件中的一行将完成该请求重写:

http-request set-path /%[req.hdr(host)]%[path] 
Run Code Online (Sandbox Code Playgroud)

然后您需要代理将实际的存储桶端点主机名发送到 S3,而不是浏览器提供的主机名:

http-request set-header Host example-bucket.s3.amazonaws.com
Run Code Online (Sandbox Code Playgroud)

代理会将修改后的请求发送到 S3,将 S3 的响应返回给 CloudFront,后者将响应返回给浏览器。

但是,如果您不想采用这种方法,因为需要服务器,则替代解决方案如下所示:

  • 为每个子域配置 CloudFront 分配,为分配设置备用域名以匹配特定子域。

  • 为每个子域的分布配置 Origin 以指向同一个存储桶,将源路径设置为/one-specific-subdomain.example.com. CloudFront的将更改请求GET /images/funny-cat.jpg HTTP/1.1,以GET /one-specific-subdomain.example.com/images/funny-cat.jpg HTTP/1.1将请求发送到S3,导致您所描述的行为之前。(这与我为 HAProxy 描述的行为的最终结果相同,但它是静态的,而不是动态的,因此每个子域一个分布;在任何一种情况下,这都不是“重定向”——因此地址栏不会改变)。

  • 在 Route 53 中为每个子域配置一个 A 记录别名,指向子域的特定 CloudFront 分配。

这一切都可以通过 API以编程方式完成,使用任何一种 SDK,或使用aws-cli,这是一种非常简单的方法来测试、原型化和编写脚本,而无需编写太多代码。CloudFront 和 Route 53 都是完全自动化的。

请注意,使用自己的 CloudFront 分配的每个站点都没有明显的缺点,因为您的命中率不会有什么不同,而且分配没有单独的费用——只有请求和带宽费用。

另请注意,CloudFront 的默认限制为每个 AWS 账户 200 个分配,但这是一个软限制,可以通过向 AWS 支持发送请求来增加。


nac*_*oab 5

由于 Lambda@edge 这可以通过 Cloud Front“Viewer Request”事件触发的 lambda 函数来完成。

以下是此类 Lambda 函数的示例,其中类似请求将从您的源foo.example.com/index.html返回文件/foo/index.html

您将需要带有 CNAME 的 CF 发行版*.example.com,以及指向它的 A 记录“*.example.com”

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const subdomain = getSubdomain(request);
  if (subdomain) {
    request.uri = '/' + subdomain + request.uri;
  }
  callback(null, request);
};

function getSubdomain(request) {
  const hostItem = request.headers.host.find(item => item.key === 'Host');
  const reg = /(?:(.*?)\.)[^.]*\.[^.]*$/;
  const [_, subdomain] = hostItem.value.match(reg) || [];
  return subdomain;
}
Run Code Online (Sandbox Code Playgroud)

至于成本,请查看lambda 定价。当前定价为每百万个请求 0.913 美元