使用Goland + Air + Delve进行远程调试Docker环境

你好,我是神山,在Trada担任服务器端工程师。

这是Toreta Advent Calendar第12天的文章。
在这篇文章中,我想要介绍一下在docker-compose环境中使用golang进行远程调试的方法。

简述

使用Docker Compose来搭建Golang环境,并在其中运行应用程序。
由于已经使用air的热重载功能,可以立即反映差异,所以我可以放心地进行开发。
但随着服务代码量的增加和需求的复杂化,逐渐变得难以调试。
因此,我尝试引入delve来进行调试。

环境

    • MacBook Pro(Apple M1)

 

    • go version go1.19.4 linux/arm64

 

    • Docker Compose version v2.13.0

 

    • air v1.40.4

 

    delve v1.20.1

docker-compose.yml的中文意思是什么?

我编写了以下的docker-compose文件。

version: '3.7'

services:
  app:
    image: 'golang:1.19'
    working_dir: '/app'
    volumes:
      - type: bind
        source: '.'
        target: '/app'
    ports:
      - '8080:8080'
      - '2345:2345'
    command: >
      sh -c '
        go install github.com/go-delve/delve/cmd/dlv@latest
        go install github.com/cosmtrek/air@latest
        air -c .air.toml
      '
    • imageはそのままgolangの1.19を指定しています。

 

    • working_dirにソースコードをbindしています。

 

    • portsは二箇所開けています。

api用の8080
リモートデバッグ接続用の2345

commandでdocker-composeをbuildするタイミングでdelveとairををinstall、airを起動するようにしています。

本記事では触れませんが、本番環境用に別でDockerfileを用意しています。
本番環境ではdelve, airともにinstallしたくないのでここに記載する形にしました。

.air.toml 我只需要一个选项在中文中的表达方式

如果正在使用Air,我认为您已创建了一个toml文件。
我只编辑了下面所述的部分,其他部分未更改。

[build]
# Just plain old shell command. You could use `make` as well.
cmd = "go build -gcflags \"all=-N -l\" -o ./.air/main ./cmd/main.go"
# Binary file yields from `cmd`.
bin = "./.air/main"
# Customize binary.
full_bin = "APP_ENV=dev APP_USER=air dlv exec ./.air/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient"

cmdでは、./cmd/main.goをbuildして、./.air/mainにバイナリを配置しています。

binでは、あらためてバイナリの位置を記載しています。

full_binでは、airコマンドを使ってdelve用のserverを起動します。

在这种情况下,运行docker-compose up应该会在终端上显示以下日志。

app  | API server listening at: [::]:2345
app  | 2022-12-19T10:16:59Z warning layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)

远程调试连接

    • golandの右上からEdit Configurationを選択します。

+ボタンからGo Remoteを選択してください。
Nameは適当な名前を入れてください。
Hostはlocalhost, Portは2345を指定してください。

Leave it runnningにチェックを入れて保存してください。

スクリーンショット 2022-12-19 19.24.58.png
    下記のような表示になっているはずなので、デバッグマークを押してください。
スクリーンショット 2022-12-19 19.27.34.png
    問題なければ下記のようにConnectedが表示されるはずです。
スクリーンショット 2022-12-19 19.28.45.png

尝试进行调试

我为此准备了一个main函数,与原始代码完全不同。通过GET http://localhost:8080/hello访问,只会返回响应。

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/hello", helloHandler)
	http.ListenAndServe(":8080", nil)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
	test := "Hello Remote Debug!!"
	fmt.Fprint(w, test)
}

我会在实际环境中设置断点并调用API进行测试。
您可以在下方的调试控制台中看到test变量的值等信息。
如果您修改并保存源代码,热重载也会有效。

スクリーンショット 2022-12-19 19.31.27.png

1. 留意事项
2. 注意要点
3. 注意事项
4. 注意细节
5. 注意重点

看起来在M1 Mac上使用指定为linux/amd64的docker环境无法使用delve调试工具。
具体来说,我将docker-compose.yml修改如下:
由于golang1.19不支持linux/amd64镜像,我将其修改为golang:1.19-buster。

version: '3.7'

services:
  app:
+   platform: 'linux/amd64'
+   image: 'golang:1.19-buster'
-   image: 'golang:1.19'
    working_dir: '/app'
    volumes:
      - type: bind
        source: '.'
        target: '/app'
    ports:
      - '8080:8080'
      - '2345:2345'
    command: >
      sh -c '
        go install github.com/go-delve/delve/cmd/dlv@latest
        go install github.com/cosmtrek/air@latest
        air -c .air.toml
      '

在其他设置保持不变的情况下,执行docker-compose up。
尽管可以启动,但出现了”无法启动进程:fork/exec /app/.air/main: 功能未实现”的错误提示。

app  | API server listening at: [::]:2345
app  | 2022-12-19T10:37:43Z warning layer=rpc Listening for remote connections (connections are not authenticated nor encrypted)
app  | could not launch process: fork/exec /app/.air/main: function not implemented

当我调查delve的问题时,发现了这个问题。
https://github.com/go-delve/delve/issues/2910

我们对此无能为力。

评论会议已经关闭了,没有人理会。太无情了。

最后

虽然有点勉强,但我已经使用delve实现了远程调试。
由于在生产环境的Dockerfile中使用了linux/amd64的golang:1.19-buster,所以我认为不能完全信赖这个调试环境。
然而,当团队成员提出评论或编写复杂逻辑时,没有调试是很困难的……
在这种情况下,只需要稍微更改docker-compose.yml和.air.toml文件,就可以构建远程调试环境,非常方便。

特别是golang这种语言经常需要像for循环这样的直接写法,所以将开发体验进行优化能提高效率呢。。

另外,请注意,在我们这里正在招聘工程师。
如果你有兴趣,请务必联系我们。

 

bannerAds