How to build a go executable that doesn't link to musl libc
Asked Answered
C

2

8

So:

The official Go build container is based on Alpine.

Alpine uses musl as libc instead of glibc.

I need to build a Go executable in a container that can be run on Ubuntu, which uses glibc.

How do I either

  1. Make the official GoLang build container use glibc or
  2. Build my GoLang project on an Ubuntu based container

I can't use the Disable CGO solution, as my Go code is a FUSE driver, which requires CGO

Conciliatory answered 4/2, 2019 at 21:46 Comment(10)
hub.docker.com/r/frolvlad/alpine-glibc Get this image as base image, run the same steps in the golang's official dockerfile (github.com/docker-library/golang/blob/master/…) to create your builder image, compile your code, profit.Embraceor
Hmm it's a good start, but now I have to configure that image to be ready to build my Go code...Conciliatory
"I have to configure that image to be ready to build my Go code" Yes, you should attempt that configuration.Telemetry
Actually, golang:latestis based on Debian Stretch. Basically, all you should need to do is to leave out the alpine modifier in the FROM directive of your build stage.Smoothtongued
Another option is installing musl-dev on Ubuntu (and adding it as a depdendency of your app)Volga
So I tried the alpine-glibc approach, but the binary still shows as linked to musl when I test with ldd. Off to try the alpine-latest. Other alternatives are: 1) building it locally on my host machine then using COPY instead of using a builder image or 2) take the setup for the golang builder image and transplant it to an Ubuntu image.Conciliatory
@Markus golang:latest still links to musl, no joy. As for valiano's suggestion, I don't have that kind of control over the configuration of the node, so that's not an option. At this point I think the simplest option is to not use a build container, and just copy the binary from my dev machine. After all I can build there, as this is where I develop and debug, and it's a vanilla Ubuntu machine.Conciliatory
Please add your Dockerfile to your question by editing it.Smoothtongued
that's actually not true the default golang:latest docker image is based on debian and can produce glibc based binaries. You need to specify what you're calling the official go Build container to clarify your question.Flagwaving
Note when the question was asked...Conciliatory
F
3

The golang:latest image is based on debian bullseye. You don't need anything else than using this image to build your binary so that it can be run as is on ubuntu.

Just start your dockerfile with this line instead of what you're currently using.

FROM golang:latest
Flagwaving answered 21/12, 2022 at 8:52 Comment(1)
At the time I asked the question 3 years ago tho… 😀Conciliatory
A
-1

Start with a Ubuntu base image and setup golang in there. Add dependencies as you discover them. If you don't care very much about the version of go, then the official packages from Ubuntu will do, and you don't have to download the official go release.

This works for us (linux / mac users):

Dockerfile

FROM ubuntu:20.04 as base

ARG BUILDPLATFORM
ARG TARGETPLATFORM
RUN echo "Building for $TARGETPLATFORM on $BUILDPLATFORM"

# Enable super fast apt caches for use with buildkit --mount=type=cache
RUN rm -f /etc/apt/apt.conf.d/docker-clean; \
    echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache

# Install dependencies for building
# --mount docs: https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
    --mount=type=cache,sharing=locked,target=/var/lib/apt \
    apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y \
    awscli \
    curl \
    gcc \
    git \
    liblxc-dev \
    liblxc1 \
    lxc-utils \
    make \
    parallel \
    pkg-config \
    upx-ucl \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

ARG GOVERSION=1.15
ARG TARGETARCH
ADD https://dl.google.com/go/go${GOVERSION}.linux-${TARGETARCH}.tar.gz /tmp/golang.tar.gz
RUN cd /opt && tar zxf /tmp/golang.tar.gz && rm /tmp/golang.tar.gz

# Set timezone to UTC
RUN echo 'UTC' > /etc/timezone && \
    dpkg-reconfigure -f noninteractive tzdata

# Set up go environment variables
ENV GOPATH=/opt/gopath
ENV GOCACHE=/opt/gocache
ENV GOROOT=/opt/go
ENV PATH="$GOROOT/bin/:$PATH"

# Install go-bindata, it is used when building drone
# go get installs to: $GOPATH/src and $GOPATH/bin
# We then move the binary to $GOROOT so we can change GOPATH in our builds
RUN go get -u github.com/go-bindata/go-bindata/... && \
    mv $GOPATH/bin/go-bindata $GOROOT/bin/go-bindata

# Install https://staticcheck.io for statistical analysis of go code
RUN go get honnef.co/go/tools/cmd/staticcheck && \
    mv $GOPATH/bin/staticcheck $GOROOT/bin/staticcheck

# Install https://github.com/kyoh86/richgo for pretty color output
RUN go get -u github.com/kyoh86/richgo && \
    mv $GOPATH/bin/richgo $GOROOT/bin/richgo

# Set up working dir for this project
WORKDIR /workspace

#########
# TESTS #
#########

# Install dependencies for running tests
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
    --mount=type=cache,sharing=locked,target=/var/lib/apt \
    apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y \
    pigz \
    bind9-host \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

# Setup git for tests
RUN git config --global user.email "[email protected]" && \
    git config --global user.name "Test Project"


FROM base as ci
ENV GOPATH=/workspace/.cache/gopath
ENV GOCACHE=/workspace/.cache/gocache

RUN mkdir -p /root/.parallel; touch /root/.parallel/will-cite


#############
# COPY CODE #
#############
#COPY go.mod go.sum /workspace/
#WORKDIR /workspace
#RUN go mod download
#COPY ./ /workspace/


# A note on folders
# /opt/go is where go itself is installed
# /opt/gopath is where `go get` installs packages
# /opt/gopath is also what `go mod` uses for package cache
# /builds is where the current project folder should be mounted

To run it:

docker.sh

docker build -q --target base -t ubuntu-go . ||
docker build    --target base -t ubuntu-go .

docker run -i -t --rm --init --env-file=.env \
  --volume /src/myproject:/workspace:delegated \
  --volume /src/myproject/.docker/gopath:/opt/gopath:delegated \
  --volume /src/myproject/.docker/gocache:/opt/gocache:delegated \
  ubuntu-go "$@"

e.g.

$ bash -x docker.sh bash -c 'seq 1 4 ; whoami'
+ docker build -q --target base -t ubuntu-go .
sha256:03eaf19625efd7f5760d14ea0d741d4454a9f280cd70c5c600bea63bbca70984
+ docker run -i -t --rm --init --env-file=.env \
  --volume /src/myproject:/workspace:delegated \
  --volume /src/myproject/.docker/gopath:/opt/gopath:delegated \
  --volume /src/myproject/.docker/gocache:/opt/gocache:delegated \
   ubuntu-go bash -c 'seq 1 4 ; whoami'
1
2
3
4
root

Our ldd deps look like this:

 linux-vdso.so.1 (…)
 libpthread.so.0 => /lib64/libpthread.so.0 (…)
 liblxc.so.1 => /usr/lib64/liblxc.so.1 (…)
 libutil.so.1 => /lib64/libutil.so.1 (…)
 libdl.so.2 => /lib64/libdl.so.2 (…)
 libc.so.6 => /lib64/libc.so.6 (…)
 /lib64/ld-linux-x86-64.so.2 (…)
 libcrypto.so.1.1 => /usr/lib64/libcrypto.so.1.1 (…)
 libseccomp.so.2 => /usr/lib64/libseccomp.so.2 (…)
 libcap.so.2 => /lib64/libcap.so.2 (…)
 libgcc_s.so.1 => /usr/lib/gcc/x86_64-pc-linux-gnu/9.4.0/libgcc_s.so.1 (…)
Acanthoid answered 31/10, 2021 at 10:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.