Anders Sewerin Johansen
Anders Sewerin Johansen

Reputation: 1666

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

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

Upvotes: 8

Views: 9049

Answers (2)

davidriod
davidriod

Reputation: 1057

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

Upvotes: 3

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 (…)

Upvotes: -1

Related Questions