代码之家  ›  专栏  ›  技术社区  ›  levon

通配符子域指向适当的S3/CloudFront子目录

  •  9
  • levon  · 技术社区  · 8 年前

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

    因此,任何

    SUBDOMAINNAME.example.com
    

    自动指向

    s3.amazonaws.com/somebucket/SUBDOMAINNAME
    

    somedistributionname.cloudfront.net/SUBDOMAINNAME
    

    有没有一种方法可以在不运行服务器进行重定向的情况下实现这一点?

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

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

    3 回复  |  直到 8 年前
        1
  •  7
  •   Michael - sqlbot    5 年前

    更新:这个答案在编写时是正确的,下面描述的技术仍然是完全可行的,但由于Lambda@Edge正如我在回答 Serving a multitude of static sites from a wildcard domain in AWS .


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

    有没有一种方法可以在不运行服务器进行重定向的情况下实现这一点?

    从技术上讲,要做到这一点,你不需要重定向。你需要重写路径,这就是为什么你最终的问题的答案是“不”的原因——因为路由53(以及一般的DNS)不能做任何与路径相关的事情。

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

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

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

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

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

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

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

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

    • 为每个子域的分发配置Origin以指向同一bucket,设置 origin path /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有一个默认值 limit of 200 distributions per AWS account 但这是一个软限制,可以通过向AWS支持部门发送请求来增加。

        2
  •  6
  •   nachoab    6 年前

    自从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;
    }
    

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

        3
  •  -1
  •   oldtechaa    8 年前

    通配符可以在S3上使用。我只需要放一个指向IP的A记录*,就可以了。