-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reproducible builds broken in 1.8.0 #2005
Comments
Just to check, do you get reproducible builds if your builder is |
No, the problem remains with a 1.17 builder. |
Version 1.8.0 and above breaks reproducible builds. GoogleContainerTools/kaniko#2005
Version 1.8.0 and above breaks reproducible builds. GoogleContainerTools/kaniko#2005
My colleague and I stumbled on this exact issue and now can confirm: downgrading to v1.7.0 of kaniko solved the problem: the produced image had the same digest! So it's indeed a regression. |
FWIW I'll add another anecdata here. I'm testing Kaniko at my workplace and had the exact same issue building a Golang 1.17 project. I could not figure out why Kaniko reproducible builds was not working even though I verified that my go binary was the same between builds. Downgrading to v1.7.0 of Kaniko fixed the issue. I could try and add some data here if that would be helpful but my situation was pretty much the one describe in the Issue description. |
Just encountered this today. We use |
Version 1.8.0 and above breaks reproducible builds. GoogleContainerTools/kaniko#2005
I think #1809 causes this issue. I compared layer tar's produced by 1.7.0 and 1.8.1 (because I am idiot) and the tar format (Pax Header) changed between these versions. 1.8.1 contains ctime and atime as @NullHypothesis noted. The old version does not. maybe |
Narrowing down @suicide's comment, the |
This reverts commit ee95be1. Due issues with reproducible builds: GoogleContainerTools#2005
I can confirm that building 1.9.1 but with #1809 reverted results in reproducible image builds working as expected. |
I believe that the most sensible place to resolve this would be in the go-containerregistry project; I've opened an issue there. |
This change adds a new flag to zero timestamps in layer tarballs without making a fully reproducible image. My use case for this is maintaining a large image with build tooling. I have a multi-stage Dockerfile that generates an image containing several toolchains for cross-compilation, with each toolchain being prepared in a separate stage before being COPY'd into the final image. This is a very large image, and while it's incredibly convenient for development, making a change as simple as adding one new tool tends to invalidate caches and force the devs to download another 10+ GB image. If timestamps were removed from each layer, these images would be mostly unchanged with each minor update, greatly reducing disk space needed for keeping old versions around and time spent downloading updated images. I wanted to use Kaniko's --reproducible flag to help with this, but ran into issues with memory consumption (GoogleContainerTools#862) and build time (GoogleContainerTools#1960). Additionally, I didn't really care about reproducibility - I mainly cared about the layers having identical contents so Docker could skip pulling and storing redundant layers from a registry. This solution works around these problems by stripping out timestamps as the layer tarballs are built. It removes the need for a separate postprocessing step, and preserves image metadata so we can still see when the image itself was built. An alternative solution would be to use mutate.Time much like Kaniko currently uses mutate.Canonical to implement --reproducible, but that would not be a satisfactory solution for me until [issue 1168](google/go-containerregistry#1168) is addressed by go-containerregistry. Given my lack of Go experience, I don't feel comfortable tackling that myself, and this seems like a simple and useful workaround in the meantime. As a bonus, I believe that this change also fixes GoogleContainerTools#2005 (though that should really be addressed in go-containerregistry itself).
Ran into this defect yesterday with |
I've submitted a Pull request to go-containerregistry based on @zx96 initial investigation. I've confirmed locally that if I build kaniko against that commit, that the bug vanishes. (Note that I also had to bump the version of cloud.google.com/go/storage to atleast v1.27.0 and not just the automatically resolved v1.21.1, before kaniko would compile again, this probably comes from jumping 5 minor versions in go-containerregistry) As soon as the PR is merged in the upstream project, I'll create a PR for kaniko |
That's fantastic, thanks @BronzeDeer and kudos to @zx96 as well. Given that change, could we then avoid using The reason I ask is that I'm trying to optimize our build process for thousands of Spring Boot apps that use multi-stage Dockerfile to separate out the app layer from the 3rd party jar layer, etc. My concern with
|
The bug fix makes TL;DR: P.S.: On Caching: Kaniko's own build-time caching seems mostly unaffected by the bug , since it caches based on content of the Dockerfile rather than raw digests, varying digests only hinder external tools (which also includes container runtimes pulling images sadly) |
Thanks for the clarity on I supposed the performance issues with this feature can be explored via (#862) and (#1960). I see a comment in #862 concerning the possible use of On caching -- I've also struggled to get kaniko to generate matching shas for the cached COPY layers. I'm using the remote |
This change adds a new flag to zero timestamps in layer tarballs without making a fully reproducible image. My use case for this is maintaining a large image with build tooling. I have a multi-stage Dockerfile that generates an image containing several toolchains for cross-compilation, with each toolchain being prepared in a separate stage before being COPY'd into the final image. This is a very large image, and while it's incredibly convenient for development, making a change as simple as adding one new tool tends to invalidate caches and force the devs to download another 10+ GB image. If timestamps were removed from each layer, these images would be mostly unchanged with each minor update, greatly reducing disk space needed for keeping old versions around and time spent downloading updated images. I wanted to use Kaniko's --reproducible flag to help with this, but ran into issues with memory consumption (GoogleContainerTools#862) and build time (GoogleContainerTools#1960). Additionally, I didn't really care about reproducibility - I mainly cared about the layers having identical contents so Docker could skip pulling and storing redundant layers from a registry. This solution works around these problems by stripping out timestamps as the layer tarballs are built. It removes the need for a separate postprocessing step, and preserves image metadata so we can still see when the image itself was built. An alternative solution would be to use mutate.Time much like Kaniko currently uses mutate.Canonical to implement --reproducible, but that would not be a satisfactory solution for me until [issue 1168](google/go-containerregistry#1168) is addressed by go-containerregistry. Given my lack of Go experience, I don't feel comfortable tackling that myself, and this seems like a simple and useful workaround in the meantime. As a bonus, I believe that this change also fixes GoogleContainerTools#2005 (though that should really be addressed in go-containerregistry itself).
This commit adds ia2 to our general docker build pipeline by adding a GitHub action. The commit also adds a Dockerfile that allows for reproducible, kaniko-based builds. Simply run "make docker" to create a reproducible image. We currently use kaniko in version 1.7.0 because 1.8.0 (the newest version as of 2022-03-22) has a bug that breaks reproducible builds: GoogleContainerTools/kaniko#2005
Actual behavior
Consider the Go program main.go and its corresponding Dockerfile (both listed below). Using kaniko in version 1.7.0, two subsequent reproducible builds using the command listed below result – as expected – in two identical Docker images. In version 1.8.0, however, two subsequent builds are no longer identical.
Expected behavior
I expect two subsequent reproducible builds to result in identical images.
To Reproduce
Steps to reproduce the behavior:
The Go program is identical in both builds but the surrounding tar archive isn't. I compared the hexdump of the tar archive of both builds and noticed that there are atime and ctime fields that contain a Unix timestamp, which is the reason why the builds differ. Could this regression have been caused by ee95be1?
Additional Information
Triage Notes for the Maintainers
--cache
flagThe text was updated successfully, but these errors were encountered: