Vscode debug GoLang micro-services in Docker

UULU
7 min readMay 8, 2019

中文版

I prefer adding print, rather than using break point.

But I can’t avoid it while some complex bug occurred. Or have to trace it into the third library codes.

I am developing micro-services in GoLang now. The services must running in Docker containers. At this time, debug will be a difficult stuff. We have to do it with Remote Debug Model.

  • vscode — My favorite Coding IDE
  • ms-vscode.go — The Go Plugin for vscode
  • delve — A Debug tool for GoLang, and supports Remote Debug Model.
  • Makefile — A Set of custom commands
  • alpine — A mini Docker image, only 5MB

Start Up Container

Compile and run Docker Container with the files Makefile & Dockerfile . I compile my service and dlv to Linux binary files, because my OS is Mac. Then copy them into alpine base container.

  1. Configure Makefile
  • Build GoLang file with argument -gcflags “all=-N -l” for supporting Debug.
  • Run container with arguments -security-opt=”seccomp=unconfined” — cap-add=SYS_PTRACE for closing security limit of Linux.
  • Run with -p 20000:20000 for exposing dlv server port, which vscode will to connect to.
NAME := account
IMAGE := $(NAME)-service
# Build image
build-debug:
# Clean unusing images (With the line only you need)
docker image prune -f
# Build dlv for Linux
GOOS=linux GOARCH=amd64 go build github.com/go-delve/delve/cmd/dlv
# Build program for Linux
GOOS=linux GOARCH=amd64 go build -gcflags "all=-N -l"
# Build Docker image with certain Dockerfile
docker build -f Dockerfile.debug -t $(IMAGE) .
# Clean the compiled files built just moment
rm -f $(NAME) dlv
# Run container
run-debug:
# my-docker-network is My docker network for the micro-services
docker run --rm --name $(IMAGE) \
--network my-docker-network \
--security-opt="seccomp=unconfined" --cap-add=SYS_PTRACE \
-p 20000:20000 \
$(IMAGE)
debug: build-debug run-debug

2. Configure Dockerfile.debug

The Docker ENTRYPOINT is ./dlv --listen=:20000 --headless=true --api-version=2 --log=true exec ./account

  • ./represents the WORKDIR (/app),dlv and account were in it.
  • --listen=:20000 The port for dlv server binding
  • --headless=true Just debug server, without UI
  • --api-version=2 The version of dlv server’s API
  • --log=true Open dlv server’s log output, or only application’s
  • exec ./account Execute application for Debug
FROM alpine:3.9# For supporting https request
RUN apk add ca-certificates
RUN mkdir /app
WORKDIR /app
ADD dlv account config.yml ./ENTRYPOINT ["./dlv", "--listen=:20000", "--headless=true", "--api-version=2", "--log=true", "exec", "./account"]

3. Run Container

You will find that no any log of application’s. Because the dlv server only prepare to debug, then waiting the debug request from client, such as vscode.

$ make debug...2019-05-08T13:03:23Z info layer=debugger launching process with args: [./account]API server listening at: [::]:20000

Vscode Configuration and Break point

1. Create config section for launch debug

  • mode Set to remote model
  • host port Set to dlv server address
  • program Set to the main GoLang file of the application

launch.json

{
"version": "0.2.0",
"configurations": [
{
"name": "Remote Debug",
"type": "go",
"request": "launch",
"mode": "remote",
"remotePath": "",
"port": 10001,
"host": "127.0.0.1",
"program": "${workspaceRoot}/cmd/account/main.go",
"showLog": true,
"env": {},
"args": []
}
]
}

2. Click Run Debug , you will find the server print new messages is from application’s starting up.

3. Then create a break point in your code line. The dlv server will print:

2019–05–08T13:06:03Z debug layer=debugger halting
2019–05–08T13:06:03Z info layer=debugger created breakpoint: …
2019–05–08T13:06:03Z debug layer=debugger continuing

4. The next, you can debug your application just as in a local machine.

--

--