Closed pganeshar closed 2 years ago
@pganeshar
I am expecting you are only experiencing this with first three steps of stage, and this happens because following steps are cached and does not uses arg so history keeps matching with intermediate image which is already in storage. I suggest using echo before these steps.
FROM builder1 AS builder2
COPY . /app
WORKDIR /app/cmd/webapp
Does changing Containerfile
to this fixes issue for you ?
FROM public.ecr.aws/docker/library/golang:1.18 as builder1
COPY go.mod /app/go.mod
COPY go.sum /app/go.sum
# internal proxy
ENV GOPROXY=a.b.c
ENV GONOSUMDB=x.y.z
WORKDIR /app
RUN go mod download
ARG GIT_HASH=""
ARG SERVICE_VERSION=""
# pass the git hash and service version to the next stage
ENV GIT_HASH=${GIT_HASH}
ENV SERVICE_VERSION=${SERVICE_VERSION}
# ------------------------------------------------------------------------
# The second stage container, for building the Go Binary
# ------------------------------------------------------------------------
FROM builder1 AS builder2
RUN echo "Service Version: $SERVICE_VERSION"
RUN echo "Git Hash: $GIT_HASH"
COPY . /app
WORKDIR /app/cmd/webapp
@flouthoc
Thanks very much for your comment and I am very sorry about the late response. Had to deal with different priorities so didn't have a chance to test this out until now.
Yes, your suggestion works. Does doing the echo after FROM builder1 AS builder2
breaks the cache?
We actually depend on these values as they get baked into the microservice image. And then Spinnaker uses these to deploy to different environments.
Here is the command we use to run go build
:
RUN GO_VERSION=$(grep -e "GO_SDK_URL " ../../go.mod | cut -d' ' -f2) &&\
SERVICE_VERSION=${SERVICE_VERSION:-$(grep -i "service_version: " ../../settings.yaml | cut -d ' ' -f2 | head -1)} &&\
CGO_ENABLED=0 go build \
--ldflags "-s -X go.sdkVersion=$GO_VERSION -X go.serviceVersion=$SERVICE_VERSION -X go.gitHash=$GIT_HASH" \
-o app_binary
Can I rely on this approach? I have tested a few times and it seemed to have worked every single time, i.e Podman used the values from build args instead of getting them from its cache.
When I searched the net I came across this article: https://stackoverflow.com/questions/35134713/disable-cache-for-specific-run-commands
Do you think its better to use that approach to bust the cache with "date +%s"?
@pganeshar Is $GIT_HASH
being propagated from the build-arg
? If yes then this should work for busting a cache for specific run instruction where HASH was changed.
I also like the idea of passing date +%s
as build-arg and using that to bust cache for a specific run instruction I think both should work here.
@flouthoc Yes, GIT_HASH does get passed to podman build command using --build-arg
but the weird this is that even this din't bust the cache and podman used previous values from its cache.
For example, I ran release/16.0.0 pipeline first, build ran correctly using the values passed in --build-arg
params.
I then ran pipeline for release/5.0.0 (which has a different git hash) but podman ran the build using the values from release/16.0.0 instead. So not sure why it used the cache in this scenario?
I am going to do some testing using the UNIX timestamp variable and hoping I get consistent results.
Thanks.
For example, I ran release/16.0.0 pipeline first, build ran correctly using the values passed in --build-arg params. I then ran pipeline for release/5.0.0 (which has a different git hash) but podman ran the build using the values from release/16.0.0 instead. So not sure why it used the cache in this scenario?
For this I think there must a case like https://github.com/containers/buildah/issues/4367#issuecomment-1291729248 , but if you can provide a small reproducer I can actually create a working example which will work.
I am going to do some testing using the UNIX timestamp variable and hoping I get consistent results.
Let me know your findings then we can discuss it here.
@flouthoc , here the steps to reproduce:
Run the following build:
podman buildx build --isolation chroot --jobs 0 --platform linux/amd64 -t ECR_URL/sandbox/test-go-microservice:16.0.0-1668700466336-signed-amd64 --network host --build-arg bitbucket_id=**** --build-arg 'bitbucket_token=****' --build-arg GIT_HASH=b8372e2ce8752d2567fd99a91d229a369fd9429e --build-arg SERVICE_VERSION=16.0.0 .
[5/5] STEP 1/9: FROM gcr.io/distroless/static:debug
[1/5] STEP 1/13: FROM public.ecr.aws/docker/library/golang:1.18 AS builder1
[3/5] STEP 1/1: FROM public.ecr.aws/docker/library/busybox:latest AS busybox
Trying to pull public.ecr.aws/docker/library/busybox:latest...
Trying to pull gcr.io/distroless/static:debug...
Trying to pull public.ecr.aws/docker/library/golang:1.18...
Getting image source signatures
Copying blob sha256:3cb762126a62bc9bf7824a3c8d7e44e871fe18cb87876e6734bdd439a8674c54
Copying blob sha256:195ea6a58ca87a18477965a6e6a8623112bde82c5b568a29c56ce4581b6e6695
Copying blob sha256:c85a0be79bfba309d1f05dc40b447aa82b604593531fed1e7e12e4bef63483a5
Copying blob sha256:52908dc1c386fab0271a2b84b6ef4d96205a98a8c8801169554767172e45d8c7
Copying blob sha256:a8ca11554fce00d9177da2d76307bdc06df7faeb84529755c648ac4886192ed1
Copying blob sha256:e4e46864aba2e62ba7c75965e4aa33ec856ee1b1074dda6b478101c577b63abd
Getting image source signatures
Copying blob sha256:22b70bddd3acadc892fca4c2af4260629bfda5dfd11ebc106a93ce24e752b5ed
Getting image source signatures
Copying blob sha256:495a88a03ecbbcbfde13499d262e093f95eadd163befc03c106c9a5d7e57eefd
Copying blob sha256:8fdb1fc20e240e9cae976518305db9f9486caa155fd5fc53e7b3a3285fe8a990
Copying config sha256:bc01a3326866eedd68525a4d2d91d2cf86f9893db054601d6be524d5c9d03981
Writing manifest to image destination
Storing signatures
Copying blob sha256:9601c0a52d295d64421ff81e82d2440cfdd3664cb109490ad34cb39f3a007602
--> bc01a332686
Copying config sha256:1367e76602742bca4790f0f8b1954fe4b3d677617326be8e89e724d5083d6bfd
Writing manifest to image destination
Storing signatures
[5/5] STEP 2/9: COPY --from=busybox /bin/sleep /bin/sleep
Copying config sha256:e53ef54a405f8a28710b399b3f4a0ef0350d6d470088cb26d39f70f9a454939f
Writing manifest to image destination
Storing signatures
--> d2ad78284b3
[5/5] STEP 3/9: USER user:user
[1/5] STEP 2/13: COPY go.mod /app/go.mod
--> a0fe83e0a9d
[5/5] STEP 4/9: COPY --from=builder2 --chown=user:user /app/cmd/webapp/app_binary /app/app_binary
--> 09ddf30164a
[1/5] STEP 3/13: COPY go.sum /app/go.sum
--> c75d92b753b
[1/5] STEP 4/13: ENV GOPROXY=https://athens.xxx.yyy.zzz
--> 6e3d3ad566c
[1/5] STEP 5/13: ENV GONOSUMDB=bitbucket.yyy.zzz
--> e55252f0e1e
[1/5] STEP 6/13: WORKDIR /app
--> 082d34e4317
[1/5] STEP 7/13: RUN go mod download
--> 39c7a7bc7da
[1/5] STEP 8/13: ARG GIT_HASH=""
--> 9771a207a83
[1/5] STEP 9/13: ARG SERVICE_VERSION=""
--> bd6ccecac77
[1/5] STEP 10/13: ENV GIT_HASH=${GIT_HASH}
--> 4b32d68bd9a
[1/5] STEP 11/13: ENV SERVICE_VERSION=${SERVICE_VERSION}
--> 21b74e5c4a9
[2/5] STEP 1/6: FROM 43325dab9f6f81ecaf0aa0f20ccf6be344efea7d95450a283fe1f7f52081cee9 AS builder2
[2/5] STEP 2/6: COPY . /app
--> 6431d051bf7
[2/5] STEP 3/6: WORKDIR /app/cmd/webapp
--> 5f92ea21931
[2/5] STEP 4/6: RUN echo "Service Version Stage 2: $SERVICE_VERSION"
Service Version 2: 16.0.0
--> 0cf3816ecf1
[2/5] STEP 5/6: RUN echo "Git Hash Stage 2: $GIT_HASH"
Git Hash 2: b8372e2ce8752d2567fd99a91d229a369fd9429e
--> 19efc03858a
[2/5] STEP 6/6: RUN GO_VERSION=$(grep -e "bitbucket.yyy.zzz/go/go " ../../go.mod | cut -d' ' -f2) && SERVICE_VERSION=${SERVICE_VERSION:-$(grep -i "service_version: " ../../settings.yaml | cut -d ' ' -f2 | head -1)} && CGO_ENABLED=0 go build --ldflags "-s -X bitbucket.yyy.zzz/go/go.sdkVersion=$GO_VERSION -X bitbucket.yyy.zzz/go/go.serviceVersion=$SERVICE_VERSION -X bitbucket.yyy.zzz/go/go.gitHash=$GIT_HASH" -o app_binary
--> 16837f6e7ef
--> a3cecf20f5b
Now, run second build (with different version and git hash):
podman buildx build --isolation chroot --jobs 0 --platform linux/amd64 -t ECR_URL/sandbox/test-go-microservice:4.0.0-1668547712824-signed-amd64 --network host --build-arg bitbucket_id=**** --build-arg 'bitbucket_token=****' --build-arg GIT_HASH=d0a62e4ea80f56e7b6786f08b6040a5674192436 --build-arg SERVICE_VERSION=4.0.0 .
[5/5] STEP 1/10: FROM gcr.io/distroless/static
[1/5] STEP 1/11: FROM public.ecr.aws/docker/library/golang:1.18 AS builder1
[3/5] STEP 1/1: FROM public.ecr.aws/docker/library/busybox:latest AS busybox
[4/5] STEP 1/1: FROM ECR_URL/common-distroless/secrets-helper:latest AS secrets
Trying to pull gcr.io/distroless/static:latest...
Trying to pull ECR_URL/common-distroless/secrets-helper:latest...
Getting image source signatures
Copying blob sha256:863c7bd51caa4c9419552b92b2c0612f3a168ae089ee7e40d42f5c3a36c8ffb9
Copying config sha256:390e1e47e10d9e6e16f4e06298c941dfa6f4f2a3a3140685b1eeeb8aa361bb10
Writing manifest to image destination
Storing signatures
--> 390e1e47e10
--> bc01a332686
[1/5] STEP 2/11: COPY go.mod /app/go.mod
--> Using cache a2fd94a30c717cd18f00b4b3e47f1f6d716c05b272b0d8d9b888d643828af0ae
--> a2fd94a30c7
[1/5] STEP 3/11: COPY go.sum /app/go.sum
--> Using cache 779e6a99b3b836485fa5ebb8fef3db3072e562926b32e278ae72c4453df54e51
--> 779e6a99b3b
[1/5] STEP 4/11: ENV GOPROXY=https://athens.xxx.yyy.zzz
--> Using cache 8aac32ffcf1ad020069841c477af18f3213191095071f710869ddb05190744e2
--> 8aac32ffcf1
[1/5] STEP 5/11: ENV GONOSUMDB=bitbucket.yyy.zzz
--> Using cache f0e0d1525e256eada80d96827b8de6d2354f241b43c3900d32821308eebb3b49
--> f0e0d1525e2
[1/5] STEP 6/11: WORKDIR /app
--> Using cache beabd5ced74997c382eb7c553ff3882cdb75d60778b820f912c2cfa306f14c4f
--> beabd5ced74
[1/5] STEP 7/11: RUN go mod download
--> Using cache 8a293c91a5138c514cb9d746d752c8def8b0b9c3066f5b9085d0e867bc174e5e
--> 8a293c91a51
[1/5] STEP 8/11: ARG GIT_HASH=""
--> Using cache 71b61ae74514a84897f126e2c01c771f56432f80367945fffacec51f2d70e4be
--> 71b61ae7451
[1/5] STEP 9/11: ARG SERVICE_VERSION=""
--> Using cache a88cfbe7c8d1c0fa67aff90ca41273b2f9adc4e9e2cd188b1e20b58264a81517
--> a88cfbe7c8d
[1/5] STEP 10/11: ENV GIT_HASH=${GIT_HASH}
--> Using cache e083d003770bb087a588534b6fcdfdfe57a8b77fc039cc158c95f7118454b46e
--> e083d003770
[1/5] STEP 11/11: ENV SERVICE_VERSION=${SERVICE_VERSION}
--> Using cache 43d2bf8fd5e7c3593dae47693b8996c8d07cb7d44a13776b8efadbed6eb51206
--> 43d2bf8fd5e
[2/5] STEP 1/6: FROM 43d2bf8fd5e7c3593dae47693b8996c8d07cb7d44a13776b8efadbed6eb51206 AS builder2
[2/5] STEP 2/6: COPY . /app
Getting image source signatures
Copying blob sha256:8fdb1fc20e240e9cae976518305db9f9486caa155fd5fc53e7b3a3285fe8a990
Copying config sha256:21e2f91908c8fe969cf3b5e2f9d0aa4afdea8a9aabf679e722c752f66fb20d0f
Writing manifest to image destination
Storing signatures
[5/5] STEP 2/10: COPY --from=busybox /bin/sleep /bin/sleep
--> 3abbee42f13
[2/5] STEP 3/6: WORKDIR /app/cmd/webapp
--> 9e81f941454
[2/5] STEP 4/6: RUN echo "Service Version Stage 2: $SERVICE_VERSION"
--> 403fa8477fe
[5/5] STEP 3/10: COPY --from=secrets /bin/ /bin/
Service Version Stage 2: 16.0.0
--> 6a32a30caf3
[5/5] STEP 4/10: USER user:user
--> 69a0e6285ae
[2/5] STEP 5/6: RUN echo "Git Hash Stage 2: $GIT_HASH"
--> e6551ba3639
[5/5] STEP 5/10: COPY --from=builder2 --chown=user:user /app/cmd/webapp/app_binary /app/app_binary
Git Hash Stage 2: b8372e2ce8752d2567fd99a91d229a369fd9429e
--> 5b360770359
[2/5] STEP 6/6: RUN GO_VERSION=$(grep -e "bitbucket.yyy.zzz/go/go " ../../go.mod | cut -d' ' -f2) && SERVICE_VERSION=${SERVICE_VERSION:-$(grep -i "service_version: " ../../settings.yaml | cut -d ' ' -f2 | head -1)} && CGO_ENABLED=0 go build --ldflags "-s -X bitbucket.yyy.zzz/go/go.sdkVersion=$GO_VERSION -X bitbucket.yyy.zzz/go/go.serviceVersion=$SERVICE_VERSION -X bitbucket.yyy.zzz/go/go.gitHash=$GIT_HASH" -o app_binary
--> 7c05726cf3f
--> 2a8ced9e014
As you can see on the second build (#2), it should have used SERVICE_VERSION=4.0.0
and GIT_HASH=d0a62e4ea80f56e7b6786f08b6040a5674192436
but instead Podman has used the values from step 1.
Here is the Dockerfile:
# ------------------------------------------------------------------------
# The first stage container, for setting up and downloading dependencies
# ------------------------------------------------------------------------
FROM public.ecr.aws/docker/library/golang:1.18 as builder1
# only copy the go.mod and go.sum for better docker caching in first stage
COPY go.mod /app/go.mod
COPY go.sum /app/go.sum
# use our internal athens proxy
ENV GOPROXY=https://athens.xxx.yyy.zzz
ENV GONOSUMDB=bitbucket.yyy.zzz
WORKDIR /app
RUN go mod download
ARG GIT_HASH=""
ARG SERVICE_VERSION=""
# pass the git hash and service version to the next stage
ENV GIT_HASH=${GIT_HASH}
ENV SERVICE_VERSION=${SERVICE_VERSION}
# ------------------------------------------------------------------------
# The second stage container, for building the Go Binary
# ------------------------------------------------------------------------
FROM builder1 AS builder2
COPY . /app
WORKDIR /app/cmd/webapp
RUN echo "Service Version Stage 2: $SERVICE_VERSION"
RUN echo "Git Hash Stage 2: $GIT_HASH"
RUN GO_VERSION=$(grep -e "bitbucket.yyy.zzz/go/go " ../../go.mod | cut -d' ' -f2) &&\
SERVICE_VERSION=${SERVICE_VERSION:-$(grep -i "service_version: " ../../settings.yaml | cut -d ' ' -f2 | head -1)} &&\
CGO_ENABLED=0 go build \
--ldflags "-s -X bitbucket.yyy.zzz/go/go.sdkVersion=$GO_VERSION -X bitbucket.yyy.zzz/go/go.serviceVersion=$SERVICE_VERSION -X bitbucket.yyy.zzz/go/go.gitHash=$GIT_HASH" \
-o application_binary
FROM public.ecr.aws/docker/library/busybox:latest as busybox
# ------------------------------------------------------------------------
# The third stage container, for running the application
# ------------------------------------------------------------------------
FROM gcr.io/distroless/static
USER user:user
COPY --from=builder2 --chown=user:user /app/cmd/webapp/application_binary /app/app_binary
COPY --from=builder2 --chown=user:user /app/settings.yaml /app/settings.yaml
COPY --from=builder2 --chown=user:user /app/settings_prod.yaml /app/settings_prod.yaml
COPY --from=builder2 --chown=user:user /app/settings_lab.yaml /app/settings_lab.yaml
WORKDIR /app
ENTRYPOINT ["/app/application_binary"]
Thanks.
Hi @pganeshar , I was able to reproduce it and found the problem. Second stage used first one as base image and never honored the CLI args. Could you try this modified Dockerfile
it should make it work for your use-case.
# ------------------------------------------------------------------------
# The first stage container, for setting up and downloading dependencies
# ------------------------------------------------------------------------
FROM public.ecr.aws/docker/library/golang:1.18 as builder1
# only copy the go.mod and go.sum for better docker caching in first stage
COPY go.mod /app/go.mod
COPY go.sum /app/go.sum
# use our internal athens proxy
ENV GOPROXY=https://athens.xxx.yyy.zzz
ENV GONOSUMDB=bitbucket.yyy.zzz
WORKDIR /app
RUN go mod download
ARG GIT_HASH=""
ARG SERVICE_VERSION=""
# pass the git hash and service version to the next stage
ENV GIT_HASH=${GIT_HASH}
ENV SERVICE_VERSION=${SERVICE_VERSION}
RUN echo "Service Version Stage 1: $SERVICE_VERSION"
RUN echo "Git Hash Stage 1: $GIT_HASH"
# ------------------------------------------------------------------------
# The second stage container, for building the Go Binary
# ------------------------------------------------------------------------
FROM builder1 AS builder2
COPY . /app
WORKDIR /app/cmd/webapp
ARG GIT_HASH=""
ARG SERVICE_VERSION=""
# pass the git hash and service version to the next stage
ENV GIT_HASH=${GIT_HASH}
ENV SERVICE_VERSION=${SERVICE_VERSION}
RUN echo "Service Version Stage 2: $SERVICE_VERSION"
RUN echo "Git Hash Stage 2: $GIT_HASH"
RUN GO_VERSION=$(grep -e "bitbucket.yyy.zzz/go/go " ../../go.mod | cut -d' ' -f2) &&\
SERVICE_VERSION=${SERVICE_VERSION:-$(grep -i "service_version: " ../../settings.yaml | cut -d ' ' -f2 | head -1)} &&\
CGO_ENABLED=0 go build \
--ldflags "-s -X bitbucket.yyy.zzz/go/go.sdkVersion=$GO_VERSION -X bitbucket.yyy.zzz/go/go.serviceVersion=$SERVICE_VERSION -X bitbucket.yyy.zzz/go/go.gitHash=$GIT_HASH" \
-o application_binary
FROM public.ecr.aws/docker/library/busybox:latest as busybox
# ------------------------------------------------------------------------
# The third stage container, for running the application
# ------------------------------------------------------------------------
FROM gcr.io/distroless/static
USER user:user
COPY --from=builder2 --chown=user:user /app/cmd/webapp/application_binary /app/app_binary
COPY --from=builder2 --chown=user:user /app/settings.yaml /app/settings.yaml
COPY --from=builder2 --chown=user:user /app/settings_prod.yaml /app/settings_prod.yaml
COPY --from=builder2 --chown=user:user /app/settings_lab.yaml /app/settings_lab.yaml
WORKDIR /app
ENTRYPOINT ["/app/application_binary"]
Since I was able to reproduce and make it work I have closed the issue with the fix suggested above but if you feel something is unresolved we can re-open this :)
Another simple example for someone who wants to try this out with a simpler file.
FROM alpine as one
RUN echo hello
RUN touch hey
ARG CACHEBUST=1
ENV CACHEBUST=${CACHEBUST}
RUN echo "cache_burst_1: $CACHEBUST"
FROM one as second
RUN echo starting second stage
ARG CACHEBUST=1
ENV CACHEBUST=${CACHEBUST}
RUN echo "cache_burst_2: $CACHEBUST"
COPY --from=0 hey .
@pganeshar If solution in https://github.com/containers/buildah/issues/4367#issuecomment-1340585567 works for you I think this problem can be a really good blog material about busting inline cache in buildah using --build-arg
Description
When I run
podman build
command with --build-arg passed in, podman / buildah keep ignoring whats passed in and using the old values from its cache instead. I am running this inside a CentOS Stream release 8 rootful container with kernel overlay. I am using the latest podman (4.2.0) and buildah (1.27.0) versions.Steps to reproduce the issue:
podman buildx build --isolation chroot --jobs 0 --platform linux/amd64 -t test-image --network host --build-arg GIT_HASH=7189d88e75391042f0ba1da774781de720382c6f --build-arg SERVICE_VERSION=6.0.0 .
podman buildx build --isolation chroot --jobs 0 --platform linux/amd64 -t test-image --network host --build-arg GIT_HASH=c828b28b538a3ec49c861c2ecf4d2a028d6cf201 --build-arg SERVICE_VERSION=7.0.0 .
GIT_HASH=c828b28b538a3ec49c861c2ecf4d2a028d6cf201
andSERVICE_VERSION=7.0.0
and use the values from step 2 instead.Here is the Dockerfile I used:
Describe the results you received:
Describe the results you expected: I expected Podman to print the passed in values: Service Version: 7.0.0 Git Hash: c828b28b538a3ec49c861c2ecf4d2a028d6cf201
Output of
rpm -q buildah
orapt list buildah
:Output of
buildah version
:Output of
podman version
if reporting apodman build
issue:*Output of `cat /etc/release`:**
Output of
uname -a
:Output of
cat /etc/containers/storage.conf
: Note: I have left out all the commented out lines by "#" below:Output of
podman info --debug
: