Skip to content
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

Static linking with Alpine image vs. dynamic linking with Debian bullseye image #453

Closed
tim-hilt opened this issue Feb 21, 2023 · 8 comments

Comments

@tim-hilt
Copy link

Hi there! I recently came across a strange issue, when trying to build a Go binary with the official golang:1.20 / golang:1.20.0-bullseye image. I wanted to run the image with distroless/static-debian11, but on execution, I only saw the message file not found.

The error can be reproduced with the following Dockerfile (found in this repository: https://github.com/tim-hilt/debug-go-distroless):

# Builder
FROM golang:1.20 AS builder
WORKDIR /go/src/app
COPY . .
RUN go build -ldflags="-w -s"

# Runner
FROM gcr.io/distroless/static-debian11
COPY --from=builder /go/src/app/app /
CMD ["/app"]

full reproduction:

git clone [email protected]:tim-hilt/debug-go-distroless.git
cd debug-go-distroless
docker build -t test . && docker run test

Strangely I could execute the binary if I built the image with golang:1.20-alpine, which is counter-intuitive, since the distroless-image as well as golang:1.20 are based on Debian 11.

I opened up an issue on the distroless-repository (GoogleContainerTools/distroless#1227). One maintainer pointed out, that the binary was statically linked on Alpine, but dynamically linked when built on Debian.

How is this possible?

@tianon
Copy link
Member

tianon commented Feb 22, 2023

If you run ldd on the binary after you build it (in both cases - using the appropriate ldd from the relevant build environment), what's the result?

@tianon
Copy link
Member

tianon commented Feb 22, 2023

My hunch is that in Alpine, it's default to disabling CGO due to having no C compiler, so it generates a more portable executable than it does in Debian where it can default to cgo. The next test would be to add ENV CGO_ENABLED=0 to both builds and see if that changes your result.

@tianon
Copy link
Member

tianon commented Feb 22, 2023

Also, in the future, these sorts of questions/requests would be more appropriately posted to a dedicated support forum, such as the Docker Community Slack, Server Fault, Unix & Linux, or Stack Overflow.

@tim-hilt
Copy link
Author

For golang:1.20:

ldd debug-go-distroless
	linux-vdso.so.1 (0x00007ffd4794a000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007ff7d8ab5000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff7d8a93000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff7d88be000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ff7d8ad5000)

For golang:1.20-alpine:

ldd debug-go-distroless
    /lib/ld-musl-x86_64.so.1: debug-go-distroless: Not a valid dynamic program

I don't really know about linking, but I assume that the binary was statically linked in the case of alpine.

If I use the following Dockerflie:

FROM golang:1.20 AS builder
WORKDIR /go/src/app
ENV CGO_ENABLED=0
COPY . .
RUN go build -ldflags="-w -s"

It links statically either way. Thanks for the hints!

@tim-hilt
Copy link
Author

I'm sorry if this was the wrong place to ask for help. I generally know if I should file a bug or ask on a support-channel. In this case though, I was sure that this is a bug.

It's not intuitive to figure out that go build links differently if a certain executable is found on $PATH. I never saw that in any language before and I've worked with quite a few.

@atc0005
Copy link

atc0005 commented Feb 22, 2023

@tim-hilt See also:

@wader
Copy link

wader commented Feb 23, 2023

@tim-hilt Just a note about ldd. I've seen some versions that don't understand so called "static PIE" binaries which i think alpine's toolchain can produce, those binaries are dynamic but has no external dependencies

@tim-hilt
Copy link
Author

Thanks for the heads up @atc0005 @wader.

In my use-case the binaries that were compiled on alpine work. Making static building explicit via a flag to go build would make muuuch more sense though. I just subscribed to the issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants