This is a little post from our development department. We at CrowdPatent focus on innovation but furthermore, we are highly interested in providing good solutions.
TL;DR use a Makefile, a simple Docker Image and Go’s power.
Developing Go applications is easy, and this blog post is not about the pros or cons of Go in comparison to XYZ. But, this blog post is about using Go and Docker to get a great developing workflow.
Using Makefiles
Makefiles
are great, because every developer should know make
and furthermore it is available on (nearly) each and every system. I’ve copied this approach from the great hashicorp dev-team. Checkout the repositories of vault terraform or consul, all of them are setup with a Makefile included.
Structuring your Makefile
The following Makefile
is a good starting point. It manages the building and running of a Go application inside a Docker container.
default: test
setup:
go get github.com/mailgun/godebug
buildgo:
CGO_ENABLED=0 GOOS=linux go build -ldflags "-s" -a -installsuffix cgo -o main .
builddocker:
docker build --rm --tag=johannesboyne/godockersample .
buildp: buildgo builddocker
run: buildp
docker run \
-e HELLO=world \
-p 1337:1337 johannesboyne/godockersample
test:
go test -timeout=5s ./...
debug:
ifndef $(instrument)
godebug run ${gofiles}
else
godebug run -instrument=${instrument} ${gofiles}
endif
One could easily add another command like make push
to push the Docker Image into it's repository.
The Dockerfile
The Dockerfile
would look like:
FROM tianon/true
EXPOSE 1337
COPY certs/certs /etc/ssl/certs/
# other stuff
ADD main /
CMD ["/main"]
Why am I using the tianon/true
image, the scratch
image should be a little bit smaller? Yes, it is. But AWS Elastic Beanstalk (single instance docker deployment) forbids the usage of the scratch
image, because it is a reserved one. Because we're using Elastic Beanstalk for single Docker instances and the Amazon EC2 Container Service (ECS) for multi container instances, thus it is important for us to have the possibility to run docker images on EB as well.
But, the tianon/true
image is a very small one too, with it’s 125 bytes total
.
About the Dockerfile, Certs and Go.
Why adding a certs
directory? Some people have already seen this error x509: failed to load system roots and no roots provided
this happens if a Go App. tries to make a HTTPS request without having access to root SSL certificates. Fixing this issue is simple and one has two options, either mount the host’s certs to the container docker run -v '/etc/ssl/certs:/etc/ssl/certs' …
or, and I prefer the later, keep the root certificates in the image. Since, no one can be sure that the needed root certificates are available on the host which is running our container.
How big is such an image you may ask? The Docker image has around 4.56 MB
, and the Go binary 4.07 MB
for the following Go source:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World %s", "Test")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":1337", nil)
}
Thus, 89 % of the Docker image size exists because of the Go binary itself.