前言:
首先我们有一个由 Amazon S3 存储桶和 Amazon EC2 实例构成的网站,在 Amazon S3 存储桶中上传静态网站文件作为静态资源,以及使用 Amazon EC2 实例提供 API 服务作为动态内容。然后,我想创建 Amazon CloudFront 分配,添加 S3 源站的静态资源,并为静态内容配置相应的缓存行为。最终实现 Amazon CloudFront 对网站加速的效果,如下图所示。此外,也有为 Amazon CloudFront 分配添加自定义域名及 SSL 证书。
Amazon CloudFront 的网站加速效果示例
操作说明
本教程以一个动静分离的源站为例,静态资源存储于 S3(Amazon S3 是一种对象存储服务,可以存储网站静态资源,CloudFront 可以使用 Amazon S3 存储桶作为源为您分发几乎任何类型的文件),动态资源来自于 ALB 后端的 EC2。
教程将分为 3 个步骤:
首先,创建 Amazon CloudFront 分配,添加自定义源站为默认回源地址;
其次,添加 S3 为静态内容回源源站;
最后,创建域名解析,使得我们的域名通过 CloudFront 进行加速访问。
首先简单介绍下 CloudFront 的常见术语:
Distribution:分配,是 CloudFront 的基本单元,每个分配有唯一的 ID 以及CloudFront 为其分配的域名(类似 abcdefg13456789.cloudfront.net)。
Origin:源站,顾名思义,是需要被加速的站点,可以是 S3 存储桶,可以是 ELB/EC2,可以是 Elemental MediaStore/MediaPackage,或者是用户自定义的站点(如第三方 IDC 中的 HTTP Web 服务器)。一个分配中可以有多个源站。
Behaviors:行为,CloudFront 通过路径匹配和优先级决定执行哪一个缓存行为,一个分配中可以有多个行为,并且每个行为对应一个源站。在行为中可以设置缓存 TTL 时间,允许的 HTTP 行为(GET,PUT,POST 等)等等。
然后将涉及如下内容:
创建一个分配;
该分配有两个源站,其中一个是创建时添加的动态资源,源站使用自定义域名,该域名已指向 Elastic Load Balancing,主要提供 API 服务作为动态内容,另一个是后添加的静态资源,源站为 S3 存储桶;
两个行为,一个是默认行为对应回源自定义域,一个是新加的行为并对应源站 S3 存储桶;
使用 Elastic Load Balancing 创建自定义域,以及使用自定义域名解析到 Elastic Load Balancing、S3 源站创建等过程不做介绍。
第一步:创建 Amazon S3 存储桶作为静态资源源站
在这一步中将创建一个AWS S3存储桶,上传静态文件作为 AWS CloudFront的S3源站。
a. 进入 Amazon S3。点击创建存储桶按钮开始创建 S3 存储桶,在存储桶名称处为存储桶命名(存储桶名称需全局唯一),AWS 区域处选择美国东部(弗吉尼亚北部)us-east-1。其他设置保持默认,点击创建存储桶按钮完成 S3 存储桶创建。
b. 下载静态主页文件 hello-world-html.zip (该文件来自 AWS-文档-Amazon CloudFront-开发人员指南),在本地电脑解压 zip 后将所有文件上传至S3存储桶中。S3 存储桶中将包含一个 index.html 文件,一个 css 文件夹(其中包含 style.css 文件)。
第二步:创建 Amazon EC2 实例作为 Web 源站
在这个步骤中将创建一个EC2实例作为Web服务器提供API服务,它将作为AWS CloudFront的Web源站
a. 进入 Amazon EC2 管理控制台,请确保右上角 AWS 区域处为弗吉尼亚北 (us-east-1)。点击启动实例按钮。
b. 在名称和标签处,为实例命名为 cloudfront-getting-started。在应用程序和操作系统映像处,选择 Amazon Linux 2 AMI。
c. 在实例类型处,保持默认的 t2.micro 实例类型设置。在密钥对处,选择您已有的密钥对,或点击创建新密钥对以创建新的密钥,请务必下载保存该密钥。
d. 在网络设置处,保持默认的网络选项。在防火墙(安全组),保持默认的创建新安全组设置,勾选允许来自互联网的 HTTP 流量。
e. 展开高级详细信息菜单,在用户数据处复制如下代码,该代码将部署一个 Node.js 应用,会监听针对 EC2 80 端口的 HTTP 请求。当收到请求时,该应用会发回一个 JSON 格式的响应,包含请求中的 HTTP Header 信息。它也会解析查询字符串的信息,并从 Web 服务器返回查询数据。请点击启动实例按钮开始创建 EC2 实例。
#!/bin/bash
yum update -y
curl -sL https://rpm.nodesource.com/setup_14.x | sudo bash -
yum install -y nodejs
npm install pm2@latest -g
npm install express --save
cat <<'EOF' >> app.js
let express = require('express');
let app = express();
app.get('/api', (req, res) => {
console.log(JSON.stringify(req.headers));
let message = {
timestamp: new Date().toISOString(),
headers: req.headers,
};
res.json(message);
});
app.listen(80, () => {
console.log('api is up!');
});
EOF
sudo pm2 start ./app.js
sudo pm2 startup systemd
sudo pm2 save
systemctl enable --now pm2-root.service
f. 等待 EC2 实例创建成功,您可以在浏览器中访问 http://{EC2-IP}/api 或 http://{EC2-DNS-NAME}/api 确保 Web 应用正常工作,您将看到类似下图的响应。
第三步:创建 Amazon CloudFront 分配并添加 S3 源站
我们已经通过步骤1和步骤2完成S3源站和EC2源站的创建,接下来创建AWS CloudFront分配并添加步骤1中创建的S3存储桶作为S3静态资源源站,并使用来源访问控制功能(Origin Access Control,OAC)安全地通过CloudFront访问和分发私有S3存储桶中的内容。
a. 进入 Amazon CloudFront 控制台,点击创建分配按钮。
b. 在源设置中,在源域处点击输入框,选择步骤 1 中创建的 S3 存储桶。在名称处,将 S3 源命名为 s3-origin-cloudfront-getting-started。
c. 在源设置,来源访问处,选择来源访问控制设置(推荐),点击创建控制设置。
d. 保持控制设置的默认设置,点击创建按钮。您将看到来源访问控制处的配置已更新。
e. 在默认缓存行为设置处,将查看器 - 查看器协议策略勾选位 Redirect HTTP to HTTPS。
f. 在缓存键和源请求处,保持默认的 CachingOptimized 缓存策略。
g. 在 Web 应用程序防火墙(WAF)处,勾选不要启用安全保护。
h. 其余设置保持默认,在下方设置 - 默认根对象处,填入 index.html。点击创建分配按钮,完成 CloudFront 分配创建和 S3 源站的添加。
i. 您将看到 CloudFront 分配正在创建,请点击蓝色提示符的复制策略按钮,并点击转到 S3 存储桶权限以更新策略按钮,打开 S3 控制台
j. 在存储桶策略处点击编辑。
k. 在策略编辑器中粘贴刚刚复制的策略,点击保存更改按钮。该策略将保护您的存储桶在不被公开访问的情况下由 CloudFront 安全地分发存储桶内容。
注意:
我在项目中遇到使用nodejs编写的静态资源项目在S3存储桶映射到CloudFront中时,只有访问https://abcdefg13456789.cloudfront.ne/ 才正常,当需要访问/blog其他子页面或者在首页点击其他子页面再一刷新时,整个页面就报403错误了。这个问题很复杂,无法判断是代码问题要优化,还是要再CloudFront上添加函数才能实现,后面发现可以使用S3静态网站托管的url用作CloudFront的源,这个棘手的路由问题就解决了。
1.在S3上开启静态网站托管,确认S3静态网站的url
2.然后在存储桶的策略中也需要更改
测试环境中的策略可以先暂时开放权限出来
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucketname/*"
}
]
}
正式环境中的策略
{
“Version”:“2012-10-17",
“Id”:“HTTP referer policy example”,
“Statement”:[
{
“Sid”:“Allow only GET requests originating from http://www.example.com and http://example.com.“,
“Effect”:“Allow”,
“Principal”:“*”,
“Action”:[“s3:GetObject”,“s3:GetObjectVersion”],
“Resource”:“arn:aws:s3:::DOC-EXAMPLE-BUCKET/*“,
“Condition”:{
“StringLike”:{“aws:Referer”:[“http://www.example.com/*”,“http://example.com/*”]}
}
}
]
}
上面的策略是允许某个域名访问S3存储桶,如果是https的网站就改成https。
3.将CloudFront的源改为S3静态网站的url,并取消默认根对象的设置
因为使用S3静态网站时已经定义了找寻index.html文件,所以在CloudFront中就不需要再去定义了。只要关注S3静态网站的url显示的路由正常,那么CloudFront权限只要分配正常,也会正常显示网站的。
第四步:为 Amazon CloudFront 分配添加 EC2 源站
通过步骤3创建一个AWS CloudFront分配并添加S3源站
a. 进入步骤 3 中创建的 CloudFront 分配,在源标签中,点击创建源。
b. 在源域处,填入 EC2 公有 IPv4 地址,您可以通过在 EC2 控制台 - 实例详细中找到该地址,如下图所示。协议保持默认的仅 HTTP,名称处填入 ec2-origin-cloudfront-getting-started,其余设置保持默认,点击创建源。
您将看到该 CloudFront 分配除 S3 源站外已添加 EC2 源站。
c. 进入 CloudFront 分配,在行为处,点击创建行为,为 EC2 源站创建缓存行为。
d. 在路径模式中键入 /api,源和源组下拉框中选择上一步创建的 EC2 源站,查看器协议策略处勾选 Redirect HTTP to HTTPS。
e. 在缓存键和源请求处,缓存策略下拉框中选择 CachingDisabled 策略,源请求策略下拉框中选择 AllViewer 策略。其余设置保持默认,点击创建分配按钮。
f. 为了保护 EC2 源站,使 HTTP 流量仅从 CloudFront 进入 EC2,接下来您将修改 EC2 实例所关联的安全组规则。回到 EC2 控制台,选中 Web 源站 EC2 实例,选中安全标签,点击 EC2 所关联的安全组资源。
g. 进入 EC2 安全组的详情页面,点击编辑入站规则按钮。
h. 首先,删除规则类型为 HTTP,源为 0.0.0.0/0 的入站规则。随后,点击添加规则按钮,添加一条新的入站规则,规则类型为 HTTP,在源处键入 pl-,您将在选项框中看到多个 VPC 托管前缀。选中带有 com.amazonaws.global 字段且 pl - 后字符位数较短的托管前缀,点击保存规则。修改完成后的安全组规则如下图所示。
i. 在修改 EC2 安全组规则后,当您再次访问该 EC2 实例的 IPv4 DNS 地址时将发生超时,这是因为您使用 VPC 托管前缀保护 EC2 仅允许来自 CloudFront 的流量进入。接下来您将验证通过 CloudFront 加速访问 S3 静态资源和 EC2 API 服务的过程。
第五步:验证 CloudFront 分配
通过步骤1~4,我们成功创建CloudFront分配,添加S3源站(S3静态网站)和ec2源站,然后访问CloudFront分配,验证CDN分发效果。
a. 进入 CloudFront 控制台,选中刚刚创建的分配,复制分配域名至浏览器。
b. 在分配域名的首页,您将看到 Hello world 首页,即步骤 1 中上传至 S3 存储桶的 index.html。您可以打开一个浏览器的无痕窗口,再次访问该分配域名,在浏览器的开发者工具中将看到 Response Headers 中的 x-cache 状态为 Hit from cloudfront,表示静态文件已被缓存在 CloudFront 边缘站点中。
c. 访问分配域名的 /api 路径,如下图所示,您将看到与步骤 2 中访问 EC2 DNS IPv4 地址类似的输出,并展示了 CloudFront 转发和增加的其他 HTTP header。通过多次刷新页面,您将在浏览器开发者工具的 Response Headers 中看到 x-cache 始终为 Miss from cloudfront,表示对 /api 路径的请求始终不进行缓存、需要通过 CloudFront 回源至 EC2 源站,达到了动态加速效果。
第六步:为 CloudFront 分配添加自定义域名并配置 SSL 证书
CloudFront自己的ssl证书是不能用到最后用到的域名解析上的,所以这一步也是非常重要的。
评论区