Debug Golang in containers with delve: container_linux.go:380: starting container process caused: exec: "/dlv": stat /dlv: no such file or directory

huangapple go评论78阅读模式
英文:

Debug Golang in containers with delve: container_linux.go:380: starting container process caused: exec: "/dlv": stat /dlv: no such file or directory

问题

我正在尝试将我自己的 Golang 调试版本应用程序与 Golang 调试工具 delve 一起容器化。以下是我如何在本地调试我的 Golang 应用程序的步骤,它是一个非常简单的 RSS 阅读器,从我感兴趣的 RSS 源中获取数据。

在我的本地终端上执行以下命令来使用 delve 调试我的 Golang 应用程序:

$ dlv debug parsedata-xml-fp.go  # 使用 delve 启动我的应用程序
Type 'help' for list of commands.
(dlv) b main
Command failed: Location "main" ambiguous: main.main, runtime.main…
(dlv) b main.main
Breakpoint 1 set at 0x760252 for main.main() ./parsedata-xml-fp.go:50
(dlv) c
> main.main() ./parsedata-xml-fp.go:50 (hits goroutine(1):1 total:1) (PC: 0x760252)
=>  50: func main() {
    51:         // [decode from response.Body]
    52:         url := "https://foreignpolicy.com/feed/"
    53:
    54:         var URLset Rss
    55:         if xmlBytes, err := getXML(url); err != nil {
(dlv) l
> main.main() ./parsedata-xml-fp.go:50 (hits goroutine(1):1 total:1) (PC: 0x760252)
=>  50: func main() {
    51:         // [decode from response.Body]
    52:         url := "https://foreignpolicy.com/feed/"
    53:
    54:         var URLset Rss
    55:         if xmlBytes, err := getXML(url); err != nil {
(dlv)

在我的本地机器上,我可以设置断点并进入我感兴趣的函数。

我尝试在我构建的容器内做同样的事情。

选项1:
以下是我的容器的 Dockerfile:

# Dockerfile.dlv
FROM golang:1.17 AS build

WORKDIR /
COPY go/app/parsedata-xml-fp.go .
COPY go.mod .
COPY go.sum .

RUN go install github.com/go-delve/delve/cmd/dlv@latest
RUN go build -gcflags="all=-N -l" -o /feedme
RUN echo $(ls /go/bin)

# stage 2 build
FROM ubuntu:18.04
WORKDIR /

EXPOSE 2345

COPY --from=build /go/bin/dlv /dlv
COPY --from=build /feedme /feedme
COPY --from=build /parsedata-xml-fp.go /parsedata-xml-fp.go
CMD ["/dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/feedme"]

当我启动容器并登录时,我遇到了以下错误:

exec: "go": 在 $PATH 中找不到可执行文件

以下是我容器中的完整日志:

sudo docker exec -it b1494552ef1d /bin/sh
# which dlv
# ls
bin   dev  etc     home  lib64  mnt  parsedata-xml-fp.go  root  sbin  sys  usr
boot  dlv  feedme  lib   media  opt  proc                 run   srv   tmp  var
# ./dlv
Delve is a source level debugger for Go programs.
  ......... # 忽略 delve 的帮助信息,只是确认 delve 已安装
Use "dlv [command] --help" for more information about a command.
# ./dlv debug parsedata-xml-fp.go
exec: "go": 在 $PATH 中找不到可执行文件
# which go
# (什么都没有显示)

我的理解是 ubuntu1804 没有安装 go。然后我尝试只使用 golang 的 Docker 镜像。

选项2:

更新的 Dockerfile 如下:使用 golang:1.17 作为基础镜像(Go 应该已经安装):

# Dockerfile.localmod
FROM golang:1.17 AS build

WORKDIR /
COPY go/app/parsedata-xml-fp.go .
COPY go.mod .
COPY go.sum .

RUN echo $(which shell)

RUN go install github.com/go-delve/delve/cmd/dlv@latest
RUN go build -gcflags="all=-N -l" -o /feedme
RUN echo $(ls /go/bin)

EXPOSE 2345
CMD ["/dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/feedme"]

这次错误发生在我启动容器时:

sudo docker run 734129d1b1a2
docker: Error response from daemon: failed to create shim: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "/dlv": stat /dlv: no such file or directory: unknown.
ERRO[0000] error waiting for container: context canceled

有人能建议如何正确地将 delve 集成到我的 Go 容器中,并在容器终端上进行调试,就像在本地调试一样吗?

英文:

I am trying to containerize a debug version Golang application of my own with Golang debugging delve build in.
Here is how I can debug my Golang application locally , it is a very simple RSS reader. It retrieves data from RSS feed I am interested in.

$on my local terminal$ dlv debug parsedata-xml-fp.go  # launch my app with delve
Type 'help' for list of commands.
(dlv) b main
Command failed: Location "main" ambiguous: main.main, runtime.main…
(dlv) b main.main
Breakpoint 1 set at 0x760252 for main.main() ./parsedata-xml-fp.go:50
(dlv) c
> main.main() ./parsedata-xml-fp.go:50 (hits goroutine(1):1 total:1) (PC: 0x760252)
=>  50: func main() {
    51:         // [decode from response.Body]
    52:         url := "https://foreignpolicy.com/feed/"
    53:
    54:         var URLset Rss
    55:         if xmlBytes, err := getXML(url); err != nil {
(dlv) l
> main.main() ./parsedata-xml-fp.go:50 (hits goroutine(1):1 total:1) (PC: 0x760252)
=>  50: func main() {
    51:         // [decode from response.Body]
    52:         url := "https://foreignpolicy.com/feed/"
    53:
    54:         var URLset Rss
    55:         if xmlBytes, err := getXML(url); err != nil {
(dlv) 

On my local machine I can set breakpoints and step into functions I am interested in.

I was trying to do the same inside a container I built.

Option #1:
Below is the Dockerfile of my container

#Dockerfile.dlv
FROM golang:1.17 AS build

WORKDIR /
COPY go/app/parsedata-xml-fp.go .
COPY go.mod .
COPY go.sum .
    
RUN go install github.com/go-delve/delve/cmd/dlv@latest
RUN go build -gcflags="all=-N -l" -o /feedme
RUN echo $(ls /go/bin)

# stage 2 build 
FROM ubuntu:18.04
WORKDIR /

EXPOSE 2345 

COPY --from=build /go/bin/dlv /dlv
COPY --from=build /feedme /feedme
COPY --from=build /parsedata-xml-fp.go /parsedata-xml-fp.go
CMD ["/dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/feedme"]

When bootup my container and login , I go the error :

exec: "go": executable file not found in $PATH

Below is the complete log in my containter

sudo docker exec -it b1494552ef1d /bin/sh
# which dlv
# ls
bin   dev  etc     home  lib64  mnt  parsedata-xml-fp.go  root  sbin  sys  usr
boot  dlv  feedme  lib   media  opt  proc                 run   srv   tmp  var
# ./dlv
Delve is a source level debugger for Go programs.
  ......... # dismiss delve help info , just to confirm dlv is installed 

Use "dlv [command] --help" for more information about a command.
# ./dlv debug parsedata-xml-fp.go
exec: "go": executable file not found in $PATH
# which go
# (nothing)

My understanding is ubuntu1804 has no go installed ? Then I tried to use go docker image only

Option #2

Updated Dockerfile as below : use golang:1.17 as base image (Go should be there):

# Dockerfile.localmod
FROM golang:1.17 AS build

WORKDIR /
COPY go/app/parsedata-xml-fp.go .
COPY go.mod .
COPY go.sum .

RUN echo $(which shell)

RUN go install github.com/go-delve/delve/cmd/dlv@latest
RUN go build -gcflags="all=-N -l" -o /feedme
RUN echo $(ls /go/bin)

EXPOSE 2345 
CMD ["/dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/feedme"]

This time error occurred right at I boot my container

sudo docker run 734129d1b1a2                     
docker: Error response from daemon: failed to create shim: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "/dlv": stat /dlv: no such file or directory: unknown.
ERRO[0000] error waiting for container: context canceled

Anyone could suggest what is the right way of intergrate delve into my Go container and debugging it on container terminal exactly as debug locally?

答案1

得分: 4

问题出在你的dlv二进制文件被动态编译了。当你使用go install下载二进制文件时,默认情况下会使用CGO_ENABLED=1(除非被覆盖),这要求大部分运行时库(包括glibc)在运行时加载。这在某些容器镜像中可能无法正常工作,因为这些库可能不存在(例如从头构建的镜像或无发行版的静态镜像)。

为了避免与容器镜像的依赖关系,可以通过将上述标志设置为0来始终下载静态编译的二进制文件。在你的Docker上下文中使用下载的二进制文件。

CGO_ENABLED=0 go install github.com/go-delve/delve/cmd/dlv@latest

你还可以观察静态和动态编译版本的dlv上的ldd输出。前者不会列出需要动态加载的任何库,而后者会列出这些库。

英文:

The problem is with your dlv binary being dynamically compiled. When you download the binary with go install, by default it downloads with CGO_ENABLED=1 (unless overriden), requiring most of the runtime libraries (including glibc) to be loaded at runtime. This might not work well in some container images, where the libraries are not present (e.g. images built from scratch/distro-less static images).

So to avoid the dependency with the container image's dependencies, always download a statically compiled one by setting the above flag to 0. Use the downloaded binary in your docker context

CGO_ENABLED=0 go install github.com/go-delve/delve/cmd/dlv@latest

You can also observe the ldd output on dlv between the statically and dynamically compiled versions. The former would list no libraries that would need to be dynamically loaded, the latter would list them.

huangapple
  • 本文由 发表于 2022年6月8日 11:52:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/72539868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定