1

I am trying to build my first Dockerfile for a Go application and use DroneCI to build pipeline.

The DroneCI configuration looks as follows:

kind: pipeline
type: docker
name: Build auto git tagger

steps:
  - name: test and build
    image: golang
    commands:
      - go mod download
      - go test ./test
      - go build ./cmd/git-tagger

  - name: Build docker image
    image: plugins/docker
    pull: if-not-exists
    settings:
      username: 
      password: 
      repo: 
      dockerfile: ./build/ci/Dockerfile
      registry: 
      auto_tag: true

trigger:
  branch:
    - master

I have followed the structure convention from https://github.com/golang-standards/project-layout:

enter image description here

The Dockerfile looks as follows so far:

FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/

The next step is, to copy the GO application binary into the container and here is the question, where to put the compiled binary? At the moment, it puts into the project folder.

softshipper
  • 32,463
  • 51
  • 192
  • 400
  • 4
    It normally doesn't matter. The key is that you know where it is, so you can execute it. – Jonathan Hall Apr 23 '20 at 13:53
  • How can I specify the output folder for binary? – softshipper Apr 23 '20 at 14:01
  • 3
    [set GOBIN, or use the `-o` flag](https://golang.org/cmd/go/#hdr-Compile_and_install_packages_and_dependencies). The binary has to be in the `package` subtree (where Dockerfile is) for [COPY](https://docs.docker.com/engine/reference/builder/#copy) to work. – Peter Apr 23 '20 at 14:30

2 Answers2

2

You can specify output directory and file name wit go build -o flag. For example:

go build ./cmd/git-tagger -o ./build/package/foo

Then edit your Dockerfile:

  1. Load the binary you've got with COPY or ADD

  2. Execute it with ENTRYPOINT or CMD

P.S you specified Dockerfile path as ./build/ci/Dockerfile in your config. But it's in package dir on the screenshot.

Don't forget that repository you linked is just smb's personal opinion and Go doesn't really enforce you to any structure, it depends on your company style standards or on your preferences. So it's not extremely important where to put the binary.

Russiancold
  • 1,179
  • 1
  • 11
  • 26
1

If the Go app doesn't need anything from the Docker image itself, e.g.: bash, ls, or really any other OS-level commands, I usually do a multi-stage build onto a Scratch image. Look at the RUN CGO_ENABLED=0 ... line below to see how the app is being compiled as a statically linked binary file. Golang apps built this way should run in Scratch containers without any other image dependencies, so an empty Scratch image works well to keep your container size very small. My Go binary was 25.5MB and the final Docker image based on Scratch was only 26.6MB

NOTE: In your scenario, since your directory structure is different than mine, you'll need to add a WORKDIR command just before the RUN CGO_ENABLED=0 ... line, something like: WORKDIR /go/src/app/cmd/git-tagger (where we've copied the repo files to the first stage image) so that you're running the go install command from the same directory as main.go.

Example multi-stage Dockerfile for Go binaries:

# Stage 1: Use base Alpine image to prepare our binary, label it 'app'
FROM golang:alpine as app
# Add golangdocker user and group so that the Docker process doesn't run as root
RUN addgroup -S golangdocker \
    && adduser -S -u 10000 -g golangdocker golangdocker
# Change to the correct directory to hold our application source code
WORKDIR /go/src/app
# Copy all the files from the base of our repository
COPY . .
# Compile the application to a single statically-linked binary file
RUN CGO_ENABLED=0 go install -ldflags '-extldflags "-static"' -tags timetzdata

# Stage 2: Use the Docker Scratch image to copy our previous stage into
FROM scratch
# Grab necessary certificates as Scratch has none
COPY --from=alpine:latest /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Copy our binary to the root of the Scratch image (note: --from=app, the name we gave our first stage)
COPY --from=app /go/bin/golangdocker /golangdocker
# Copy the user from the first stage so that we don't run the process as root
COPY --from=app /etc/passwd /etc/passwd
# Change to the non-root user
USER golangdocker
# Run our app
ENTRYPOINT ["/golangdocker"]

More context can be found here: https://mwiater.github.io/golangdocker/docs.html#docker-docker-build-notes

MJW
  • 11
  • 1