At Soluto we are working on an open-source project named Tweek. One of its components is a proxy server that we decided to implement in Go.

In order to make the Docker  image of the proxy server lighter we built it from scratch. But, while building a container from scratch has its advantages, it also comes with a big setback.

# Stage 1: Build executable
FROM golang:1.9.2 as buildImage

WORKDIR /go/src/github.com/Soluto/golang-docker-healthcheck
COPY main.go .

RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o server

# Stage 2: Create release image
FROM scratch as releaseImage

COPY --from=buildImage /go/src/github.com/Soluto/golang-docker-healthcheck/server ./server

ENV PORT=8080
EXPOSE $PORT

ENTRYPOINT [ "/server" ]

 

The Problem

We usually add a HEALTHCHECK instruction to our Dockerfiles and then check the status of containers with the docker inspect command. Usually the health check performs an http request to server endpoint, and if it succeeds the server is considered to be in healthy condition. In Linux-based containers we usually do it with the curl or wget command. The problem is that in containers built from scratch there are no such commands. So we needed a simple solution that wouldn’t take too much time or effort.

Solution

We decided to add a tiny executable to the container that will do the work. Therefore we added a new package that contains a file with several lines…

_, err := http.Get(fmt.Sprintf("http://127.0.0.1:%s/health", os.Getenv("PORT")))
if err != nil {
 os.Exit(1)
}

… and built and copied the executable to the image. Finally, we added the HEALTHCHECK instruction to the Dockerfile

...
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o server
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o health-check "github.com/Soluto/golang-docker-healthcheck/healthcheck"

# Stage 2: Create release image
FROM scratch as releaseImage

COPY --from=buildImage /go/src/github.com/Soluto/golang-docker-healthcheck/server ./server
COPY --from=buildImage /go/src/github.com/Soluto/golang-docker-healthcheck/health-check ./healthcheck

HEALTHCHECK --interval=1s --timeout=1s --start-period=2s --retries=3 CMD [ "/healthcheck" ]
...

 

As a result we have two executables in the docker container: the server and the health-check utility.

Here there is the github repository with the whole example project.

Conclusion

In this repository we demonstrated how to implement a health-check for a server implemented in Go, for a Docker container built from scratch.

If you want to see a real-world application, please visit the Tweek project.

And what’s great is, in addition to http requests, this approach can be used for all kinds of checks as well. Feel free to try it out!