查一般的 bug,我更习惯直接加打印,能快速定位问题,远比断点调试方便的多。但遇到一些复杂、或是涉及第三方类库时,却不得不需要添加断点逐步跟踪逻辑。
使用 golang 开发的微服务,全部是直接运行在 docker 容器里的,想要调试并不是一件简单的事情,这时候我们就需要 Remote Debug (远程调试)
- vscode - 代码编辑器
- ms-vscode.go - vscode 支持 Go 语言的插件
- delve - Golang 调试工具,支持远程调试
- Makefile - 封装指令的合集
- alpine - 迷你 Docker 系统镜像,只有 5MB
启动容器
通过Makefile
和Dockerfile
编译镜像和运行 Docker 容器,我的方式是在本地(MacOS)上编译好 Linux 的可执行文件,拷贝到用 alpine 小镜像创建的容器里。也可以选择用 golang 镜像,拷贝源码进去。
- 配置
Makefile
- 注意在编译应用程序时需要,通过添加
-gcflags "all=-N -l"
生成支持 Debug 的包 - 运行容器时需要通过参数
--security-opt="seccomp=unconfined" --cap-add=SYS_PTRACE
关闭容器的安全限制 - 运行时需要把 dlv 监听的端口暴露出来
-p 20000:20000
,以便 vscode 调试程序去连接
NAME := account
IMAGE := $(NAME)-service
# 编译镜像
build-debug:
# 清理废弃的image (按需开启)
docker image prune -f
# 编译Linux版 dlv可执行文件
GOOS=linux GOARCH=amd64 go build github.com/go-delve/delve/cmd/dlv
# 编译Linux版 应用可执行文件
GOOS=linux GOARCH=amd64 go build -gcflags "all=-N -l"
# 指定Dockerfile 生成镜像
docker build -f Dockerfile.debug -t $(IMAGE) .
# 清理掉生成的可执行文件
rm -f $(NAME) dlv
# 运行容器
run-debug:
# my-docker-network 是我的Docker network,所有的微服务都在这个网络里,可以直接互连
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
- 配置
Dockerfile.debug
容器启动入口为 ./dlv --listen=:20000 --headless=true --api-version=2 --log=true exec ./account
-
./
代表 WORKDIR 的/app
目录,dlv、account 可执行文件我们通过 Dockerfile 都已经添加到这个目录里了 -
--listen=:20000
dlv 服务绑定的端口 -
--headless=true
调试服务器,无 ui 模式 -
--api-version=2
服务提供的 API 版本 -
--log=true
开启日志,否则只能看到应用日志,没有 dlv 自身的日志 -
exec ./account
准备执行的程序,这个程序必须支持 Debug
与exec
对应的还有另外一个指令: debug
,直接调试源码可以用
FROM alpine:3.9
# 应用程序需要这个组件来支持请求HTTPS的网址
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"]
- 运行容器
会发现没有应用启动日志,是因为微服务在此时还未被拉起,只是关联好了
$ make debug
...
2019-05-08T13:03:23Z info layer=debugger launching process with args: [./account]
API server listening at: [::]:20000
Vscode 配置及断点调试
- 配置启动文件
mode
remote 开启远程模式host
、port
配置成 dlv 暴露出的服务器地址program
配置成本地对应的源码 main 文件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": []
}
]
}
- 点击开始调试,应用才开始运行,所以可以看出是 vscode 客户端通知 dlv 服务器启动应用的
- 这时候我们在 vscode 里创建一个断点,服务器会出现对应的日志。
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
- 接下来就和在本地调试程序是一样的了。