In this blogpost I would like to show you some basic Docker setup I have been using so far in my Go projects. We will be looking at multi-stage Docker builds and how to utilize docker-compose.
In a typical project setup in Go you would most probably start with a file main.go. In addition to that I usually add a Dockerfile for building a Docker image and a docker-compose file to easily spin up my dependencies like databases and queues.
To start we create a new folder to work in and initiales the folder as a Go module.
I would also like to reuse the webserver from my last blogpost and run it in our Docker setup which we are about to create in this blog post. For convenience I also put the code in below codeblock.
Please download this file and put it as main.go in our new folder go-docker-webserver. With all of this in place we should now be able to start our webserver using go run. So far all good, but now it is time to come to the essence of this blogpost.
Docker
Now I would like to add a Dockerfile which is able to compile the binary and package that as a docker image. I expect you to have some basic Docker knowledge as I will not discuss all the Docker specifics in detail. I have previously written some blogs on Docker which you can find here. These include the following:
Today I will use a multi stage build process to get an image which is as small as possible for our Go binary to run. In my Docker builds I always prefer the alpine images as they are small, quick to download and consume not to much storage on my development machine. This isn't mandatory, just my personal preference.
In below Dockerfile I defined the first stage of this build as builder utilizing the golang:1.12-alpine image. To fix cgo usecases for the docker image I have added some missing tools to the alpine image. After we COPY our code from our workspace into the build folder within the alpine image. Once that is done we run go get which will download our dependencies as they are defined in the go.mod file. Once that is done we can build the binary using go build. Now first have a peek at the first half of below Dockerfile before I will continue the further explanation.
In the second half the above Dockerfile we are about to build a Docker image based on the scratch image. This is an image without OS and no further things installed. It is the smallest image you will be able to start with. As we have compiled our binary with some specific options it will not have any dependencies, meaning we can run it as bare bone as possible. -extldflags "-static" means do not link against shared libraries.
Let's give this a quick try.
Also have a peek at the image size and be amazed how small it is :).
Docker-compose
Now lets also have a look on a docker-compose file. In this example I will first show you how we can utilize this to have an easier way to build our Docker image using docker-compose.
To run our application using docker-compose we can now utilize this using the following command.
As you can see this will save us some time to build a new image and run it.
In order to have a nice example on how to further use docker-compose I would like to add another container with an external tool to have a nice showcase. So lets add Traefik to our setup which is an awesome.
Simplifynetworking complexity while designing, deploying, and running
applications.
Treafik has a nice option to have it configure itself by using Docker labels. In our Docker example I will use this approach to configure Traefik. Traefik needs access to the Docker sock to be able to do these configurations Therefore we will add a volumes so the Traefik container will have access to the sock. Furthermore we will boot Traefik with the --api and --docker option.
As you can see we have also add a label on our web container to configure it using a Traefik frontend rule. With above rule we make our service available to the outside world at http://localhost/api/v1/awesome/. Therefore I also removed the previously public exposed port 5000. This means we can not directly access our webserver anymore from outside the Docker network.
Now lets boot our Docker environment again. This time I will also use the -d parameter to run it in the background and not block our terminal.
Using docker-compose logs -f web we can for example still get the logs from our web container whenever we need that.
Now lets have a look at http://localhost:8888 which is the management interface of Traefik. As you can see there is one frontend to our web container with currently one backend. We can now scale our web container to run it as multiple instances using docker-compose. As Traefik is utilizing the Docker sock it will automatically pick up the new instances available. All requests will now be loadbalanced on these containers. Let's give this a try.
Using above we will scale our web container to 3 instances. In the Traefik Dashboard you will see there are 3 backends available for your web deployment. When you make requests to our Traefik endpoint which we configured for our web deployment http://localhost/api/v1/awesome/ you will see all still works as expected and gives us the HTTP 200 OK response. To see that there is actual load balancing happening you can see multiple containers are booted using following command, and you can check the logs to see requests are showing up in the different instances.
Just invoke the request a few times and you will see the requests are every time handled by different instances of the web container. When you try to access any url different then the configured path you will get a HTTP 404 response from Traefik as these endpoints have no backend.
Now you can do similar for defining you dependencies like databases in the docker-compose file to have a fully fledged local development environment. A fully working example can be downloaded from here. Thanks for reading, please share with your friends and colleagues and see you next time.
You have disabled cookies. To leave me a comment please allow cookies at functionality level.
In a previous blogpost I wrote how to create a Webserver in Go with graceful shutdown. This time I want to show you a more improved version which you can utilize better in your projects as it can be used as a drop in server.go file in your project where I also make use of some popular high performing libraries.
In previous example I coded the full example in main.go. Although nothing wrong with that I learned while building microservices for a while it would be more convenient for me if I could…
In this blog post I would like to cover the recently released Elasticsearch 7.0-rc1 Go client for Elasticsearch. In this blogpost I want to show you a small example with a simple Docker setup using to build a Elasticsearch cluster.
In my previous blogpost I covered some Docker tips and tricks we will utilize again in this blog post.
Initializing your project
To start with we first have to create a project folder. In this folder we will have to initialize our Go module, add our Dockerfile and…
In this blogpost I want to show you how you can make a http webserver in Go with gracefull shutdown. Using this approach you allow the server to clean up some resources before it actually shuts down. Think about finishing a database transaction or some other long operation. We will be using the things we learned in my blogpost on concurency. So expect to see channels and go routines as part of the solution.
When I create new http servers I usually start with an commandline flag to provide the p…
In this blog I would like to zoom in on Interfaces and type assertions in Go. Compared to language like c# and Java implementing interfaces works slightly different. In the remainder of this blog I want to give you a bit of theory and practical usecases. In case this is your first time working with Go you might want to check out this blog which shows you how to setup your development environment including a small hello world.
The empty interface (interface{}) is an interface which defines zero…