docker容器run报错/bin/sh: ./main: not found问题记录

目录

背景

排查Dockerfile

原因及解决方式


背景

在debian linux上通过Dockerfile构建go程序,构建时未报错,运行时报错:可执行文件找不到。

确切的说,是编译之后的二进制文件放在alpine镜像中运行时报了该错。

首先,确认是否路径不正确、main文件放的位置不对、main不存在等路径问题导致,如果是改一下就好了,反之继续看。

这里作者通过多次排查,构建完成时形成的二进制文件 main 确实是存在的,路径正确。


排查Dockerfile

先说说构建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/main

3、静态编译问题
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),这个目录名会包含指定的后缀。这样可以避免不同编译配置下的标准库与默认编译配置下的标准库相互影响。

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ProblemTerminator

您的鼓励将是作者最大的动力哦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值