

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用亚马逊通过 VPC 在 Amazon S3 存储桶中提供静态内容 CloudFront
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront"></a>

*Angel Emmanuel Hernandez Cebrian，Amazon Web Services*

## Summary
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-summary"></a>

当您提供托管在亚马逊网络服务 (AWS) 上的静态内容时，推荐的方法是使用亚马逊简单存储服务 (S3) Simple Service 存储桶作为来源，然后使用 CloudFront 亚马逊来分发内容。该解决方案有两个主要优点：便于在边缘位置缓存静态内容，以及能够为 CloudFront 分发定义 [Web 访问控制列表](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl.html) (Web ACLs)，这有助于您以最小的配置和管理开销保护对内容的请求。

然而，标准推荐方法有一个常见的架构限制。在某些环境中，您希望部署在虚拟私有云（VPC）中的虚拟防火墙设备检查所有内容，包括静态内容。标准方法不通过 VPC 路由流量进行检查。此模式提供了另一种架构解决方案。您仍然使用 CloudFront 分配在 S3 存储桶中提供静态内容，但是流量是使用 Application Load Balancer 通过 VPC 路由的。然后，AWS Lambda 函数从 S3 存储桶检索并返回内容。

## 先决条件和限制
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-prereqs"></a>

**先决条件**
+ 一个有效的 Amazon Web Services account。
+ S3 存储桶中托管的静态网站内容。

**限制**
+ 这种模式中的资源必须位于单个 AWS 区域 ，但可以在不同的 Amazon Web Services account 中进行预调配。
+ 限制分别适用于 Lambda 函数可以接收和发送的最大请求与响应大小。有关更多信息，请参阅 [Lambda 函数作为目标](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html)中的*限制*（弹性负载均衡文档）。
+ 使用此方法时，在性能、可扩展性、安全性和成本效益之间找到良好的均衡非常重要。尽管 Lambda 具有很高的可扩展性，但如果并发 Lambda 调用数量超过最大限额，某些请求会受到限制。有关更多信息，请参阅 Lambda 限额（Lambda 文档）。在使用 Lambda 时，您还需考虑定价。要最大限度地减少 Lambda 调用，请确保正确定义分配的缓存。 CloudFront 有关更多信息，请参阅[优化缓存和可用性](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ConfiguringCaching.html)（CloudFront 文档）。

## 架构
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-architecture"></a>

**目标技术堆栈**
+ CloudFront
+ Amazon Virtual Private Cloud（Amazon VPC）
+ 应用程序负载均衡器
+ Lambda
+ Amazon S3

**目标架构**

下图显示了当您需要使用 CloudFront 通过 VPC 从 S3 存储桶提供静态内容时建议的架构。

![通过 VPC 中的应用程序负载均衡器到达 Lambda 函数的流量。](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/images/pattern-img/e0dd6928-4fe0-47ab-954f-9de5563349d8/images/b42c7dd9-4a72-4998-bf88-195c8f90ed3e.png)


1. 客户端请求 CloudFront 分发 URL 以获取 S3 存储桶中的特定网站文件。

1. CloudFront 将请求发送到 AWS WAF。AWS WAF 使用 ACLs 应用于分配的 Web 来筛选请求。 CloudFront 如果确定请求有效，则流程继续。如果请求被确定为无效，客户端会收到 403 错误。

1. CloudFront 检查其内部缓存。如果存在与传入请求匹配的有效密钥，则关联的值将作为响应发送回客户端。否则，流程将继续。

1. CloudFront 将请求转发到指定的 Application Load Balancer 的 URL。

1. 应用程序负载均衡器有一个与基于 Lambda 函数目标组关联的侦听器。应用程序负载均衡器调用 Lambda 函数。

1. Lambda 函数连接至 S3 存储桶，对其执行 `GetObject` 操作，然后将内容作为响应返回。

**自动化和扩展**

要使用这种方法自动部署静态内容，请创建 CI/CD 管道来更新托管网站的 Amazon S3 存储桶。

Lambda 函数会在服务的限额和限制范围内自动扩缩以处理并发请求。有关更多信息，请参阅 [Lambda 函数扩展](https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html)和 [Lambda 限额](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html)（Lambda 文档）。对于其他 AWS 服务和功能，例如 CloudFront 和 Application Load Balancer，AWS 会自动对其进行扩展。

## 工具
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-tools"></a>
+ [Amazon](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html) 通过全球数据中心网络交付您的网页内容，从而降低延迟并提高性能，从而 CloudFront加快网络内容的分发。
+ [弹性负载均衡（ELB）](https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/what-is-load-balancing.html)将传入的应用程序或网络流量分发到多个目标。在这种模式中，您可使用通过弹性负载均衡预调配的[应用程序负载均衡器](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html)，将流量引导到 Lambda 函数。
+ [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 是一项计算服务，可帮助您运行代码，而无需预置或管理服务器。它仅在需要时运行您的代码，并且能自动扩缩，因此您只需为使用的计算时间付费。
+ [Amazon Simple Storage Service（Amazon S3）](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html)是一项基于云的对象存储服务，可帮助您存储、保护和检索任意数量的数据。
+ [Amazon Virtual Private Cloud (Amazon VPC)](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) 可帮助您将 AWS 资源启动到您定义的虚拟网络中。此虚拟网络类似于您在自己的数据中心内运行的传统网络，具有使用 AWS 可扩展基础设施的优势。

## 操作说明
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-epics"></a>

### 用于通过 VPC 提供 CloudFront 来自 Amazon S3 的静态内容
<a name="use-cloudfront-to-serve-static-content-from-amazon-s3-through-a-vpc"></a>


| Task | 说明 | 所需技能 | 
| --- | --- | --- | 
| 创建 VPC。 | 创建一个 VPC 以托管在此模式中部署的资源，例如应用程序负载均衡器和 Lambda 函数。 有关说明，请参阅[创建 VPC](https://docs.aws.amazon.com/vpc/latest/userguide/working-with-vpcs.html#Create-VPC)（Amazon VPC 文档）。 | 云架构师 | 
| 创建 AWS WAF web ACL。 | 创建 AWS WAF web ACL。稍后在此模式中，您将此 Web ACL 应用于 CloudFront 分发。有关说明，请参阅[创建 Web ACL](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-creating.html)（AWS WAF 文档）。 | 云架构师 | 
| 创建 Lambda 函数。 | 创建 Lambda 函数，将 S3 存储桶中托管的静态内容以网站形式提供。使用此模式的[其他信息](#serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-additional)部分中提供的代码。自定义代码，以识别您的目标 S3 存储桶。 | 常规 AWS | 
| 上传 Lambda 函数。 | 输入以下命令将 Lambda 函数代码上传至 Lambda 中的 .zip 文件存档中。<pre>aws lambda update-function-code \<br />--function-name  \ <br />--zip-file fileb://lambda-alb-s3-website.zip</pre> | 常规 AWS | 
| 创建应用程序负载均衡器。 | 创建指向 Lambda 函数的面向互联网的应用程序负载均衡器。有关说明，请参阅[为 Lambda 函数创建目标组](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#register-lambda-function)（弹性负载均衡文档）。要实现高可用性配置，请创建应用程序负载均衡器并将其连接至不同可用区中的私有子网。 | 云架构师 | 
| 创建分 CloudFront 配。 | 创建指向您创建的 Application Load Balancer 的分 CloudFront 配。[See the AWS documentation website for more details](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/patterns/serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront.html) | 云架构师 | 

## 相关资源
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-resources"></a>

**AWS 文档**
+ [优化缓存和可用性](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ConfiguringCaching.html)（CloudFront 文档）
+ [Lambda 函数作为目标](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html)（弹性负载均衡文档）
+ [Lambda 限额](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html)（Lambda 文档）

**Amazon Web Services 网站**
+ [应用程序负载均衡器](https://aws.amazon.com/es/elasticloadbalancing/application-load-balancer/)
+ [Lambda](https://aws.amazon.com/en/lambda/)
+ [CloudFront](https://aws.amazon.com/en/cloudfront/)
+ [Amazon S3](https://aws.amazon.com/en/s3/)
+ [AWS WAF](https://aws.amazon.com/en/waf/)
+ [Amazon VPC](https://aws.amazon.com/en/vpc/)

## 附加信息
<a name="serve-static-content-in-an-amazon-s3-bucket-through-a-vpc-by-using-amazon-cloudfront-additional"></a>

**代码**

下面的 Lambda函数示例是用 Node.js 编写而成。此 Lambda 函数充当 Web 服务器，对包含网站资源的 S3 存储桶执行 `GetObject` 操作。

```
/**

 * This is an AWS Lambda function created for demonstration purposes.

 * It retrieves static assets from a defined Amazon S3 bucket.

 * To make the content available through a URL, use an Application Load Balancer with a Lambda integration.
 * 
 * Set the S3_BUCKET environment variable in the Lambda function definition.
 */

var AWS = require('aws-sdk');

exports.handler = function(event, context, callback) {

    var bucket = process.env.S3_BUCKET;    
    var key = event.path.replace('/', '');
    
    if (key == '') {
        key = 'index.html';
    }

    // Fetch from S3
    var s3 = new AWS.S3();
    return s3.getObject({Bucket: bucket, Key: key},
       function(err, data) {

            if (err) {
                return err;
            }

            var isBase64Encoded = false;
            var encoding = 'utf8';
            
            if (data.ContentType.indexOf('image/') > -1) {
                isBase64Encoded = true;
                encoding = 'base64'
            }
    
            var resp = {
                statusCode: 200,
                headers: {
                    'Content-Type': data.ContentType,
                },
                body: new Buffer(data.Body).toString(encoding),
                isBase64Encoded: isBase64Encoded
            };

            callback(null, resp);
        }
    );
};
```