7

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

  • 1
    https://hub.docker.com/r/frolvlad/alpine-glibc/ Get this image as base image, run the same steps in the golang's official dockerfile (https://github.com/docker-library/golang/blob/master/Dockerfile-alpine.template) to create your builder image, compile your code, profit. – atakanyenel Feb 04 '19 at 21:53
  • Hmm it's a good start, but now I have to configure that image to be ready to build my Go code... – Anders Sewerin Johansen Feb 04 '19 at 21:59
  • 1
    "I have to configure that image to be ready to build my Go code" Yes, you should attempt that configuration. – Jonas G. Drange Feb 05 '19 at 06:17
  • 3
    Actually, `golang:latest`is 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. – Markus W Mahlberg Feb 05 '19 at 06:23
  • Another option is installing `musl-dev` on Ubuntu (and adding it as a depdendency of your app) – valiano Feb 05 '19 at 13:41
  • 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. – Anders Sewerin Johansen Feb 05 '19 at 15:45
  • @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. – Anders Sewerin Johansen Feb 06 '19 at 15:56
  • Please add your Dockerfile to your question by editing it. – Markus W Mahlberg Feb 06 '19 at 18:24
  • 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. – davidriod Dec 21 '22 at 08:52
  • Note when the question was asked... – Anders Sewerin Johansen Jan 01 '23 at 20:20

2 Answers2

2

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
davidriod
  • 937
  • 5
  • 14
-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 "test@project.com" && \
    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 (…)