查一般的 bug凰兑,我更習(xí)慣直接加打印,能快速定位問(wèn)題审丘,遠(yuǎn)比斷點(diǎn)調(diào)試方便的多吏够。但遇到一些復(fù)雜、或是涉及第三方類(lèi)庫(kù)時(shí)滩报,卻不得不需要添加斷點(diǎn)逐步跟蹤邏輯锅知。
使用 golang 開(kāi)發(fā)的微服務(wù),全部是直接運(yùn)行在 docker 容器里的脓钾,想要調(diào)試并不是一件簡(jiǎn)單的事情售睹,這時(shí)候我們就需要 Remote Debug (遠(yuǎn)程調(diào)試)
- vscode - 代碼編輯器
- ms-vscode.go - vscode 支持 Go 語(yǔ)言的插件
- delve - Golang 調(diào)試工具,支持遠(yuǎn)程調(diào)試
- Makefile - 封裝指令的合集
- alpine - 迷你 Docker 系統(tǒng)鏡像可训,只有 5MB
啟動(dòng)容器
通過(guò)Makefile
和Dockerfile
編譯鏡像和運(yùn)行 Docker 容器昌妹,我的方式是在本地(MacOS)上編譯好 Linux 的可執(zhí)行文件,拷貝到用 alpine 小鏡像創(chuàng)建的容器里握截。也可以選擇用 golang 鏡像飞崖,拷貝源碼進(jìn)去。
- 配置
Makefile
- 注意在編譯應(yīng)用程序時(shí)需要谨胞,通過(guò)添加
-gcflags "all=-N -l"
生成支持 Debug 的包 - 運(yùn)行容器時(shí)需要通過(guò)參數(shù)
--security-opt="seccomp=unconfined" --cap-add=SYS_PTRACE
關(guān)閉容器的安全限制 - 運(yùn)行時(shí)需要把 dlv 監(jiān)聽(tīng)的端口暴露出來(lái)
-p 20000:20000
固歪,以便 vscode 調(diào)試程序去連接
NAME := account
IMAGE := $(NAME)-service
# 編譯鏡像
build-debug:
# 清理廢棄的image (按需開(kāi)啟)
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
# 運(yùn)行容器
run-debug:
# my-docker-network 是我的Docker network,所有的微服務(wù)都在這個(gè)網(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
容器啟動(dòng)入口為 ./dlv --listen=:20000 --headless=true --api-version=2 --log=true exec ./account
-
./
代表 WORKDIR 的/app
目錄牢裳,dlv、account 可執(zhí)行文件我們通過(guò) Dockerfile 都已經(jīng)添加到這個(gè)目錄里了 -
--listen=:20000
dlv 服務(wù)綁定的端口 -
--headless=true
調(diào)試服務(wù)器叶沛,無(wú) ui 模式 -
--api-version=2
服務(wù)提供的 API 版本 -
--log=true
開(kāi)啟日志贰健,否則只能看到應(yīng)用日志,沒(méi)有 dlv 自身的日志 -
exec ./account
準(zhǔn)備執(zhí)行的程序恬汁,這個(gè)程序必須支持 Debug
與exec
對(duì)應(yīng)的還有另外一個(gè)指令: debug
,直接調(diào)試源碼可以用
FROM alpine:3.9
# 應(yīng)用程序需要這個(gè)組件來(lái)支持請(qǐ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"]
- 運(yùn)行容器
會(huì)發(fā)現(xiàn)沒(méi)有應(yīng)用啟動(dòng)日志辜伟,是因?yàn)槲⒎?wù)在此時(shí)還未被拉起氓侧,只是關(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ǎn)調(diào)試
- 配置啟動(dòng)文件
mode
remote 開(kāi)啟遠(yuǎn)程模式host
、port
配置成 dlv 暴露出的服務(wù)器地址program
配置成本地對(duì)應(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ǎn)擊開(kāi)始調(diào)試导狡,應(yīng)用才開(kāi)始運(yùn)行约巷,所以可以看出是 vscode 客戶(hù)端通知 dlv 服務(wù)器啟動(dòng)應(yīng)用的
- 這時(shí)候我們?cè)?vscode 里創(chuàng)建一個(gè)斷點(diǎn),服務(wù)器會(huì)出現(xiàn)對(duì)應(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
- 接下來(lái)就和在本地調(diào)試程序是一樣的了独郎。