查一般的 bug,我更習(xí)慣直接加打印,能快速定位問題,遠(yuǎn)比斷點調(diào)試方便的多。但遇到一些復(fù)雜、或是涉及第三方類庫時,卻不得不需要添加斷點逐步跟蹤邏輯。
使用 golang 開發(fā)的微服務(wù),全部是直接運行在 docker 容器里的,想要調(diào)試并不是一件簡單的事情,這時候我們就需要 Remote Debug (遠(yuǎn)程調(diào)試)
- vscode - 代碼編輯器
- ms-vscode.go - vscode 支持 Go 語言的插件
- delve - Golang 調(diào)試工具,支持遠(yuǎn)程調(diào)試
- Makefile - 封裝指令的合集
- alpine - 迷你 Docker 系統(tǒng)鏡像,只有 5MB
啟動容器
通過Makefile和Dockerfile編譯鏡像和運行 Docker 容器,我的方式是在本地(MacOS)上編譯好 Linux 的可執(zhí)行文件,拷貝到用 alpine 小鏡像創(chuàng)建的容器里。也可以選擇用 golang 鏡像,拷貝源碼進(jìn)去。
- 配置
Makefile
- 注意在編譯應(yīng)用程序時需要,通過添加
-gcflags "all=-N -l"生成支持 Debug 的包 - 運行容器時需要通過參數(shù)
--security-opt="seccomp=unconfined" --cap-add=SYS_PTRACE關(guān)閉容器的安全限制 - 運行時需要把 dlv 監(jiān)聽的端口暴露出來
-p 20000:20000,以便 vscode 調(diào)試程序去連接
NAME := account
IMAGE := $(NAME)-service
# 編譯鏡像
build-debug:
# 清理廢棄的image (按需開啟)
docker image prune -f
# 編譯Linux版 dlv可執(zhí)行文件
GOOS=linux GOARCH=amd64 go build github.com/go-delve/delve/cmd/dlv
# 編譯Linux版 應(yīng)用可執(zhí)行文件
GOOS=linux GOARCH=amd64 go build -gcflags "all=-N -l"
# 指定Dockerfile 生成鏡像
docker build -f Dockerfile.debug -t $(IMAGE) .
# 清理掉生成的可執(zhí)行文件
rm -f $(NAME) dlv
# 運行容器
run-debug:
# my-docker-network 是我的Docker network,所有的微服務(wù)都在這個網(wǎng)絡(luò)里,可以直接互連
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 可執(zhí)行文件我們通過 Dockerfile 都已經(jīng)添加到這個目錄里了 -
--listen=:20000dlv 服務(wù)綁定的端口 -
--headless=true調(diào)試服務(wù)器,無 ui 模式 -
--api-version=2服務(wù)提供的 API 版本 -
--log=true開啟日志,否則只能看到應(yīng)用日志,沒有 dlv 自身的日志 -
exec ./account準(zhǔn)備執(zhí)行的程序,這個程序必須支持 Debug
與exec對應(yīng)的還有另外一個指令: debug,直接調(diào)試源碼可以用
FROM alpine:3.9
# 應(yīng)用程序需要這個組件來支持請求HTTPS的網(wǎng)址
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"]
- 運行容器
會發(fā)現(xiàn)沒有應(yīng)用啟動日志,是因為微服務(wù)在此時還未被拉起,只是關(guān)聯(lián)好了
$ make debug
...
2019-05-08T13:03:23Z info layer=debugger launching process with args: [./account]
API server listening at: [::]:20000
Vscode 配置及斷點調(diào)試
- 配置啟動文件
moderemote 開啟遠(yuǎn)程模式host、port配置成 dlv 暴露出的服務(wù)器地址program配置成本地對應(yīng)的源碼 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": []
}
]
}
- 點擊開始調(diào)試,應(yīng)用才開始運行,所以可以看出是 vscode 客戶端通知 dlv 服務(wù)器啟動應(yīng)用的
- 這時候我們在 vscode 里創(chuàng)建一個斷點,服務(wù)器會出現(xiàn)對應(yīng)的日志。
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
- 接下來就和在本地調(diào)試程序是一樣的了。