Conversation
- Use compressed layer format for containerd storage - Added parallelization in layer processing - Optimized size calculation during tar processing
There was a problem hiding this comment.
Pull Request Overview
This pull request introduces significant performance optimizations for containerd storage scenarios in the Docker image save/load functionality. The changes focus on reducing expensive operations and adding parallelization to improve image saving performance, especially when using containerd as the storage backend.
- Implements cached containerd storage detection to avoid redundant checks
- Adds parallel processing for layer preparation and downloads using errgroups and semaphores
- Optimizes layer size calculations by deferring expensive operations for containerd storage
Signed-off-by: Jesse Brown <jabrown85@gmail.com>
|
Note: This PR was reverted in #297. |
| // Stream compressed data directly to tar (Docker-native format) | ||
| tempFile, err := os.CreateTemp("", "compressed-layer-*.tar") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer os.Remove(tempFile.Name()) | ||
| defer tempFile.Close() | ||
|
|
||
| // Calculate size while streaming to temp file | ||
| bytesRead, err := io.Copy(tempFile, compressedReader) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| uncompressedSize = bytesRead | ||
|
|
||
| // Rewind temp file for reading | ||
| if _, err := tempFile.Seek(0, 0); err != nil { | ||
| return err | ||
| } |
There was a problem hiding this comment.
layer.Size() returns the compressed size and we need the uncompressed size for the tar header.
There was a problem hiding this comment.
i am kinda confused, how could io.Copy on layer.Compressed() returns uncompressed size?
is it supposed to be layer.Uncompressed()? (or) willingly copying the compressed layer into tar!!
if it is to copy compressed layer into tar archive then: no.of bytes copied by io.Copy on compressed layer == layer.Size() isn't it
There was a problem hiding this comment.
You may be right - it has been a minute since I looked at this properly. If you have the time or interest I'd love for some extra 👀 on optimizations...
This containerd-snapshotter default removes an ancient assumption that allowed for the project to not have to pay the cost of reading and re-sending the same base layers over and over after each build. Even though we are using tar.
There was a problem hiding this comment.
i have few doubts on docker tarball format, let me know the answers if you know
- if a given layer already exists in docker's containerd storage, should we need to rearchive those layers into tarball, (or)
docker image loaddoesn't error if a layer already exists in containerd content store?
There was a problem hiding this comment.
I think we have to rearchive them. I've tried a few ways to avoid but nothing works.
The main reason I had to revert was/is that we can produce a fine looking tarball and docker will load it fine..but then it is actually NOT ok in containerd-snapshotter storage. You can't copy/push it without getting errors like (#296)
Digest did not match, expected
sha256:8a78... got sha256:90e0...
This pull request introduces significant performance optimizations and code improvements to the image saving and layer handling logic in
local/store.go, with a focus on supporting containerd storage scenarios more efficiently. The main changes include implementing caching for containerd storage detection, optimizing layer processing by introducing parallelism, and improving how layer sizes are calculated and cached. These updates are especially beneficial for environments using containerd, leading to faster image saves and more efficient resource usage.Performance optimizations for containerd storage:
Storestruct and used it throughout the codebase to avoid redundant checks and improve performance. (containerdStorageCache,usesContainerdStorageCached) [1] [2]AddLayermethod to proactively calculate (or defer) the uncompressed size of compressed layers, optimizing for containerd by skipping unnecessary calculations during download and only performing them when required. [1] [2]Parallelization and concurrency improvements:
errgroupand semaphores in both layer preparation for tar creation and during layer downloads, reducing wall-clock time and making better use of system resources. [1] [2]API and function signature updates:
doSave,addImageToTar) to accept anisContainerdStorageflag, ensuring the optimizations are applied conditionally based on the storage backend. [1] [2] [3] [4]These changes collectively enhance the efficiency and scalability of image saving and layer management, especially when working with containerd-based Docker setups.
Note: the streaming of the existing layer files is the biggest win here by a large margin
This is an effort to close out buildpacks/pack#2272 and make sure perf is decent on docker's containerd storage option.
Using @edmorley 's slightly modified example in the issue above. This is a subsequent
pack build. I've only extracted the Export numbers here as this isn't targeting the otherpackinefficiencies.baseline non-
containerdstorage current lifecycle:containerdstorage current lifecycle:containerdstorage improved lifecycle:We are never going to get to pre-
containerdperf due to the cheat we were using before. We now have to give the docker daemon layers we were entirely able to skip before. Those layers are also the bigger base layers. We are much closer todocker load <exported>on a clean docker installation though. IME it was about ~1.7s.