目录
背景
排查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镜像有关。
Go语言默认生成的二进制文件依赖于运行环境中的动态链接库。如果你没有对程序进行静态编译(即使用CGO_ENABLED=0),可能会导致在Alpine环境下找不到相应的库文件。
这就需要在编译Go程序时确保使用了静态编译选项,例如:CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o 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),这个目录名会包含指定的后缀。这样可以避免不同编译配置下的标准库与默认编译配置下的标准库相互影响。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.youkuaiyun.com/HYZX_9987/article/details/135358717