We’ve recently had some problems with a Go application that was running inside a Docker container in a very big Docker Compose setup.
After getting fed up with writing console prints and rebuilding the Docker image for that container and spinning up all the containers to debug things, we started investigating how we could speed up our debugging process.
Now if you read through the pages linked above you will find out how to install and setup all these things. It’s pretty straight forward. The Docker part, however, is not. As such, I will show you a basic Go application which mimics what we had to deal with and how to set up debugging for it.
The following is the main.go of our app. It will connect to a Redis server, set and get a value.
As you can see, it relies on the Redigo package, so make sure you get it and place it in your vendor folder.
To make sure you have everything setup the right way, go ahead and build it locally by running :
go build -o main main.go
If you run the application built this way, it will fail of course, because you need to connect to Redis. I’ve set the hostname for the server to redis which will point to an IP on the docker-machine when we docker-compose up.
Now we have to build the image for this application.
ENV GOPATH /opt/go:$GOPATH
ENV PATH /opt/go/bin:$PATH
ADD . /opt/go/src/local/myorg/myapp
RUN go get github.com/derekparker/delve/cmd/dlv
RUN go build -o main main.go
When this image will be built, it will basically copy the application code, set up the environment and build the Go application. The application’s entrypoint will be the main executable that will be built. We also install the Delve command line tool but we won’t use it if we run a container from this image directly (i.e. docker run).
Note the GOPATH variable and the path to which we copy our code. This path is very important for Delve and our debug configuration.
The Docker Compose file
Now that we have the Dockerfile to build the image, we have to define the docker-compose.yml file. Here, however we will overwrite the entrypoint for the container to launch Delve. Also the code that we copied will be replaced with a volume that will point to the code on the host machine, and we will also remove some security constraints that prevent Delve from forking the process.
Essentially, for the context I mentioned above we try not to touch the base image for the application since it might get accidentally pushed to the Docker Hub with debugging parameters. So in order to avoid that we have our Docker Compose process override the image with what we need to go about debugging.
It’s here that we introduce the Redis server dependency we have. Note that for the myapp container we’ve exposed the ports that the Delve command line tool listens to.
So to see that everything is working, you can now run :
docker-compose up --build
This will build the image and start up the redis and myapp containers.
You should see the following output coming from the myapp container:
myapp_1 | 2016/12/15 08:50:39 server.go:71: Using API v1
myapp_1 | 2016/12/15 08:50:39 debugger.go:65: launching process with args: [/opt/go/src/local/myorg/myapp/debug server]
myapp_1 | API server listening at: [::]:2345
Which means that the Delve command line tool compiled our Go code into a debug executable, started it, and it’s listening for remote connections to the debugger on port 2345.
Now we just have to set up our launch.json config in the .vscode folder of our project.
You can Next and Continue, look at the callstack, see the locals, view contents of specific variables, etc.
I hope this proves to be as useful to you as it did for us. The tools mentioned in this post really save us a heap of trouble.
We really have to thank the open source community that brought us these tools. They are the real heroes.
IntelligentBee respects and applies the new policy regarding personal data protection as well as the changes proposed by European Regulation (EU) 2016/679. Before continuing to browse our website, please take time to read and understand the contents.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.