Create a Docker image for GO
Using Codefresh pipelines
Codefresh can work with Go projects of any version using built-in modules or any other dependency mechanism.
The example golang project
You can see the example project at https://github.com/codefresh-contrib/golang-sample-app. The repository contains a simple Golang web application including unit tests. There are 3 Dockerfiles available:
- Simple Dockerfile (with old Go version that requires
GOPATH
building) - Dockerfile with Go modules (optimized for Docker caching)
- Multi-stage Dockerfile (with Go modules and unit tests)
Let’s see these workflows in order.
Simple Docker image pipeline
The most simple pipeline that you can create is just two steps:
- A clone step to fetch the code
- A build step to create a Docker image
codefresh.yml
version: '1.0'
steps:
main_clone:
title: Cloning main repository...
type: git-clone
repo: 'codefresh-contrib/golang-sample-app'
revision: master
git: github
MyAppDockerImage:
title: Building Docker Image
type: build
image_name: my-golang-image
working_directory: ./
tag: full
dockerfile: Dockerfile
Once you run this pipeline Codefresh will create a Docker image for the Golang application:
The big advantage of this workflow is that the Dockerfile you use can define any Go version and dependency tool. As long as the Dockerfile is self-contained (i.e. it compiles GO on its own), the pipeline will work as expected.
In the example application, the simple (unoptimized) Dockerfile has an old Go version that still requires GOPATH
folders.
Dockerfile
FROM golang:1.10
# Set the Current Working Directory inside the container
WORKDIR $GOPATH/src/github.com/codefresh-contrib/go-sample-app
# Copy everything from the current directory to the PWD (Present Working Directory) inside the container
COPY . .
# Download all the dependencies
RUN go get -d -v ./...
# Install the package
RUN go install -v ./...
# This container exposes port 8080 to the outside world
EXPOSE 8080
# Run the executable
CMD ["go-sample-app"]
Run unit tests as part of the pipeline
If you want to run Go specific steps in your pipeline, you can use freestyle steps with any GO image that you want. If your GO application is using GO modules, this is even easier as you don’t need to place the application into a specific GOPATH compliant directory first.
This pipeline is running unit tests as a separate step and then builds the docker image.
codefresh.yml
version: '1.0'
stages:
- checkout
- test
- build
steps:
main_clone:
title: Cloning main repository...
type: git-clone
stage: checkout
repo: 'codefresh-contrib/golang-sample-app'
revision: master
git: github
MyUnitTests:
title: Unit test
stage: test
image: 'golang:1.12'
commands:
- go test -v
MyAppDockerImage:
title: Building Docker Image
type: build
stage: build
image_name: my-golang-image
working_directory: ./
tag: modules
dockerfile: Dockerfile.mod
If the unit tests fail, then the docker image will never be created (Codefresh automatically stops a pipeline when there is an error).
Notice that in this case we have added module support in the Go application. The new Dockerfile is the following:
Dockerfile
FROM golang:1.12-alpine
RUN apk add --no-cache git
# Set the Current Working Directory inside the container
WORKDIR /app/go-sample-app
# We want to populate the module cache based on the go.{mod,sum} files.
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
# Build the Go app
RUN go build -o ./out/go-sample-app .
# This container exposes port 8080 to the outside world
EXPOSE 8080
# Run the binary program produced by `go install`
CMD ["./out/go-sample-app"]
The Dockerfile will also automatically take advantage of the Codefresh distributed docker cache.
Create a multi-stage Docker image for GO
Especially with Go applications, the recommended way to create Docker images is with multi-stage builds. This makes the resulting Docker image as compact as possible.
You can also embed unit tests in the Docker creation process, which guarantee the correctness of image (integration tests are best kept in the pipeline).
Here is the new Dockerfile:
Dockerfile
FROM golang:1.12-alpine AS build_base
RUN apk add --no-cache git
# Set the Current Working Directory inside the container
WORKDIR /tmp/go-sample-app
# We want to populate the module cache based on the go.{mod,sum} files.
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
# Unit tests
RUN CGO_ENABLED=0 go test -v
# Build the Go app
RUN go build -o ./out/go-sample-app .
# Start fresh from a smaller image
FROM alpine:3.9
RUN apk add ca-certificates
COPY --from=build_base /tmp/go-sample-app/out/go-sample-app /app/go-sample-app
# This container exposes port 8080 to the outside world
EXPOSE 8080
# Run the binary program produced by `go install`
CMD ["/app/go-sample-app"]
Codefresh has native support for multi-stage builds. The pipeline is the same as the first one with just two steps.
codefresh.yml
version: '1.0'
steps:
main_clone:
title: Cloning main repository...
type: git-clone
repo: 'codefresh-contrib/golang-sample-app'
revision: master
git: github
MyAppDockerImage:
title: Building Docker Multi-stage Image
type: build
image_name: my-golang-image
working_directory: ./
tag: multi-stage
dockerfile: Dockerfile.multistage
You should see a much smaller Docker image at the end.
Viewing Docker images
If you look at your Docker registry dashboard created the advantages of the multi-stage build are very clear:
We recommend using Go modules and multi-stage builds in your Go projects.
Related articles
Codefresh YAML for pipeline definitions
Steps in pipelines
Creating pipelines
How Codefresh pipelines work