【问题标题】:Wildcard subdomains point to appropriate S3/CloudFront subdirectories通配符子域指向适当的 S3/CloudFront 子目录
【发布时间】:2016-10-04 17:26:35
【问题描述】:

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

所以任何

SUBDOMAINNAME.example.com

自动指向

s3.amazonaws.com/somebucket/SUBDOMAINNAME

somedistributionname.cloudfront.net/SUBDOMAINNAME

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

是否可以在不更改每个新子域的 DNS 记录的情况下完成,或者如果不能,则以编程方式添加 DNS 规则?

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

【问题讨论】:

    标签: amazon-s3 dns amazon-cloudfront amazon-route53 wildcard-subdomain


    【解决方案1】:

    更新:这个答案在编写时是正确的,下面描述的技术仍然是完全可行的,但可能不太理想,因为现在可以使用 Lambda@Edge 来实现这个目标,正如我在对 @987654321 的回答中所解释的那样@.


    不,无法自动执行此操作。

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

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

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

    现在,这可以通过单个 Route 53 * 通配符条目、为 *.example.com 配置的单个 CloudFront 分配以及一个或多个运行 HAProxy 的 EC2 实例轻松实现请求路径重写并将请求转发到 S3 存储桶。基本配置文件中的一行就可以完成请求重写:

    http-request set-path /%[req.hdr(host)]%[path] 
    

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

    http-request set-header Host example-bucket.s3.amazonaws.com
    

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

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

    • 为每个子域配置 CloudFront 分配,为分配设置 alternate domain name 以匹配特定子域。

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

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

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

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

    另请注意,CloudFront 有一个默认的 limit of 200 distributions per AWS account,但这是一个软限制,可以通过向 AWS 支持发送请求来增加。

    【讨论】:

    • 感谢您的详细解答!我可能会使用您的第一个“零接触”方法,因为创建单独的 CF 分布似乎效率低下。那么,使用带有路径重写的 HAProxy 的服务器不会增加任何可观的资源开销或响应延迟,不是吗?
    • 另外,如果我的 CloudFront 分配源设置为 S3 存储桶,我是否仍需要 HAProxy 按照您的建议将请求发送到 S3,然后再发送到 CloudFront?我不能直接将其代理到 CloudFront 分配吗?
    • CloudFront 将请求发送到 HAProxy,这就是您在 CloudFront 中设置的源服务器。然后代理在修改请求后将请求发送到 S3。只要代理与 S3 位于同一区域,额外的延迟就会非常小。您不希望在 CloudFront 之前使用代理,因为这会导致所有请求都使用离代理最近的 CloudFront 边缘,而不是使用离发出请求的浏览器最近的边缘,从而破坏 CloudFront 缓存离用户最近的内容的优势.
    • 还需要将 CloudFront 缓存行为配置为将 Host 标头列入白名单以转发到代理,否则代理将不知道最初由浏览器发送到 CloudFront 的主机名。
    • @Clintm 您的评论就足够了。没有必要投反对票。已接受的答案无法删除或向下移动,但我已进行了编辑。
    【解决方案2】:

    通配符适用于 S3。我只是放了一个指向 IP 的 A 记录 * 并且它起作用了。

    【讨论】:

    • 我建议进行编辑。以后请尽量使用正确的大小写和语法。
    • 不回答问题
    【解决方案3】:

    由于 Lambda@edge,这可以通过 Cloud Front“查看器请求”事件触发的 lambda 函数来完成。

    这是一个此类 Lambda 函数的示例,其中像 foo.example.com/index.html 这样的请求将从您的源返回文件 /foo/index.html

    您将需要 CNAME *.example.com 的 CF 分发,以及指向它的 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;
    }
    

    至于费用请看lambda pricing。目前的定价是每百万个请求 0.913 美元

    【讨论】:

      猜你喜欢
      • 2021-12-06
      • 2016-04-25
      • 1970-01-01
      • 2014-01-28
      • 1970-01-01
      • 1970-01-01
      • 2010-10-17
      • 2015-08-26
      • 2019-04-29
      相关资源
      最近更新 更多