Aws-cli: AWS S3 同步问题

创建于 2014-01-17  ·  41评论  ·  资料来源: aws/aws-cli

sync命令存在一些问题,特别是在从 S3 ( s3 -> local ) 同步的情况下。 我想尝试总结已知问题以及一些可能选项的建议,并让人们有机会分享他们可能有的任何反馈。

同步行为概述

同步行为旨在成为高效的cp ; 仅将不同的文件从源复制到目标。 为此,我们需要能够确定 s3/local 中的文件是否不同。 为此,我们使用两个值:

  • 文件大小(来自stat本地文件和ListObjects响应中的Size键)
  • 上次修改时间(本地文件的 mtime 和ListObjects响应中的LastModified键)

顺便说一句,我们使用ListObjects操作是因为我们在一次调用中最多可以返回 1000 个对象。 这意味着我们仅限于从ListObjects响应返回的信息,即LastModified, ETag, StorageClass, Key, Owner, Size

现在给定远程和本地文件的文件大小和上次修改时间,我们尝试确定文件是否不同。 文件大小很容易,如果文件大小不同,那么我们知道文件不同,我们需要同步文件。 然而,最后修改时间更有趣。 虽然本地文件的修改时间是一个真正的mtime,在LastModified不时ListObjects是真正的对象上传的时间。 所以想象一下这个场景:

aws s3 sync local/ s3://bucket/
sleep 10
aws s3 sync s3://bucket local/

在第一个同步命令 ( local->s3 ) 之后,本地文件的 mtime 为 0,s3 中的内容的 LastModified 时间为 10(使用相对偏移量)。 当我们运行第二个 aws s3 同步命令(从 s3 同步到本地)时,我们将首先进行文件大小检查。 在这种情况下,文件大小相同,因此我们查看上次修改时间检查。 在这种情况下,它们是不同的(local == 0, s3 == 10)。 如果我们进行严格的相等比较,由于上次修改时间不同,我们将不必要地将文件从 s3 同步到本地。 所以我们可以说,如果文件大小相同并且 s3 中的最后修改时间大于(更新)本地文件,那么我们不同步。 这是当前的行为。

但是,如果远程文件是带外更新的(通过控制台或某些其他 SDK)并且大小保持不变,则会产生问题。 如果我们运行aws s3 sync s3://bucket local/我们不会同步远程文件,即使我们假设这样做。

潜在的解决方案

以下是可能的解决方案。

  1. 将时间检查更改为严格的相等比较。 如果时间不同,我们会同步。 这存在aws s3 sync local s3://bucket && aws s3 s3://bucket local会不必要地同步文件的问题。 但是,当我们下载文件时,我们将文件的 mtime 设置为与 LastModified 时间匹配,因此如果您要运行aws s3 sync s3://bucket local _again_,它不会同步任何文件。
  2. 文件上传时修改本地 mtime 以匹配 S3 的 Last Modified 时间。 然后我们将文件时间检查更改为严格的等于/不等于检查。 这种方法的唯一缺点是人们是否会期望我们弄乱他们文件的 mtime(我不希望)。
  3. 向每个对象添加自定义元数据。 这可能会添加一些本地 mtime、md5 的组合,因此我们还可以根据需要比较 md5 校验和。 但是,对于每个请求,我们都必须从 ListObjects 切换到 HeadObject。 这将需要多 1000 倍的 API 调用,并且在许多小文件的情况下会减慢同步速度。 在稳定状态下,只要我们可以更快地下载/上传 HeadObjects,就不会有问题。 这种方法的一个缺点是其他工具不会添加此元数据,这会使与其他工具的互操作更加困难(如果对象缺少元数据,我们可能只会同步)。
  4. 保留本地 ETag 缓存。 我们可以将文件名与文件的 ETag/本地 mtime/上次修改时间/md5 相关联。 从好的方面来说,我们仍然使用 ListObjects,因此我们一次可以返回 1000 个对象。 缺点是每个客户端都必须保留一个缓存。 这也是实现上最复杂的解决方案。

如果我遗漏了任何其他潜在的解决方案,请加入。

automation-exempt feature-request s3 s3md5 s3sync

最有用的评论

颠倒这个备份!

所有41条评论

而不是存储服务器提供的 ETag 的缓存,ETag 可以在客户端计算吗? 然后它几乎就像做 md5 检查一样,但使用 ListObjects 响应中可用的数据。 只要 ETag 算法不依赖服务器端状态……

啊: https : = 203510 =hashArgs%23203510

你找到了:-)

——塞伯

2014 年 1 月 17 日 16:26,Jeff Waugh < [email protected] [email protected] > 写道:

啊: https : = 203510 =hashArgs%23203510


直接回复本邮件或在 Gi tHub上查看

Amazon EU Societe a responsabilite limitee,5 Rue Plaetis, L - 2338 Luxembourg,RCS Luxembourg n B 101818,资本社会:37.500 欧元。 Autorisation d establissement en qualite de commercante n 104408。

是的,我们无法可靠地计算分段上传的 ETag,否则这将是一个很好的解决方案。

您能否为时间行为添加一个新标志(或 2 个)? 也许--check-timestamps用于选项 1,而--update-local-timestamps用于选项 2。这样用户可以指定更强大的更改检查并同时接受结果。

是的,我认为为选项 1 和 2 添加标志是一种合理的方法。 一个潜在的问题是,默认(未指定选项)行为会出现sync行为不符合预期的情况,但我不确定将默认行为更改为这些选项中的任何一个是否是一件好事在这里,考虑到我们将要进行的潜在权衡。

@jamesls我正在使用同步命令来部署生成的静态网站。

使用当前版本,我每次同步都会重新上传所有文件,因为重新生成站点时 mtime 会发生变化,即使内容没有。

出于我的目的(并且我想象有很多其他人使用这个很棒的工具来上传他们的静态网站)按照 #575 中的建议通过 ETag 同步将是最棒的,但考虑到我对这个问题的阅读,它似乎不是一个选项。

除非,为了静态站点的目的,仅长度检查(尽管可能有点危险)是可行的。

另一种选择是我们禁用分段上传并使用 #575 - 我们会立即看到巨大的节省。

我发现了相反的问题。 我在 S3 中更改了一个文件,该文件具有相同的大小,但较新的时间戳和 s3 同步不会将其拉下来

aws s3 同步 s3://bucket/path/ dir

查看 S3 中的数据......我认为这是因为时区问题。
属性显示时间

最后修改时间:2/21/2014 10:50:33 AM

但是 HTTP 标头显示

最后修改时间:2014 年 2 月 21 日星期五 15:50:33 GMT

请注意,Last Modified 属性不显示时区?

由于我的 s3 同步命令在与我放置文件的位置不同的时区的不同服务器上运行,因此它认为该文件是过去的并且不拉它。

我不得不更改为 s3 cp 以确保它获取所有文件

我认为作为第一步,我们应该实现--size-only参数。 它不能解决一般情况下的问题,但对于某些情况,这会有所帮助,并且很容易理解/解释,尤其是上面引用的将静态站点同步到 s3 的用例。

我认为,如果要同步的文件比目标新,同步应该有一个选项来始终同步文件。 我们正在将文件从机器 A 同步到 S3,然后从 S3 同步到机器 B。如果文件的大小没有改变(但内容改变),这个文件将不会到达机器 B。这种行为被破坏了。 如果我同步到很多文件,我不介意,但不应遗漏更改的文件。

根据我之前的帖子,“较新”也需要考虑时区。
目前情况并非如此,如果您将文件从一个时区推送到 S3,然后从另一个时区同步,则无法正确检测到该文件是否较新。

@jamesls除了 --size-only 参数之外,我对使用 --name-only 参数很感兴趣。 也就是说,不要检查文件大小或上次修改时间。 只需复制源中存在但目标中不存在的文件。 在我们的场景中,我们从 s3 同步到本地,一旦我们下载了一个文件,我们不希望它在 s3 上发生变化。 如果此选项导致对本地 (nfs) 文件系统的操作较少,则可能会提高性能。

@jamesls --size-only等应该在 1.3.6 中可用吗?

我的案例 186692581 的 AWS Support 代表说他将我的建议转发给了您。
我想无论如何我都会在这里发表评论:

我认为一个简单的解决方案是引入一个模糊因素。
如果本地 -> S3 副本通常不会超过 5 分钟,
然后在随后的时间比较中使用 10 分钟的模糊因子。
将 10 分钟内的相对时间视为相等。
如果 S3 时间更新超过 10 分钟,则从 S3 -> 本地同步。
也许添加“--fuzz=10m”作为选项。

@jamesls @adamsb6
至少对于单部分上传的文件来说, https://github.com/aws/aws-cli/pull/575不是一个不错的选择吗?

如果您在 S3 上检查文件的 ETAG 格式,您可以区分文件是作为单个(“ETAG =“MD5 哈希”)还是多部分(ETAG =“MD5 哈希”-“部分数”)上传。所以你可以将所有本地 MD5 文件与其 ETAG 进行比较,如果文件作为多部分上传,您可以跳过它。

我们有一个客户在 S3 存储桶的某些文件夹中拥有大量视频剪辑,这些视频剪辑已同步到所有 AWS 区域的 ec2 实例。 所有文件都作为一个部分上传。
目前我们遇到了由于 s3cmd 的问题,即在某些情况下某些文件已损坏。 如果我们再次进行完全同步,我们将收取 14 TB 流量。

我们的问题:损坏的文件与 s3 上的原始文件大小完全相同,由于 s3cmd 的时间戳错误,我们无法使用上述选项。 在这种情况下, --compare-on-etag将是防止再次同步所有文件的绝佳解决方案。

即使对于正常同步, --compare-on-etag选项也会很棒,如果您只有单部分上传的文件,因为 aws s3 同步只会同步更改的文件。

我刚刚花了 3 个小时的大部分时间试图找到使用同步命令所需的最低权限。 我得到的错误是:
A client error (AccessDenied) occurred when calling the ListObjects operation: Access Denied

当真正的错误应该是:
A client error (AccessDenied) occurred when calling the ListBucket operation: Access Denied

显示每个命令具有最低权限的表的帮助项将非常有帮助。

404 似乎是一个_非常好的主意_,看到这一点,直到我读到我认为同步已经做到了这一点。

编辑:为了澄清,将类似 rsync 的行为添加到“aws s3 同步”。 似乎所报告的那个问题与我最初理解的不太一样。

由于最新的 AWS-CLI-bundle.zip 不包含上面实现的修复,我做了一个 git clone。 我可以在名为“customizations”的文件夹中看到新代码。 但是,我不清楚如何使用此代码创建 aws-cli 命令。 我必须运行 make-bundle 吗?

是的。 我使用以下步骤将其安装到新服务器 (Ubuntu) 上:

git clone https://github.com/andrew512/aws-cli.git
cd aws-cli
pip install -r requirements.txt
sudo python setup.py install

好的。
我在 1.3.18 版中看到了修改后的代码。
它接受我的 --exact-timestamps 参数。
我以为我之前安装的最新下载包是 1.3.21。

可靠的版本控制实际上只适用于正式的 AWS 版本。 我在 1.3.18 分叉了 repo,所以这就是它将报告的版本,但它已经有几个版本已经过时了,1.3.22 是目前最新的版本。 希望 AWS 接受拉取请求并在未来的官方版本中包含该功能。 它对我们非常有价值,有助于解决相当有问题的默认行为。

@andrew512抱歉耽搁了。 我认为您发送的 PR 是一个好主意,让客户反馈有关aws s3 sync哪些更改对他们有效,哪些无效的反馈非常有帮助。 我一会儿去看看。

我认为......对于我们这些不介意标头请求的人来说,MD5 上的比较应该是一种选择。 我会(其次)投票给 --compare-on-etag,因为我只从一台服务器更新到 S3——因此本地 MD5 存储库对我来说不是问题。 但我绝对认为我们需要一些东西。 事实上,我从来不确定我的本地和 S3 存储库是相同的。 像这样的情况我们在哪里?

@janngobble +1

我们的用例是我们在 git repo 中拥有这些文件,它们是配置文件,因此修改日期和文件大小都不起作用,因此我们希望看到一个实际的 md5 选项,用于那些可以处理性能影响的选项。

这是因为当您检出 git repo 时,文件修改日期是写入文件的时间。 文件大小也不起作用,因为文件更改可能类似于:

foo="bar"

foo="baz"

所以文件不会改变大小。

@jamesls为什么不能使用这里的方法来计算分段上传的 md5? 它对我有用。

你好,
我也用 foo="bar"/foo="baz" 很好地描述了这个问题。
我使用 S3 存储桶进行应用程序部署,部署完成后所有服务器都从 S3 同步。 我有几次操作符的问题 >= 更改为 <= 由于此错误而未同步的文件中,并且由于此错误,同步命令对我来说不是很可靠。 大小相同但内容不同,文件应该同步。
我对如何做没有特别的建议,抱歉,但我只是暴露我的用例:)

想想看,我在开发 node-ftpsync 时遇到了同样的问题。 我认为 AWS 会有一些神奇的解决方案来解决这个问题。

  1. 糟透了,但这是必要的。 mtime.equals 比较是检测变化的最快/最脏的方法。 不幸的是,在比较本地和远程主机之间的时钟时间之前需要对其进行标准化。

@ngbranitsky建议的那样“模糊”它(即四舍五入到最接近的 10 分钟)可能是个好主意。 在 node.js 中这样做很痛苦,但在 python 中它应该像使用按位 AND 截断最后几位一样简单。

  1. 可能有效,但有副作用。 我还可能通过更改本地 mtime 以匹配服务器的 MDTM(即 FTP mtime)值来探索此选项。 不幸的是,FTP 服务器实现很少符合 RFC,并且可能不包括 MDTM。 举个例子,通过 FTP 同步文件很糟糕,因为不能保证客户端和服务器都是完整的。

由于 AWS 没有这个问题,您还必须考虑如何在本地主机上使用 mtime。 通过在每次同步时更改 mtime 值,如果有转译器监视这些文件,您是否会触发大规模更新事件? 是否有其他元数据存储使用 mtime 作为衡量文件更改的指标?

  1. 如果您不考虑成本,这是最好的选择。 额外的 API 调用会给用户带来成本(即货币成本)。 MD5 仅用于文件添加和更改的文件,但对于存储大量小文件和/或经常更改的文件的用户,它可以累加。
  2. 到目前为止,这是确保一致性的最佳选择,但也是实施时最大的痛苦。

我意识到这不是一个通用的解决方案,但是看到以下用于同步的可选行为 imo 会非常好。 这有点像没有。 4 在 OP 中,只为了更好的性能而修改。

  1. 为本地文件系统保留 etags 的本地缓存,如果用户不能保证对存储桶中的文件进行带外更改,则可以选择服务器。
  2. 在同步本地-> 服务器时,使用 mtime/size 与缓存数据库进行比较的第一遍用于确定本地文件中的潜在更改。 (这样做是为了避免不必要地重新计算相对于 fstat 昂贵的 md5。)
  3. 对于可能更改的集合,重新计算 md5->etag。 如果文件实际上不同,则将其上传。 在任何情况下都会更新本地缓存数据库,以防止在本地文件上重新计算 md5。

这里的用例是同步 web 地图图块,其中 <1% 通常每天更改。 例外情况是在源数据中发生的删除操作会更改切片的空间覆盖范围,这需要重新生成整个地图切片集。

问题在于所涉及的数量庞大。 我看到关于大型分段上传的讨论是一个用例,但不是关于许多单独的小文件。 多少? 我们目前有大约 200 万,但情况可能会变得更糟。 例如,在缩放级别 16 时,世界有 1 << 16 x 1 << 16,或 65536 x 65536 图块,或 ~4b。

目前的选择是:

  1. 正常与 aws-cli 同步,重新上传所有内容。

    1. 这是整体最快的。

    2. 唉,会发生许多不必要的上传。

  2. 与使用 md5 (etag) 而不是 mtimes 的 s3cmd 同步。 很遗憾:

    1. 每次运行它总是为每个本地文件重新计算md5,这相对昂贵且IOPS很多。

    2. 它是单线程和内存受限的,虽然总是很慢,但在上传许多单独的文件时特别慢,特别是在其他 S3 区域延迟较高的情况下。

我可以毫不费力地编写 C 代码来遍历目录路径,更新本地 sqlite3 缓存数据库,并构建可能更改的设置/上传队列。 不幸的是,我没有 Python 经验,无法将此作为可选同步行为的拉取请求提交。

我不知道“许多小文件,保证没有带外更改,仅限本地->服务器”用例有多普遍。

我发现同步将上次修改时间的差异视为更新的原因是不直观和危险的,即使源中较新的文件正在替换目标中较旧的文件。 这种奇怪的行为应该被记录下来。

也与#404 相关。

添加一个单独的、自记录的--sync-if标志而不是越来越多的非自记录选项怎么样? (例如--size-only--exact-timestamps 。我可以同时应用这两个吗?为什么我必须阅读文档/尝试解决这个问题?)。

--sync-if可以有一个选项列表:

--sync-if newer-timestamp,different-md5,different-timestamp,different-size,deleted,...

用户可以指定一个或多个(逗号分隔的列表),如果文件满足任何条件,它将在目的地更新(上传/删除)。

这将极大地阐明行为,特别是如果您在文档中提到默认行为是: --sync-if different-timestamp,different-size

通读这个问题,我不知道这个同步行为是否已经修复。

我想要一些像rsync -avz一样简单的东西来同步我的本地构建与服务器上的构建。

我一直在使用aws s3 sync ,但是因为我有一个很大的文件(一个电影的帮助文件)并且我的构建步骤从新创建了所有文件,它每次都会复制所有文件并且不必要地缓慢更新网站。

然后我开始使用--size-only来加速它。 不幸的是,最近这让我很不爽。 我重命名了一个文件,构建步骤包括 service-worker.js 文件中的文件列表,因此 sw 知道要缓存的内容。 不幸的是,新旧文件名的长度相同,因此它没有更新 service-worker.js 文件,并继续为旧文件名提供 404。 我花了很长时间才弄清楚发生了什么。

在其他环境中,这似乎确实是一个已解决的问题——即通过 rsync——不过,tbh,我可能有点不知道在 S3 上做这样的事情所带来的挑战。 无论如何,最近有点受此影响,我正在查看其他客户,但他们似乎有依赖项,我不想仅针对此功能采用这些依赖项。

有一个 etag 同步选项会很棒。 我知道在某些情况下它会失败,但对我来说这将是非常有价值的。

早上好!

我们将在 GitHub 上关闭此问题,作为我们迁移到UserVoice以处理涉及 AWS CLI 的功能请求的一部分。

这将使我们为您提供最重要的功能,让您更轻松地搜索和显示对您最关心的功能的支持,而不会因错误报告而淡化对话。

作为 UserVoice 的快速入门(如果还不熟悉的话):发布想法后,人们可以对想法进行投票,产品团队将直接对最受欢迎的建议做出回应。

我们已经从 GitHub 导入了现有的功能请求 - 在那里搜索这个问题!

别担心,为了后代,这个问题仍然存在于 GitHub 上。 由于它是将原始帖子以纯文本形式导入 UserVoice,因此我们仍会牢记 GitHub 问题上已经存在的评论和讨论。

GitHub 将继续作为报告错误的渠道。

再次,现在可以通过在以下位置搜索标题找到此问题: https :

-AWS 开发工具包和工具团队

此条目可以在 UserVoice 上特别找到: https :

摆脱 github 问题? 这似乎是一个错误......

同意。 这看起来更像是微软用来判断问题的重要性/影响的方法,但我觉得它很烦人。

根据社区反馈,我们决定将功能请求返回到 GitHub 问题。

颠倒这个备份!

登顶!

比较 md5 会很棒,我还要补充一点,在上传或下载时输出 md5 会很有帮助。 这可以存储在我们自己的数据库中,并有助于确定是否需要通过我们的数据库进行同步以限制请求。

@jamesls你能评论这个问题吗?
https://github.com/aws/aws-cli/issues/4460

此页面是否有帮助?
0 / 5 - 0 等级