目录
在debian linux上通过Dockerfile构建go程序,构建时未报错,运行时报错:可执行文件找不到。
确切的说,是编译之后的二进制文件放在alpine镜像中运行时报了该错。
首先,确认是否路径不正确、main文件放的位置不对、main不存在等路径问题导致,如果是改一下就好了,反之继续看。
这里作者通过多次排查,构建完成时形成的二进制文件 main 确实是存在的,路径正确。
先说说构建Dockerfile时的两种方式。
一是直接使用go镜像,编译,完成。此时构建出来的镜像大小为700多M;
二是使用go镜像完成编译,得到可执行文件后,再以 alpine 镜像运行起来。这种方式大小仅30多M。
方式1是没问题的,本文出现问题时使用的是方式2:
FROM golang:1.20.1 AS build
WORKDIR /app
COPY myproject . ??
RUN go env -w GO111MODULE=on?
RUN go env -w GOPROXY=https://goproxy.cn,https://goproxy.io,direct
RUN go mod download && go build -o main .
FROM alpine:latest
WORKDIR /project
RUN set -eux && sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN apk update && apk add --no-cache tzdata
ENV TZ="Asia/Shanghai"
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY --from=build /app/main .
ENTRYPOINT ["/project/main"]
那么会不会是main文件没有可执行权限导致的?通过构建中增加ls命令看到构建后的二进制文件权限:
#21 0.385 -rwxr-xr-x ? ?1 root ? ? root ? ? ?28238801 Oct ?3 10:31 main
即可执行权限是有的,不是这个原因。
你没有看错,也没有写错,main确实存在并且正常,但就是启不动。这两种方式的主要区别就是镜像不同,猜测是镜像内部的原因,果不其然,经过调查这和alpine镜像有关。
向AI请教后得到了如下结果:
当在Docker容器中基于Alpine Linux部署Go语言编译的程序时,遇到/bin/sh: ./main: not found这样的错误,通常有以下几种可能的原因:
1、程序文件路径或名称不正确
确保在Dockerfile中,你正确地将编译好的二进制文件复制到了容器的工作目录,并且以正确的名称引用它。
检查Dockerfile中的COPY或ADD指令是否将Go程序从构建上下文正确地放入到镜像中。2、执行权限缺失:
需要确保主程序具有可执行权限,在编译后的主机上可以运行 chmod +x main 来赋予可执行权限,或者在Dockerfile中添加类似命令:dockerfile
RUN chmod +x /path/to/main3、静态编译问题
Go语言默认生成的二进制文件依赖于运行环境中的动态链接库。如果你没有对程序进行静态编译(即使用CGO_ENABLED=0),可能会导致在Alpine环境下找不到相应的库文件。
解决方案:在编译Go程序时确保使用了静态编译选项,例如:CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o main .
4、musl libc兼容性问题
虽然Go语言编译器生成的二进制文件理论上可以在任何支持POSIX标准的操作系统上运行,但有时可能因为不同libc实现之间的差异出现问题。
如果你的程序确实依赖特定libc的功能,确保在Alpine下这些功能可用,或者确认编译时没有隐含的cgo依赖。5、启动脚本或Docker命令问题
在Dockerfile中使用CMD或ENTRYPOINT指定的启动命令如果不正确,也可能导致找不到程序。检查并确保启动命令正确无误,比如:
CMD ["./main"]
总之,检查和解决这类问题时,需要考虑的是编译配置、文件复制过程以及最终容器内部的执行环境。
经过前面的分析,前两条可忽略。最终的解决方案如第三条所说,需要对程序进行静态编译编译
核心是编译命令,修改后正确的如下:
RUN go mod download && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
含义
CGO_ENABLED=0 表示禁用了 CGO。
-a 表示强制重新构建所有包。
-installsuffix cgo 用于指定一个额外的安装后缀,特别是当值设为cgo时,通常是为了处理带有C语言绑定(即使用了import "C")的Go程序。因为带cgo的项目编译时会生成额外的依赖文件,并且这些依赖可能与纯Go项目的编译结果不同,通过-installsuffix cgo可以让这两种类型的项目共存于同一系统而不会产生冲突。
同时指定了-installsuffix的话,编译器会在构建时为导入的标准库创建一个新的包缓存目录(pkg),这个目录名会包含指定的后缀。这样可以避免不同编译配置下的标准库与默认编译配置下的标准库相互影响。