Goland 对容器中的 Go 程序断点远程调试

news/2024/11/8 20:43:46/

1,针对 golang 程序打断点有哪几种情况

  • 临时进程:针对临时运行一次的 Golang 脚本,比如定时统计脚本,定时推送脚本。
  • 常驻进程:针对一直在后台运行的 Golang 程序,比如 HTTP 或者 GRPC 服务。

我们现在假设不管是上面的临时进程还是常驻进程都是运行的容器中。(因为不在容器中的大家可以直接在 Goland 中进行断点 debug)

2,临时进程断点 debug?

1,首先我们需要在一个 含有 Golang 程序的容器中安装 delve(简称dlv)(debug 工具,类似 PHP 的 xdebug)

第一种安装方式:go install github.com/go-delve/delve/cmd/dlv@latest

第二种安装方式(推荐):针对我们使用的 golang 镜像,进行二次定制,直接将 delve 打包进行,dockerfile 如下

FROM golang:1.20.2 AS builderWORKDIR /build# 这里他的 main.go 没有直接在项目主目录,不同于其他 golang 开源项目。
RUN git clone --depth 1 --branch v1.20.2 https://github.com/go-delve/delve.git && \cd /build/delve/cmd/dlv && \go build -o /build/delve/bin/dlvFROM golang:1.20.2COPY --from=builder /build/delve/bin/dlv /usr/local/bin/dlv
  • 执行命令进行编译(双版本):docker buildx build --platform linux/amd64,linux/arm64 -t golang:1.20.2-debug .

备注:使用 FROM golang:1.20.2 AS builder 的语法是 Dockerfile 中的多阶段构建(Multi-stage build)功能,旨在帮助优化镜像的大小和构建过程

2,在下载好dlv的容器中的程序主目录,运行dlv的命令

dlv debug --headless --listen=:40000 --api-version=2 --accept-multiclient

如下图代表启动成功,并且监听 40000 端口,等待远程的 debug 工具(Goland/vscode等)连接。

image.png

3,配置 goland 连接第二步运行的dlv

image.png

image.png

image.png

image.png

4,使用 goland 打断点并触发 debug

image.png

3,常驻进程断点 debug?

如果你只调试一个常驻进程也可以像临时进程进行一样 debug,不过当你有多个微服务用 docker-compose 部署起来以后,一般都是启动 docker 就启动对应的 golang 程序。这个时候要去杀死这个进程再用dlv进行启动,会发现比较麻烦

这里提供一个方便的方法。就是原来的微服务配置全部不动。然后

  • 通过一个 shell 脚本,获取到容器里面运行微服务的pid。
  • 通过 dlv的 attach 操作直接入侵正在运行的goalng程序 同时暴露 40000 端口。
  • 通过 goland 的 go remote 连接暴露的 40000 端口。

细化操作如下: 1, 在容器内,编译并运行(或者直接修改 docker-compose 中的 command)一个 golang web 服务 编译并且运行:

go build -gcflags="-N -l" -o demo
./demo

这里先 build 是因为需要添加 -gcflags="-N -l" 这个参数,因为当使用go run命令运行Go代码时,无法直接添加编译器标志(例如-gcflags="-N -l"),而只有加了这个参数才可以避免 golang 在做编译时把一些代码进行忽略,导致goland无法再某一行代码上打断点且会报错:could not find statement at /usr/local/go/src/cmd/ go/main.go:24, please use a line with a statement

2,通过一个shell 脚本(dlv_debug.sh)启动 dlv

#!/bin/bash# 配置这个程序的名字
#program_name="/data/golang_breakpoint_debug/tmp/main"
program_name="/demo"# 获取所有程序
program_list=$(ps -ef | grep -v "air" | grep -v "/bin/sh")# 获取包含 program_name 的程序的PID
go_app_pid=$(echo "$program_list" | awk -v pname="$program_name" '$0 ~ pname {print $2}')echo "====== get golang program,name:$program_name pid: $go_app_pid ======"# 运行dlv命令
dlv attach "$go_app_pid" --headless --listen=:40000 --api-version=2 --accept-multiclient

可以在容器内运行上面的 shell 脚本:

image.png

也可以在容器外面执行这个命令:

docker exec -t golang_breakpoint_debug /bin/sh -c "chmod +x /data/golang_breakpoint_debug/dlv_debug.sh && /data/golang_breakpoint_debug/dlv_debug.sh"

3,按照临时程序的debug流程一样配置goland,启动 debug

image.png

以上就是怎么在不停止golang 的常驻进程(比如 HTTP或者 GRPC 的服务)的情况下,对这个 golang 的常驻进程进行 debug。

4,全自动一键 debug(不想折腾可以跳到第五小节)

上面每次停止 debug 或者 代码更新以后都需要再去手动启动一下dlv,但是程序员是最讨厌手动的。所以得想办法解决这两个手动操作。

4.1 解决代码更新自动重启

安装一个工具包:go install http://github.com/cosmtrek/air@latest 将 app 启动命令替换为 air 启动的命令:air --build.cmd 'go build -gcflags="all=-N -l" -o demo' --build.bin="./demo"

备注:这里 air 会监听 golang 代码,当代码发生变动的时候,会自动重启。

4.2 解决每次都要手动启动dlv

配置一下before launch 来每次自动启动dlv 在go remote 里面增加一个 before launch 命令如下图

image.png

dlv_debug_out_start.sh 的 shell 脚本内容如下:

#!/bin/bashdocker exec -t golang_breakpoint_debug /bin/sh -c "chmod +x /data/golang_breakpoint_debug/dlv_debug.sh && /data/golang_breakpoint_debug/dlv_debug.sh" &sleep 0.5

注意的第一点: 报错:the input device is not a TTY 因为一般docker 命令里面是用的 exec -it ,而如果您在命令中使用了 -t 或 --tty 选项,它会要求终端交互性,并且如果当前的输入设备不是一个 TTY,则会出现该错误。

注意的第二点: shell 脚本中需要 sleep 0.5 ,是因为这个是顺序执行,因为 shell 里面最后加了 & 所以不会阻塞,有可能 shell 运行完了,Goland 启动 debug, 但是shell 那里异步启动还没完成,会导致启动失败。

注意的第三点: 我们配置 go remote 的时候需要配置停止 debug 以后自动停止远程的dlv。

image.png

4.3 整个流程的一个回顾

完成上面的配置,基本就可以做到一键 debug 运行在 docker 中的常驻微服务。最后我们再整体回顾一下,看下这个流程是怎么走通的,假设现在有一个常驻 web 服务大体的流程是

1,docker-compose 启动所有容器,此时容器中有一个 web 服务。

2,这个时候你在 Goalnd里面打好断点,同时点击Goland 中的debug按钮。

3,点击 debug 按钮后,触发 before launch,在宿主机运行 shell 脚本(dlv_debug_out_start.sh)启动 web 服务容器内 shell 脚本(dlv_debug.sh)。

4,web 容器里面的 shell 脚本(dlv_debug.sh)获取正在运行的web服务pid,然后启动dlv劫持这个pid,同时暴露 40000 端口。

5,此时 before launch 运行完毕,Goalnd的debug客户端连接上 web 容器的40000端口,此时可以进行 debug 操作。

6,debug 完毕以后,我们关闭 debug,这个时候自动触发关闭远程dlv

7,我们修改我们需要修改的 Golang 文件,此时触发 air,重启了 web 服务

8,我们再次尝试 debug(循环 3-8)

按照以上的流程,其实我们可以给其他任何语言(PHP/jave)运行在 docker 里面的服务做添加类似的debug配置。

5,Goland 断点 debug 的基础操作教程

最后提供一个 Goland 断点 debug 页面中简单调试会用到的基础按钮的作用,具体可看下图。红色框起来的的几的按钮以及后面对这几个按钮的解释。

image.png

  • Show Execution Point (Alt + F10):如果你的光标在其它行或其它页面,点击这个按钮可跳转到当前代码执行的行。
  • Step Over (F8):步过,一行一行地往下走,如果这一行上有方法不会进入方法。
  • Step Into (F7):步入,如果当前行有方法,可以进入方法内部,一般用于进入自定义方法内,不会进入官方类库的方法。
  • Force Step Into (Alt + Shift + F7):强制步入,能进入任何方法,查看底层源码的时候可以用这个进入官方类库的方法。
  • Step Out (Shift + F8):步出,从步入的方法内退出到方法调用处,此时方法已执行完毕,只是还没有完成赋值。
  • Run to Cursor (Alt + F9):运行到光标处,你可以将光标定位到你需要查看的那一行,然后使用这个功能,代码会运行至光标行,而不需要打断点。
  • Evaluate Expression (Alt + F8):计算表达式。

另外贴一个 PHP 的 断点 debug 教程,我记得好像很多是 PHP 转 Golang:

以上,基本就是全部的利用 Goland 对 运行在 docker 里面的 goland 程序进行 debug 的配置和基本操作了,创作不易,欢迎点赞收藏,以及如果有疑问或者改进意见,欢迎在评论区留言。


http://www.ppmy.cn/news/1195547.html

相关文章

BIOS开发笔记 – 显示

UEFI启动流程跑完前三阶段,UEFI环境的准备基本完成,到BDS阶段的任务就是准备引导OS。在此之前还需要使一些必要的硬件工作起来,比如键盘设备,屏幕等,怎么让屏幕工作呢?简单的说就是执行其相关的UEFI驱动。要注意一下的是,这里所说的驱动并不是屏幕的驱动,而是GPU的驱动…

java服务器环境配置以及项目搭建

一. 内容简介 使用Mavn聚合工程,springboot整合spring,springmvc,mybatis框架,完成项目搭建 二. 软件环境 2.1 java 1.8.0_144 2.2 mysql Ver 8.0.30( 8.10的好像出问题,我给重装了) 2.3 IntelliJ IDEA 2023.1 2.4 Apache Maven 3.9.5 …

离散数学实践(2)-编程实现关系性质的判断

*本文为博主本人校内的离散数学专业课的实践作业。由于实验步骤已经比较详细,故不再对该实验额外提供详解,本文仅提供填写的实验报告内容与代码部分,以供有需要的同学学习、参考。 -------------------------------------- 编程语言&#xff…

MySql表自修改报错:You can‘t specify target table ‘student‘ for update in FROM clause

文章目录 一、发现问题二、场景1:在where条件中查询了修改表的数据三、场景2:在set语句中查询了修改表的数据 一、发现问题 在一次准备处理历史数据sql时,出现这么一个问题:You cant specify target table 表名 for update in FR…

AM@点与点集的关系@n维空间邻域

文章目录 abstract坐标平面平面点集 平面邻域利用邻域描述点与点集的关系聚点点集分类 n n n维空间基础概念线性运算和空间概念 空间中的两点距离 n n n维空间中的变元极限 n n n维空间内的邻域 abstract 坐标平面和平面点集, n n n维空间点集点与点集的关系n维空间及其邻域 …

各位社区工作者!打工而已,不要太上头!!

社工家人们,打工而已,不要太上头!咱能偷懒的就偷懒啊! 合情合理的偷懒不仅让你更轻松,工作效率还会提高,何乐而不为呢!!就比如说各种报告啊,活动方案这些啊,…

【equals比较方法 和 内部类】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言对象比较equals方法内部类实例内部类静态内部类总结 前言 对象比较equals方法 三种比较相等方法: 第一种,如果两侧是基本数据类型&…

[NOIP2017 提高组] 列队 题解

数据结构。 n 1 n1 n1 的 case:考虑有 m q mq mq 个位置,出队的人直接添加到队尾。维护位置对应的人,每次查询第 k k k 个人的位置。 实现考虑维护 01 序列,表示位置上是 / 否有人,每次查前缀和为 k k k 的位置即…