【GO 学习笔记】goland 调试 docker 容器中的 go 代码

本贴最后更新于 1976 天前,其中的信息可能已经物是人非

goland 调试 docker 容器中的 go 代码

前言:为什么需要去远程容器中的 go 代码?

最近正在看 Harbor 的源码,由于本人电脑是 windows 无法直接启动进行 debug,后来搭了一个 CentOS 的桌面环境,又发现启动需要很多很多环境参数...而且还有很多其他组件都是运行在 docker 上,使用 linux 系统启动的方案又失败了!后来查了好多资料,找到 delve 这个插件,该插件可以在 docker 容器中,远程调试代码。这里记录下过程(使用的是小 demo 而不是 Harbor)!

一、 下载安装 delve

  1. 进入 GOPATH 路径创建对应目录
mkdir -p $GOPATH/github.com/go-delve cd $GOPATH/github.com/go-delve
  1. github clone delve 源码
$ git clone https://github.com/go-delve/delve.git
  1. 编译
$ cd $GOPATH/src $ go install github.com/go-delve/delve/cmd/dlv
  1. 编译完成后 dlv 文件就在 $GOPATH/bin 目录下了,可以以下命令查看版本信息:
$ dlv -v

二、 准备 Demo

  1. 准备一个 demo,文件名为 web.go
package main import ( "crypto/md5" "fmt" "html/template" "io" "log" "net/http" "os" "strconv" "strings" "time" ) func sayHelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() // 解析参数,默认是不会解析的 fmt.Println(r.Form) // 这些信息是输出入服务器的打印信息 fmt.Println("path ", r.URL.Path) fmt.Println("scheme ", r.URL.Scheme) fmt.Println(r.Form["url_long"]) for k, v := range r.Form { fmt.Println("key:", k) fmt.Println("value", strings.Join(v, "")) } fmt.Fprintf(w, "hello vic") } func login(w http.ResponseWriter, r *http.Request) { fmt.Println("method ", r.Method) if r.Method == "GET" { t, _ := template.ParseFiles("login.html") log.Println(t.Execute(w, nil)) } else { r.ParseForm() // 如果注释这句不会有输出 默认情况下,Handler里面是不会自动解析form的,必须显式的调用r.ParseForm()后,你才能对这个表单数据进行操作 //请求的是登录数据,那么执行登录的逻辑判断 fmt.Println("username:", r.Form["username"]) fmt.Println("password:", r.Form["password"]) } } func upload(w http.ResponseWriter, r *http.Request) { fmt.Println("method:", r.Method) //获取请求的方法 if r.Method == "GET" { crutime := time.Now().Unix() h := md5.New() io.WriteString(h, strconv.FormatInt(crutime, 10)) token := fmt.Sprintf("/x", h.Sum(nil)) t, _ := template.ParseFiles("upload.html") fmt.Println(token) t.Execute(w, nil) } else { r.ParseMultipartForm(32 << 20) file, handler, err := r.FormFile("uploadfile") if err != nil { fmt.Println(err) return } defer file.Close() fmt.Fprintf(w, "%v", handler.Header) f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) // 此处假设当前目录下已存在test目录 if err != nil { fmt.Println(err) return } defer f.Close() io.Copy(f, file) } } func main() { http.HandleFunc("/v2", sayHelloName) //设置访问路由 http.HandleFunc("/login", login) http.HandleFunc("/upload", upload) err := http.ListenAndServe(":9090", nil) // 设置监听端口 if err != nil { log.Fatal("ListenAndServe: ", err) } }

三、制作镜像

  1. 准备启动脚本 start.sh
#!/bin/bash dlv debug --headless --listen=:2345 --api-version=2 --accept-multiclient $GOPATH/src/web.go
  1. 创建 Dockerfile,并将以上所有文件放在同一个目录中,目录下有 4 个文件
[root@localhost go-debug]# ll 总用量 17708 -rwxr-xr-x. 1 root root 18119408 12月 3 14:52 dlv -rw-r--r--. 1 root root 187 12月 3 18:55 Dockerfile -rw-r--r--. 1 root root 107 12月 3 16:21 start.sh -rw-r--r--. 1 root root 2168 12月 3 14:49 web.go
  1. 编写 Dockerfile
FROM golang:1.12.12 RUN mkdir /web/ COPY ./dlv /usr/local/bin/ COPY ./web.go /go/src COPY ./start.sh /web/ RUN chmod u+x /web/start.sh WORKDIR /go/src/ ENTRYPOINT ["/web/start.sh"]
  1. 构建镜像
$ docker build . -t web:1.0
  1. 运行镜像
$ docker run -d -p 9090:9090 -p 2345:2345 --privileged web:1.0

四、goland 的配置

20191203193707.png

这里有个坑就是,远程被调试的代码和本地代码都必须在 $GOAPTH/src 目录下!!!

goland 单击 debug 按钮 打上断点 接下来就可以远程调试了!!访问接口,就发现代码停在了断点上了

$ curl localhost:9090/v2

20191203194429.png

整个流程结束!

源码地址:

五、后续思路

  1. 修改启动脚本,代码通过挂载的方式加到容器中,应该可以达成不用重复制作镜像的效果
  2. 用 harbor 源码调试,这样就可以进行愉快的 debug 了 而不是光靠想象了
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    498 引用 • 1395 回帖 • 247 关注
  • Harbor
    5 引用
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    494 引用 • 930 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...