1 个不稳定版本

0.1.0 2022 年 8 月 3 日

#322缓存

MPL-2.0 许可证

34KB
506

OpenSUSE 镜像感知 RPM 缓存代理

这是一个小型的服务,允许从 download.opensuse.org 及其镜像网络缓存 RPM、元数据、磁盘镜像等。这允许更快的内容刷新、更低的延迟,尤其是在您拥有多个系统时,可以更好地利用带宽。在一些早期的测试中(请参阅技术细节部分),这已被证明可以在热缓存中减少 zypper 元数据刷新时间 75%,在温缓存中减少 25%。此外,重复安装软件包的速度显著提高,测试表明此代理能够以 400MB/s 或更高的速度提供数据。

用法

容器

docker run -p 8080:8080 -v /your/storage/:/tmp/osuse_cache/ -u X:X firstyear/opensuse_proxy_cache:latest

Docker 容器通过环境变量进行配置。这些变量会影响镜像

  • CACHE_LARGE_OBJECTS - 是否缓存大型对象,如 ISO/vm 镜像/引导镜像?
  • CACHE_SIZE - 缓存内容的磁盘大小,以字节为单位。默认为 16GiB。
  • CACHE_PATH - 缓存内容应存储的路径。默认为 /tmp/osuse_cache
  • BIND_ADDRESS - 监听的地址。默认为 [::]:8080
  • VERBOSE - 启用详细日志。
  • TLS_BIND_ADDRESS - 监听 https 的地址。默认关闭。
  • TLS_PEM_KEY - PEM 格式密钥的路径。
  • TLS_PEM_CHAIN - PEM 格式 CA 链的路径。
  • MIRROR_CHAIN - 您希望直接使用的上游镜像的 URL(可能另一个 opensuse-proxy-cache 实例)

从源码(高级)

cargo run -- --help

更改您的系统仓库

为了让您的系统使用此代理,它们需要配置为通过此缓存发送流量。以下内容可以更新您的仓库位置。将 IPADDRESS 改为您的缓存的主机名或 IP。

sed -i -E 's/https?:\/\/download.opensuse.org/http:\/\/IPADDRESS:8080/g' /etc/zypp/repos.d/*.repo

提示:这也适用于 obs:// 仓库 :)

已知问题

未知内容类型

某些类型的内容尚未“分类”,这意味着我们不缓存它们,因为我们不知道要使用什么策略。如果您在日志中看到这些内容,请报告它们!日志行如下

opensuse_proxy_cache::cache ⚠️  Classification::Unknown - /demo_unknown.content

此信息将帮助我们调整内容分类器以及我们可以应用于缓存项的策略。

磁盘使用量可能超过配置容量

由于本服务中缓存的工作方式,如果当前存在一些处于未命中处理过程中的内容,它们将不计入最大容量中,这可能导致磁盘使用量超出您分配的量。将容量超出15%作为缓冲“以防万一”是一个安全的规则。此外,请注意,当缓存确实驱逐项目时,它不会在所有该项目的活动下载完成后从磁盘上删除它们。这又可能导致磁盘使用量看起来大于容量。

技术细节

撰写此文档的部分动机是zypper refresh或zypper install在操作具有高延迟的国家或地区时的性能缓慢(以及zypper破坏其他缓存代理(如squid)的行为)。

元数据刷新

让我们来看看运行zypper ref --force repo-oss时会发生什么。

由于download.opensuse.org和mirrorcache.opensuse.org位于欧盟,往返延迟约为350ms。这很快就会累积起来,其中单次HTTP GET请求从我家的互联网连接(澳大利亚,到我的ISP 20ms)大约需要1秒钟。

一个存储库需要4个必需的文件(repomd.xml、media、repomd.xml.key和repomd.xml.asc)。zypper最初会对repomd.xml执行HEAD请求,然后关闭连接。如果这被认为是“过时”,zypper然后打开第二个连接并请求完整的4个文件集。

从我的连接来看,HEAD请求需要0.7秒。第二次GET请求序列从第一个连接打开到关闭需要2.6秒。

如果我们执行完全刷新,这个过程会为每个我们拥有的存储库重复两次连接,仅网络操作就需要约3.2秒。

给定一个opensuse/tumbleweed:latest容器,运行time zypper ref --force需要32秒才能完成。添加更多存储库会使所需时间线性增加。

以代理缓存作为存储库进行操作,当缓存“热”时,可以将此时间缩短至8秒,现在这几乎全部是zypper处理内容时的CPU时间。当缓存“暖”时(即有效,但我们需要检查内容是否已更改),完成所需的时间是21秒。在某些情况下,元数据可能是混合的暖/冷/冷,一些对此进行的测试显示执行时间在9秒到16秒之间。但仍然比没有的好。

影响这种执行时间减少的主要因素是

  • 连接池更有效,这意味着所有元数据流都通过单个连接传输。
  • 当存在元数据并对其进行有效性检查时,只需要一个HEAD请求,从而减少了传输/延迟。

更新:zypper总是首先请求“repomd.xml”,这意味着我们可以检测到这一点并使用它来预取其他元数据,或者我们可以使用它来确定剩余元数据的有效性。使用预取,冷缓存完成刷新需要14.1秒,热缓存仍然是8.0秒,而暖缓存需要10.5秒来完成。

RPM下载

这是在这个服务中找到的大多数收益的地方。假设我们有一个opensuse/tumbleweed:latest容器,我们正在运行“zypper in -y less”。这应该需要下载4个RPM:file-magic、libmagic、file和less。

zypper 首先向 download.opensuse.org 发送初始 GET 请求,下载 /tumbleweed/repo/oss/media.1/media,返回 200 状态码和当前媒体构建的名称。然后 zypper 以 /tumbleweed/repo/oss/noarch/file-magic-5.40-1.13.noarch.rpm 的内容类型 application/metalink+xml 请求该文件。对此请求的响应提供了一个包含位置和优先级的镜像集合,该集合在一个 12158 字节的 xml 文件中。我们可以按地理顺序查看这些镜像。

<!-- Mirrors in the same AS (4739): -->

<!-- Mirrors which handle this country (AU): -->
<url location="nz" priority="1">http://opensuse.mirrors.uf1.nz/tumbleweed/repo/oss/noarch/file-magic-5.40-1.13.noarch.rpm</url>

<!-- Mirrors in the same continent (OC): -->
<url location="nz" priority="2">http://opensuse.mirrors.theom.nz/tumbleweed/repo/oss/noarch/file-magic-5.40-1.13.noarch.rpm</url>

<!-- Mirrors in the rest of the world: -->
<url location="tw" priority="3">http://free.nchc.org.tw/opensuse/tumbleweed/repo/oss/noarch/file-magic-5.40-1.13.noarch.rpm</url>
<url location="jp" priority="4">http://ftp.riken.jp/Linux/opensuse/tumbleweed/repo/oss/noarch/file-magic-5.40-1.13.noarch.rpm</url>
<url location="jp" priority="5">http://ftp.kddilabs.jp/Linux/packages/opensuse/tumbleweed/repo/oss/noarch/file-magic-5.40-1.13.noarch.rpm</url>

zypper 不是使用具有优先级的单个镜像,而是查询前 5 个镜像并解析它们的名称。在这种情况下,文件下载开始于 ftp.kddilabs.jp - 是的,第一个请求的镜像在列表中是第 5 个(而不是第一个)。zypper 等待此文件下载完成然后关闭连接。

然后 zypper 在新的连接中请求来自 download.opensuse.org 的 libmagic 的另一个 application/metalink+xml。同样,zypper 看到了相同的 5 个镜像,这次决定从 free.nchc.org.tw 下载。

此过程再重复两次,用于剩余的两个文件。下载 898Kib 的文件耗时约 5 秒(平均速度约为 180Kib/s)。这使得 zypper 的总执行时间为 9.89 秒。

正如我们所看到的,这里有一些行为会导致挫败感和延迟。

  • 对具有高延迟的目标进行元数据的多重重新打开连接。
  • 金属链接文件中的大部分数据都被丢弃。
  • 镜像不是按优先级顺序使用(uf1.nz - 42ms / 13MB/s,ftp.riken.jp - 298ms / 300kb/s)。
  • 下载被发送到多个镜像,这破坏了大多数缓存代理,因为代理是在完整 URL 上进行缓存的。

使用代理缓存时,会有不同的行为。目前,不是使用 application/metalink 文件,而是使用(我认为是实验性的?)mirrocache 系统。该系统与 download.opensuse.org 的功能相似,但不是发送 xml,而是将 302 重定向到包含您内容的镜像。此时重定向到的镜像似乎位于欧盟,因此延迟非常高,导致冷安装所需时间增加至 22 秒(这比没有代理的最坏情况时间还要慢)。

然而,一旦缓存“热”了,zypper 的完整执行时间只需 6.5 秒。这比没有代理缓存快 3.3 秒。

对于更大的下载,这个过程会多次支付,特别是在有许多系统和 zypper dup,或 docker 容器和 zypper 安装的情况下。以下是一个安装 rust 的示例,rust 是一个大型二进制文件。以下时间是在 zypper in -y rust1.54 执行时间。

  • 无缓存 - 27.586 秒
  • 冷缓存 - 41.010 秒
  • 热缓存 - 10.403 秒

因此,如果您网络中有多台 opensuse 机器,您可能会从这种代理缓存中获得好处,仅从节省带宽和时间方面来看也是如此。 :)

Zypper 的潜在改进领域

从上述分析中,我们可以看到 zypper 可以通过以下方式改进,以帮助那些在高延迟/低带宽连接上的人们。好处是这些改进中的每一个都会帮助那些靠近欧盟镜像的人,同时也会减少现有镜像和 download.opensuse.org 自身的负载:D

即使对 zypper 做了所有这些更改,这个代理缓存仍然对用户有益,尤其是如果您在您的网络中托管多个机器。毕竟,很难超越同一网络的延迟和带宽:)

如果您想体验“澳大利亚互联网体验”,可以在测试机器上使用以下方法添加延迟

sudo tc qdisc add dev eth0 root netem delay 350ms

下载.opensuse.org 在多个地区/Anycast

目前所有元数据请求都由 download.opensuse.org 提供。将这些请求分离到多个地区(通过 Anycast 或使用首次运行配置文件或其他 geoip 知识来确定最近的地区)将有益于减少延迟,尤其是在亚洲和大洋洲等对欧盟/美国具有高延迟的地区。

在刷新时改进连接池/请求批处理

在刷新过程中,每个刷新的仓库都会打开两个连接。由于许多仓库来自 download.opensuse.org,更好的连接池使用以及并行执行请求以利用 CPU 时间处理接收到的元数据(即异步/线程行为)将显著减少 zypper 刷新完成所需的时间。

对于每个下载的 rpm,都会建立两个连接 - 一个用于 application/metalink+xml 文件,另一个用于包含该文件的镜像。对于像 zypper dup 或完整分布升级这样的大规模下载。如果我们说这是安装了 50 个软件包,这意味着向 download.opensuse.org 发出 50 个单独的请求来下载这个金属链文件。

而不是金属链文件与软件包一一对应,它们应该与仓库一一对应。这样,在安装开始时,可以查询使用的仓库的金属链文件,提供可以完全服务该仓库的镜像列表。这将节省 ~607500 字节的数据传输到 download.opensuse.org,以及 49 个更少的连接,以及 ~50 秒的仅延迟来检索这些数据。

此外,这些金属链数据可以在仓库刷新时而不是在安装时返回。

镜像应进行评估

当首次请求仓库的金属链文件时,可以评估到提供的镜像的延迟。这些评估结果可以缓存(并在 zypper ref 或 ref --force 时重新运行)。

从这些评估结果中,可以确定“最低延迟”的镜像,以及选择其他可能的镜像。

而不是在“金属链前五名”之间进行“轮询”,这将允许在最低延迟或低于 10% 的镜像之间进行轮询。

对于可以访问许多高性能镜像的人来说,这意味着他们现在可以平衡超过 5 个镜像。对于只能访问一个高性能镜像的人来说,他们始终会被正确地引导到该镜像,并且能够在真正的延迟排序中回退。

一次只使用一个镜像

而不是在单个文件之间轮询镜像,应该为每个安装调用专门使用一个镜像。这允许有更一致的下载体验,并且通过能够更有效地重用连接来减少连接延迟。

依赖关系

~5–15MB
~193K SLoC