强制刷新CloudFront分发/文件更新

156

我正在使用亚马逊的CloudFront服务来提供我的Web应用程序的静态文件。

有没有办法告诉CloudFront分配需要刷新文件或指出应该刷新的单个文件?

亚马逊建议您为文件命名版本,例如logo_1.gif,logo_2.gif等,作为解决此问题的变通方法,但这似乎是一个相当愚蠢的解决方案。难道绝对没有其他方法吗?


1
可能是如何在亚马逊CDN(CloudFront)上更新文件?的重复问题。 - Steffen Opel
作为旁注,我认为这样命名静态文件并不愚蠢。我们一直在使用它,并且根据版本控制中的文件版本进行自动重命名已经为我们节省了很多麻烦。 - eis
1
除非你需要替换的文件已经在1000个不同的在线位置链接,否则@eis是无法实现的。祝你好运更新所有这些链接。 - Jake Wilson
@Jakobud,为什么在这种情况下链接应该被更新?它们指向的是特定版本,而不是最新版本,如果文件已经更改。如果文件没有更改,它将像以前一样工作。 - eis
6
有时候,一家公司会在发布某个东西的图片或其他类型的内容时犯错,导致他们收到了律师事务所的下架通知并需要更换文件。仅仅上传一个新文件并改名并不能解决这种问题,而这种问题越来越普遍了。 - Jake Wilson
我已经在@SteffenOpel提到的重复问题的答案中总结了可能的解决方案,网址为https://dev59.com/z3NA5IYBdhLWcg3wHp-B#66976601。 - Aidin
13个回答

140

好消息。Amazon终于添加了失效功能。详见API参考

这是API参考中的一个示例请求:

POST /2010-08-01/distribution/[distribution ID]/invalidation HTTP/1.0
Host: cloudfront.amazonaws.com
Authorization: [AWS authentication string]
Content-Type: text/xml

<InvalidationBatch>
   <Path>/image1.jpg</Path>
   <Path>/image2.jpg</Path>
   <Path>/videos/movie.flv</Path>
   <CallerReference>my-batch</CallerReference>
</InvalidationBatch>

9
请注意,无效化需要一些时间(根据我阅读的一些博客文章,大约需要5-30分钟)。 - Michael Warkentin
39
如果您不想亲自发出 API 请求,也可以登录 Amazon 控制台并在那里创建一个无效请求: http://docs.amazonwebservices.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html - j0nes
23
请记住,每月前1,000个作废请求之后,每个文件的费用为$0.005。详情请参阅https://aws.amazon.com/cloudfront/pricing/。 - TimS
1
在进行API的createInvalidation请求后,我仍然看到更新需要5-10分钟左右才能失效。注意,我是在您评论4年之后写下这条评论的。 - tim peterson
4
截至2020年,每个路径的费用为0.005美元,不再是每个文件。因此,如果您使路径无效,例如 /* - 所有文件 - 您只需支付一次失效费用,无论文件/ URL 的数量如何。此外,AWS每月提供1000个路径的免费失效请求。请参见文档中的失效请求部分。 - Wesley Gonçalves
显示剩余6条评论

20
截至3月19日,亚马逊现在允许Cloudfront的缓存TTL为0秒,因此理论上您永远不应该看到旧对象。因此,如果您的资产在S3中,则可以简单地转到AWS Web Panel => S3 => Edit Properties => Metadata,然后将“Cache-Control”值设置为“max-age = 0”。
这是直接从API文档中摘取的:

为了控制CloudFront是否缓存对象以及缓存时间,我们建议使用Cache-Control头并使用max-age指令。 CloudFront会将对象缓存指定的秒数。(最小值为0秒。)


新版的AWS控制台界面中这个设置在哪儿?我找不到它。 - ill_always_be_a_warriors
1
我找到了单个文件的设置,但是否有一种设置可以使上传到我的存储桶的任何内容具有0的TTL? - ill_always_be_a_warriors
虽然我肯定也对整个存储桶的设置感兴趣,但我发现这是一个更快/更好的解决方案。无效请求(以及API的其余部分)非常令人困惑和文档不完善,我在这之前曾经白费了3个小时的时间,而现在这个方法立即就奏效了。 - Two-Bit Alchemist
41
称呼我疯子也好,但将TTL设置为0和max-age设置为0实际上是在不使用CloudFront缓存的情况下使用它,这样会把所有请求转发到源服务器并不断检查更新,这本质上使CDN无用。 - acidjazz
8
如果你只是将CloudFront作为一种机制来具有带有自定义域名的静态SSL启用的S3站点,那么缓存并不重要。此外,我们正在讨论的问题是在开发阶段,零时间缓存是有益的。 - Dan G
@ill_always_be_a_warriors 不,你必须手动完成所有文件的操作。我已经设置了,但CloudFront没有刷新我的内容。我想我会放弃了,对于一个小型静态网站来说,CloudFront似乎有些过度,我将默认使用Cloudflare的Flexible SSL。 - Paul Razvan Berg

10

Bucket Explorer 现在有一个用户界面,使这个过程相当容易。以下是操作步骤:

右键单击您的bucket。选择"管理分发"。
右键单击您的分发。选择"获取Cloudfront使无效列表" 然后选择"创建"来创建新的使无效列表。 选择要使无效的文件,然后点击"使无效"。等待5-15分钟。


10

这正是我一直在寻找的。当从Git自动部署时,我将把它挂钩到Beanstalkapp的Web-hooks中!感谢提供链接! - cointilt

10

5分钟自动更新设置

大家好。目前执行CloudFront自动更新(失效)最好的方式是创建Lambda函数,每次上传文件到S3存储桶时触发(新文件或覆盖文件均可)。

即使您以前从未使用过lambda函数,也很容易 - 只需按照我的一步一步说明操作,只需花费5分钟:

第1步

转到https://console.aws.amazon.com/lambda/home,然后单击创建Lambda函数

第2步

单击空白函数(自定义)

第3步

单击空白(虚线)方框,并从组合中选择S3

第4步

选择您的 Bucket (与CloudFront分配相同)

第5步

事件类型设置为“对象创建(全部)”

第6步

设置前缀和后缀,如果您不知道它是什么,则将其留空。

第7步

选中启用触发器复选框,然后单击下一步

第8步

为您的函数命名(类似于:YourBucketNameS3ToCloudFrontOnCreateAll

第9步

运行时设置为Python 2.7(或更高版本)

第10步

粘贴以下代码替换默认的Python代码:

from __future__ import print_function

import boto3
import time

def lambda_handler(event, context):
    for items in event["Records"]:
        path = "/" + items["s3"]["object"]["key"]
        print(path)
        client = boto3.client('cloudfront')
        invalidation = client.create_invalidation(DistributionId='_YOUR_DISTRIBUTION_ID_',
            InvalidationBatch={
            'Paths': {
            'Quantity': 1,
            'Items': [path]
            },
            'CallerReference': str(time.time())
            })

步骤 11

在新的浏览器选项卡中打开https://console.aws.amazon.com/cloudfront/home,并复制您的 CloudFront 发布 ID 以供下一步使用。

步骤 12

返回 lambda 选项卡,在 Python 代码中将您的分发 ID 粘贴到 _YOUR_DISTRIBUTION_ID_ 的位置。保留引号。

步骤 13

设置 handler:lambda_function.lambda_handler

步骤 14

点击 role 下拉框,选择 Create a custom role。浏览器会打开一个新标签页。

步骤 15

点击 view policy document,点击 edit,点击 OK 并用以下内容替换角色定义(不要更改标签):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
          "cloudfront:CreateInvalidation"
      ],
      "Resource": [
          "*"
      ]
    }
  ]
}

步骤16

点击允许。这将返回一个Lambda。请仔细检查你刚刚创建的角色名是否在现有角色组合框中被选中。

步骤17

内存(MB)设置为128,超时设置为5秒。

步骤18

点击下一步,然后点击创建函数

步骤19

你已经准备好了!从现在开始,每次你上传/重新上传任何文件到S3时,它都将在所有CloudFront边缘位置进行评估。

附注 - 在测试时,请确保你的浏览器从CloudFront加载图像,而不是从本地缓存中加载。

附注2 - 请注意,每月仅有前1000个文件无效化是免费的,超过限制的每个无效化费用为0.005美元。此外,对于Lambda函数可能会有额外的收费,但价格非常便宜。


每个 S3 批次只需要最后一项? - Phil
@Phil 代码是这样编写的,只有新上传的文件才会被使失效,而不是整个存储桶。如果是多文件上传,则每个文件都将单独失效。非常有效。 - Kainax
这段代码之所以按预期工作,仅因为S3当前每个通知只包含一个项目,即数组的长度始终为1,因此,即使您一次上传多个文件,您也会获得每个文件的全新通知。在任何情况下,您都不会收到整个存储桶的通知。尽管如此,如果AWS更改了该行为,按原样编写的代码将无法使用。最好编写处理整个数组的代码,而不考虑长度,这是我的原始(可悲地错过的)观点。 - Phil
AWS添加事件处理程序的唯一原因是...嗯...处理事件。为什么要删除它?无论如何,只要添加了新文件,它都应该触发API事件,这就是现在的工作方式,也将继续工作。我使用AWS已经4年了,他们从未更改过某些东西,以至于之前的代码停止工作。即使他们更改API,他们也会更改一个新的独立版本,但所有先前的版本始终得到支持。在这种特殊情况下,我不相信个人文件事件会被删除。它可能已经被全球数百万项目使用。 - Kainax
如果我误解了您的第一个评论,并且您的意思是 'Quantity': 1 只会添加最后一个项目——那么在数组中的每个项目都有一个 FOR 循环。 - Kainax
PUT事件的S3通知包括一个Record元素的数组 - 数组长度可变,因此要处理它们,需要使用循环,就像您所做的那样。但是 - 您的代码依赖于数组恰好有1个元素 - 这相当于简单地说path = "/" + event["Records"][0]["s3"]["object"]["key"](严格来说,您将取最后一个项目,而不是第一个,但对于长度为1的数组,目前(但不必)情况下,这相当于同一件事情。另外,我从未提到或建议AWS删除任何通知。 - Phil

5
如果您安装了boto(它不仅适用于Python,还安装了一堆有用的命令行工具),它会提供一个名为cfadmin或“云前端管理”的命令行工具,提供以下功能:
Usage: cfadmin [command]
cmd - Print help message, optionally about a specific function
help - Print help message, optionally about a specific function
invalidate - Create a cloudfront invalidation request
ls - List all distributions and streaming distributions

运行以下代码可以使事物无效:

$sam# cfadmin invalidate <distribution> <path>

实际上,cfadmin是一个非常有用的工具,特别是当您需要从控制台\bash\travis ci部署脚本重置CloudFront缓存时。顺便说一下,这里是一篇文章,介绍如何在travis部署到aws期间重置\使无效CoudFront缓存:http://www.mikitamanko.com/blog/2014/10/26/travis-invalidate-aws-cloudfront-cache/ - Mikita Manko

3
在ruby中,使用fog gem。
AWS_ACCESS_KEY = ENV['AWS_ACCESS_KEY_ID']
AWS_SECRET_KEY = ENV['AWS_SECRET_ACCESS_KEY']
AWS_DISTRIBUTION_ID = ENV['AWS_DISTRIBUTION_ID']

conn = Fog::CDN.new(
    :provider => 'AWS',
    :aws_access_key_id => AWS_ACCESS_KEY,
    :aws_secret_access_key => AWS_SECRET_KEY
)

images = ['/path/to/image1.jpg', '/path/to/another/image2.jpg']

conn.post_invalidation AWS_DISTRIBUTION_ID, images

即使进行了失效操作,所有亚马逊边缘服务器上的刷新仍需花费5-10分钟才能完成。

3

当前AWS CLI支持预览模式下的失效。请在控制台中运行以下命令:

aws configure set preview.cloudfront true

我使用npm来部署我的Web项目。在我的package.json文件中,我有以下脚本:

{
    "build.prod": "ng build --prod --aot",
    "aws.deploy": "aws s3 sync dist/ s3://www.mywebsite.com --delete --region us-east-1",
    "aws.invalidate": "aws cloudfront create-invalidation --distribution-id [MY_DISTRIBUTION_ID] --paths /*",
    "deploy": "npm run build.prod && npm run aws.deploy && npm run aws.invalidate"
}

使用上述脚本,您可以通过以下方式部署您的网站:

npm run deploy

1
我认为你需要在“aws.invalidate”命令中加上星号,将“--paths /”更改为“--paths /*”。我的情况与你的类似,但是没有使分发失效... - Herald Smit

3
一种非常简单的做法是使用文件夹版本控制。如果您的静态文件有几百个,只需将它们全部放入一个名为年份+版本号的文件夹中。例如,我使用一个名为2014_v1的文件夹,在其中放置了所有我的静态文件...因此,在我的HTML中,我总是将引用放到文件夹中。(当然,我有一个PHP包含文件,在其中设置了文件夹的名称)。因此,通过更改一个文件,实际上会更改所有PHP文件中的内容。如果我想要完全刷新,只需将文件夹重命名为2014_v2并放入我的源文件夹中,并在PHP包含文件中更改为2014_v2。所有HTML自动更改并请求新路径,CloudFront MISS缓存并请求源。例如:source.mydomain.com是我的源,cloudfront.mydomain.com是CloudFront分发的CNAME。因此,PHP调用了这个文件cloudfront.mydomain.com/2014_v1/javascript.js,当我想要完全刷新时,只需将文件夹重命名为“2014_v2”,并通过设置文件夹为“2014_v2”来更改PHP包含文件。这样就不需要延迟无效和不需要费用!这是我在stackoverflow上的第一篇文章,希望我做得好!

3

前往 CloudFront。

点击您的 ID/Distributions。

点击 Invalidations。

点击创建 Invalidation。

在示例框中输入 * 并点击 Invalidate。

完成。

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接