Recently I developed an application which had some random downtimes because of some unstable components that are under control by an external company. The operations department forced me to provide a http based health check route to support the uptime 24/7. I was free to use whatever I like, it just has to respond with HTTP code 200 if all checked application components are up and with 503 (Service unavailable) if there is some issue.
So the first step for me was to think about, what is the simplest technology I can use to setup a little Webhook that checks all my components and is easy to spin up with the current Docker based workflow. Some people might think about a simple AWS Lambda function, but sadly there’s no setup given at the moment. My choice fell on a Docker Multi stage build for a simple Golang Webhook, which was really fast to code and to build.
In general, I started from zero with coding in Golang, because my license for the JetBrains IDE called GoLand already expired and I don’t want to spend some money for it. I researched for an IDE and decided to go with Visual Studio Code and the corresponding Go Plugin (https://code.visualstudio.com/docs/languages/go). Setting this up on my MacBook took me 5 minutes and I was well prepared for the battle.
Dockerfile to build and run the healthcheck app
Next step was to setup a build environment for the Healthcheck app, which I planned to have in Docker. I created a Dockerfile with the following content to build the application and being able to run it:
FROM golang:alpine as builderRUN apk update && apk add git && apk add ca-certificatesADD . /build/WORKDIR /buildRUN go get -d -vRUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main .FROM scratchCOPY --from=builder /build/main /app/COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/WORKDIR /appCMD ["./main"]
First step is to take the latest golang image and add an alias called builder. To enable https support to check if our Web-Interface is available, we add ca-certificates. Further, we add the current host directory to the build dir in the container and use it as the current working directory(docker creates the dir automatically). Then we fetch all dependencies found in the Go code and compile it as a linux binary. Now we throw all build artefacts away and use the empty scratch image and copy the built binary into a app folder and the installed certs into its linux folder. If the container will be executed we just run the built linux binary with the Docker CMD statement as a blocking daemon.
Build a Go application
To build the application we need to create a main.go file and start with the following snippet:
Now we can build the application and run it with the following commands:
docker build -t healthcheck .
docker run --rm healthcheck
If you have Docker version 17.05 or higher everything should work as expected. You can see the build and execution output here:
The built image is pretty small as a result of the Multi-Stage Docker Build:
Without this Multi-Stage approach you get an image with a size of 822 MB!!! I was completely shocked!
Code the final Healthcheck
The build environment is prepared now and we can code the Healthcheck app which will have the following requirements:
- Listen on port 3001
- Have a route on /healthcheck
- Checking the database and the web frontend
- Give a json based Status code back
- Status code 503 if something is broken, 200 if everything is fine
The final result to achieve this looks like this:
Let’s cut this code into small pieces and bring some light into it.
- We need a struct which forms our json output called HealthState with the Status code and the corresponding error messages
- In the main function we setup our Webservice listening on port 3001 and on /healthcheck
- The function handleRequest initializes a empty struct and calls the functions to check our MySQL DB and the WebUI interface
- In the checkWeb function we make a GET-Request to our WebUI and check if get a successfull HTTP Status code back
- The checkMysql function tries to connect to the database and pings it to evaluate if everything is fine
- Last step is to check if we have collected some error messages in our struct and set the Status code as state in the struct. Finally we return the serialized json output.
If you build and run the docker container you get something like this if your services are down:
docker run --rm -p 3001:3001 healthcheckcurl localhost:3001/healthcheck
In case of success you get state 200 and ErrorMessages: null
Hopefully this post helped you to figure out how to build a small and useful Go application in almost no time. Have fun coding Golang!