

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 Amazon CloudFront 在 Amazon S3 儲存貯體中透過 VPC 提供靜態內容
<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*

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

當您提供託管在 Amazon Web Services (AWS) 上的靜態內容時，建議方法是使用 Amazon Simple Storage Service (S3) 儲存貯體做為原始伺服器，並使用 Amazon 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>

**先決條件**
+ 作用中的 AWS 帳戶
+ S3 儲存貯體中託管的靜態網站內容。

**限制**
+ 此模式中的資源必須位於單一 AWS 區域，但可以在不同的 AWS 帳戶中佈建。
+ 限制適用於 Lambda 函數可分別接收和傳送的最大請求和回應大小。如需詳細資訊，請參閱 Lambda 函數中的*限制*做為目標 (Elastic Load Balancing 文件）。 [https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html](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 文件）。

## Architecture
<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)
+ Application Load Balancer
+ Lambda
+ Amazon S3

**目標架構**

當您需要使用 CloudFront 透過 VPC 從 S3 儲存貯體提供靜態內容時，下圖會顯示建議的架構。

![流量流經 VPC 中的 Application Load Balancer 至 Lambda 函數。](http://docs.aws.amazon.com/zh_tw/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 會使用套用至 CloudFront 分佈的 Web ACLs 來篩選請求。如果請求被判定為有效，流程會繼續。如果請求被判定為無效，用戶端會收到 403 錯誤。

1. CloudFront 會檢查其內部快取。如果有符合傳入請求的有效金鑰，則會將關聯的值傳回給用戶端做為回應。如果沒有，流程會繼續。

1. CloudFront 會將請求轉送至指定 Application Load Balancer 的 URL。

1. Application Load Balancer 具有以 Lambda 函數為基礎的與目標群組相關聯的接聽程式。Application Load Balancer 會叫用 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 CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html) 透過全球資料中心網路提供 Web 內容，從而降低延遲並改善效能，從而加快 Web 內容的發佈速度。
+ [Elastic Load Balancing (ELB)](https://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/what-is-load-balancing.html) 會將傳入的應用程式或網路流量分配到多個目標。在此模式中，您可以使用透過 Elastic Load Balancing 佈建的 [Application Load Balancer](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>

### 使用 CloudFront 透過 VPC 從 Amazon S3 提供靜態內容
<a name="use-cloudfront-to-serve-static-content-from-amazon-s3-through-a-vpc"></a>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| 建立 VPC。 | 建立 VPC 以託管在此模式中部署的資源，例如 Application Load Balancer 和 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 | 
| 建立 Application Load Balancer。 | 建立指向 Lambda 函數的面向網際網路的 Application Load Balancer。如需說明，請參閱[建立 Lambda 函數的目標群組](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#register-lambda-function) (Elastic Load Balancing 文件）。如需高可用性組態，請建立 Application Load Balancer，並將其連接至不同可用區域中的私有子網路。 | 雲端架構師 | 
| 建立 CloudFront 分佈。 | 建立指向您建立之 Application Load Balancer 的 CloudFront 分佈。[See the AWS documentation website for more details](http://docs.aws.amazon.com/zh_tw/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) (Elastic Load Balancing 文件）
+ [Lambda 配額](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html) (Lambda 文件）

**AWS 服務網站**
+ [Application Load Balancer](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>

**Code**

下列範例 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);
        }
    );
};
```