使用 S3 作为容器注册表
Using S3 as a Container Registry

原始链接: https://ochagavia.nl/blog/using-s3-as-a-container-registry/

开发人员与 Outerounds 合作构建了一个自定义容器映像生成器。 他们发现 Amazon Simple Storage Service (S3) 可以充当容器注册表。 通过 HTTP 公开 S3 存储桶并将图像文件上传到指定路径,用户可以直接从 S3 URL 执行“docker pull”命令。 开发人员通过创建运行“cowsay”的容器映像来演示此功能,然后从 S3 URL 拉取并执行该映像。 传统上,容器映像托管在 Docker Hub、GitHub 容器注册表或弹性容器注册表 (ECR) 等容器注册表上,但开发人员发现,与 ECR 相比,S3 在上传过程中提供明显更高的速度。 在测量性能差异后,他们发现 S3 比 ECR 快大约八倍。 S3 速度提高的关键优势在于它能够同时上传各个层块,从而有效地利用可用带宽。 然而,与 ECR 相比,“问题”在于 ECR 遵守开放容器倡议 (OCI) 分发规范,该规范要求顺序层推送,从而导致大量带宽未使用。 因此,“docker pull”功能通过发出 HTTP 请求来运行,允许从任何具有正确配置的内容类型的 HTTP 服务器中拉取容器。 因此,精心布置的 S3 存储桶可以充当容器注册表。 尽管如此,作者承认潜在的缺点,包括与图像验证、安全性和访问控制相关的问题,并鼓励在做出明确的结论之前进行进一步的研究和实验。

开放容器倡议 (OCI) 分发规范存在多个问题,导致其效率较低且用户友好性较低。 尽管分块上传,但层推送必须按顺序发生。 通过 Docker Hub 和 GitHub PackageRegistry (GHPR) 等服务进行分块上传目前已损坏。 规范中指定的内容范围值不符合注释请求 (RFC) 7233 格式。 并行性发生在单个 blob 级别,但不是每个 blob 级别。 此外,由于在标准创建过程中丢失了文本,因此错过了标准化标签分页列表的一个关键机会。 由于这一遗漏,不同的组织实施了独特的解决方案。 将应用程序及其依赖项容器化到单个可传输文件中具有许多优点。 开发人员不再需要单独安装依赖项、管理配置文件或处理各种操作系统的复杂问题。 这种便利可以更轻松地跨不同平台部署应用程序。 以前,实现类似的功能需要对配置、设置过程和自动化部署工具进行大量的编程工作。 然而,容器化提供了一个广泛的、通用的标准,简化了无数开发人员的开发。 通用方法(包括配置、存储、数据和环境元素的标准化)使创建和维护系统变得更加简单。 虽然用户可能会在某些情况下体验到改进,但仍担心由于将完整的库集作为部署工件而导致带宽和存储消耗。 尽管高速互联网接入变得越来越普遍,但集装箱化的影响在没有快速连接的地区仍然相当大。 许多缺点可以通过有效的增量压缩和基础图像利用来弥补。 总之,虽然容器化以前是可以实现的,但它所带来的好处(例如易于设置、减少依赖管理和提高可移植性)证明了其广泛采用的合理性。 尽管容器化在某些情况下可能会带来挑战,但它可以解决开发人员和运营团队等的关键痛点。 对于许多人来说,用更高的带宽和存储容量来换取简化的操作知识和降低的复杂性证明是可以接受的折衷方案。
相关文章

原文

For the last four months I’ve been developing a custom container image builder, collaborating with Outerbounds. The technical details of the builder itself might be the topic of a future article, but there’s something surprising I wanted to share already: you can use S3 as a container registry! You heard it right. All it takes is to expose an S3 bucket through HTTP and to upload the image’s files to specific paths. With that in place, you can actually docker pull from it. Isn’t that neat?

In the rest of this post I’ll explain how it all works, but let’s start with a demo for the impatient among us. I created a container image that runs cowsay and mirrored it to a bucket. Here’s what happens when you pull and run it from the bucket’s url:

$ docker run --rm pub-40af5d7df1e0402d9a92b982a6599860.r2.dev/cowsay

 _________________________
< This is seriously cool! >
 -------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Don’t you agree with the cow? Note that, for this demo, I’m using R2 instead of S3 (because it has free egress 😎). Fortunately, it doesn’t matter whether you use R2 or S3, since they are API-compatible. As a matter of fact, I used the AWS SDK to push my image to R2.

But why?

Using S3 is not the traditional approach for hosting container images. You’d normally use a container registry, such as DockerHub, GitHub Container Registry, ECR, etc. What benefits does S3 bring, then, to make deviating from the trodden, boring, path worthwhile?

Let’s take a step back. We are developing a custom image builder (or bakery, as we affectionately call it) because of speed. We want to go from requirements to a ready-to-pull image in a few seconds. The easiest container registry to use in our case is ECR, because we are on AWS. However, it turns out there’s a substantial performance difference between S3 and ECR when it comes to upload speed!

I discovered the performance gap somewhat by accident. Since speed is important for us, and the first rule of performance optimization is to measure, I instrumented the code to generate traces. Having that, I went hunting for optimization opportunities and came across something unexpected: the traces showed that pushing layers to the container registry accounted for a significant amount of time! That felt off, so I decided to run a small benchmark: to upload a 198 MiB layer to ECR and to S3, and observe the difference in duration.

Here’s the outcome:

Minimum observed speed Maximum observed speed
ECR 24 MiB/s (8.2 s) 28 MiB/s (7.0 s)
S3 115 MiB/s (1.7 s) 190 MiB/s (1.0 s)

The table shows that S3 is up to 8x faster than ECR, which is almost an order of magnitude! Of course, there are caveats, but “raw” S3 container registries are nevertheless a promising avenue of optimization.

What makes S3 faster than ECR?

The big difference between pushing to ECR and uploading objects to S3 is that the latter allows uploading a single layer’s chunks in parallel. Given enough bandwidth, this yields a massive increase in throughput. In fact, parallel chunked uploads are recommended in the AWS docs to maximize bandwidth usage.

Why can’t ECR support this kind of parallel uploads? The “problem” is that it implements the OCI Distribution Spec, which is the standard for container registries (i.e. the reason why you can docker pull and docker push to different registry implementations). According to the specification, a layer push must happen sequentially: even if you upload the layer in chunks, each chunk needs to finish uploading before you can move on to the next one. Needless to say, having a single active connection per layer leaves a significant amount of bandwidth unused!

Aside: we also tested the performance of sequential uploads to S3. The result? Throughput went down to ECR-like levels!

But S3 is not a container registry!

Indeed, S3 is not a container registry in the strict sense of the word. You can’t docker push to it, and the fact that you can docker pull is mostly a happy coincidence. So how does it work?

The answer to our question is revealed by looking at the inner workings of docker pull. Spoiler: it’s HTTP requests all the way down. More specifically, I logged the requests issued by docker pull and saw that they are “just” a bunch of HEAD and GET requests. As an example, see the log of a docker pull my-image:latest at my self-hosted registry (lines starting with # are comments):

# Check whether the image's manifest is present in the registry
HEAD /v2/my-image/manifests/latest
# Download the image's manifest
GET /v2/my-image/manifests/latest
# Re-download the image's manifest, now addressed using the manifest's hash
# (I think this is a sanity check by docker)
GET /v2/my-image/manifests/sha256:dabf91b69c191a1a0a1628fd6bdd029c0c4018041c7f052870bb13c5a222ae76
# Download one of the image's blobs (which happens to be the image's metadata)
GET /v2/my-image/blobs/sha256:a606584aa9aa875552092ec9e1d62cb98d486f51f389609914039aabd9414687
# Download the remaining image's blob (which happens to be its only layer)
GET /v2/my-image/blobs/sha256:ec99f8b99825a742d50fb3ce173d291378a46ab54b8ef7dd75e5654e2a296e99

That’s it! A docker pull is merely downloading files through HTTP! Which means… You can pull containers from any static file server, as long as it has the necessary files at the expected paths and sets the right Content-Type header for each request. Since a S3 bucket is capable of both, a carefully crafted bucket can become a container registry!

Aside: if you want to know more about “manifests”, “blobs”, and such, check out my article on Crafting container images without dockerfiles and the OCI Image Format Specification.

Caveats

In case it’s not already clear: this is all very experimental. I’m waiting to do more research before making any serious claims. Will it end up in production? Or will you, my dear reader, send me an email explaining how my approach is utterly flawed?

Note that, while I haven’t made a survey of the container registry offerings out there, it’s obvious they come with features that make them more attractive than dumping files on a bucket. For instance: you can trust the images you upload are actually valid (because the registry uses the standard push method); you can run automated security scans against your layers and receive warnings if there’s anything fishy; you can natively specify who has access to private repositories; etc.

Don’t let these caveats discourage you, though. If it all works as well as I’m hoping, maybe we’ll see a new trend of hosting public container images in Cloudflare’s R2! What would you say to free egress?

PS. What about the whale?

It’s a pun… go have a look at the docker logo 😉

If you liked this article, feel free to check out past blog posts or to subscribe.

联系我们 contact @ memedata.com