Golang中执行go run main正常但go build -o main.go指定GOOS和GOARCH失败的问题
Golang中执行go run main正常但go build -o main.go指定GOOS和GOARCH失败的问题 大家好。
当我在我的MBP M1上执行 go run main.go 时,它可以正常运行。
当我执行 go build . 时,它可以成功构建。
但是,当我尝试使用下面的导出变量来构建,或者执行Makefile时,就会失败 - 错误信息如下。
<备注:我已在AWS上基于Amazon Linux的EC2实例(x86-64)上尝试了相同的命令/步骤,因为我打算部署到Lambda>
BINARY_NAME=main
export GOOS=linux
export GOARCH=amd64
export CGO_ENABLED=0
.DEFAULT_GOAL := deploy
deploy:
go build -o ${BINARY_NAME} .
zip -r function.zip main
aws lambda update-function-code --function-name "S3JSONDecomposer-Golang" --zip-file fileb://function.zip --region="af-south-1" | jq .
run:
go run ${BINARY_NAME}.go
./main.go:130:14: undefined: kafka.ConfigMap
./main.go:142:25: undefined: kafka.NewProducer
请帮忙。
G
更多关于Golang中执行go run main正常但go build -o main.go指定GOOS和GOARCH失败的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢,我会去看看是否能做… 会反馈
G
更多关于Golang中执行go run main正常但go build -o main.go指定GOOS和GOARCH失败的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
func main() {
fmt.Println("hello world")
}
基础容器不包含这些库
你是否可以在某个构建步骤中复制这些库呢?
(我想到的是多阶段 Dockerfile。)
考虑执行Go和C的静态链接。这篇文章详细解释了执行此操作的过程和注意事项。Matt Turner - 2022年静态链接Go 祝你好运
使用 Lambda 时,您只需提供代码,无法访问任何外部操作系统级别的变量。
Lambda 基于/部署在 Amazon Linux 操作系统构建上。 感谢提供的 StackOverflow 线索,我会去看看。
G
太棒了,正如所说,感谢指点。
安装了 gcc 设置了 CGO=1 重新运行了 Makefile,它编译成功了… 谢谢,现在快速测试一下应用。
G
georgelza:
这是在 CGO=0 的情况下……因为我认为它与我的目标架构相同。
每当 Go 包含 C 代码时,都必须设置 CGO 环境变量。这与目标架构无关。
我曾尝试(使用Python)通过Docker进行编译,但遗憾的是总是遇到问题。对于Python,我最终构建了这些EC2镜像,它们具有目标操作系统/架构……
如前所述,我也尝试在EC2实例上进行编译,结果遇到了相同的错误。我会很快再试一次,谁知道呢 😉
我会告知进展,感谢你的见解。
G
通过在基于 Amazon Linux 的 EC2 上构建我的二进制文件,我实现了完全相同的结果。
我也尝试过在容器内构建,但目前遇到了同样的问题。基础容器不包含这些库,因为看起来即使我遵循静态链接方法,仍有一些组件是动态链接的,而这些组件在 Lambda 初始化函数时使用的目标容器中并不存在。
我目前正在与一个问题作斗争,对 Lambda 或 Go 都缺乏足够的知识/经验。
:smile:
G
你好 @georgelza,
我猜你使用的是 confluent-kaftka-go。这个库基于 C 库 librdkafka,因此编译时需要 CGO。
只有纯 Go 代码才能轻松进行交叉编译。我可能会在 Mac 上使用虚拟机(Docker Desktop、Podman Desktop、OrbStack、Colima 等)来搭建一个 Linux 构建环境。
……所以如果我设置了 CGO_ENABLED=0
然后执行
go build -ldflags "-linkmode 'external'"
# jsondecom
./main.go:139:14: undefined: kafka.ConfigMap
./main.go:151:25: undefined: kafka.NewProducer
./main.go:241:23: undefined: kafka.Message
./main.go:242:28: undefined: kafka.TopicPartition
./main.go:242:80: undefined: kafka.PartitionAny
引用 georgelza:
本以为这会是一个完整/自包含的二进制文件…
只有纯 Go 代码才能做到。任何包含的 C 代码都会有 C 依赖。
我对 LambdaOS 一无所知,但我找到了这个 Stackoverflow 帖子,它表明你可能需要设置 LD_LIBRARY_PATH,并且很可能还需要以某种方式提供所需的 glibc。
找到
加载 libssl.so.1.0.0 时提示没有这样的文件或目录
标签: amazon-web-services, go, apache-kafka, aws-lambda
提问者: maathor 提问时间: 10:01AM - 24 Aug 18 UTC
G
将我的代码复制到基于Amazon Linux… x86-64…的EC2实例上后,我又回到了这个问题。
这是在设置 CGO=0 的情况下… 因为我假设它与我的目标架构相同,并且是x86-64,所以我猜它与源架构相同。
如果我设置 CGO=1,那么它会抱怨缺少C编译器。必须看看要安装什么C编译器,以前从未这样做过。
make deploy
go build -o main .
# jsondecom
./main.go:130:14: undefined: kafka.ConfigMap
./main.go:142:25: undefined: kafka.NewProducer
make: *** [Makefile:11: deploy] Error 1
有点卡住了,看着说明,我已经根据说明构建了一个 Dockerfile… 当然,当我运行并调用 docker 容器时,我遇到了已知的问题。 所以回到那个链接,它提到使用那个 .configure 来编译那些库文件… 那么,这个 .configure 是从哪里来的,它将要编译的源文件又在哪里? 然后它把它们复制到 {$PWD}/lib/,但这是哪个目录,是在容器内部。 有时候真希望写这些“如何解决问题”的人能退一步,考虑到执行这些操作的人可能不具备和他们相同的基础知识。 当然,一旦我让这个本地运行/调用工作起来,还得看看如何使用 AWS ECR 进行部署… 在某个地方看到过关于它的说明。 唉,看起来 Go 语言对 Lambda 不太友好。 G
我理解你的想法,但不太确定……我知道你给它一个包含主程序的 zip 文件,但不确定这个 zip 文件是否可以包含依赖项,以及它们是否可以被复制。
对我来说……这也没有意义……可执行文件应该是完整自包含的,否则如果你把它从一台主机复制到另一台主机……你最终会陷入部署的噩梦,而这正是单个 Go 二进制文件试图解决的问题。
我在某个视频/笔记中看到,你可以构建用于部署应用程序的 Docker 镜像,我在想这是否可行。那样的话,Lambda 就不是将我的二进制文件推送到镜像中并调用它,而是部署我提供的镜像(其中包含了所有必需的内容)。但转念一想,如果这样做,我为什么不直接在我们自己的 K8S / EKS 集群上部署呢?对我来说,到了那个阶段,Lambda 部署的优势就被削弱/减少了。
G
设置 CGO_ENABLED=1(在 MAC 上,让我在我的 EC2 实例上尝试一下)
go build -o main .
# runtime/cgo
linux_syscall.c:67:13: error: call to undeclared function 'setresgid'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
linux_syscall.c:67:13: note: did you mean 'setregid'?
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/unistd.h:593:6: note: 'setregid' declared here
linux_syscall.c:73:13: error: call to undeclared function 'setresuid'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
linux_syscall.c:73:13: note: did you mean 'setreuid'?
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/unistd.h:595:6: note: 'setreuid' declared here
make: *** [deploy] Error 1
唉……
看起来,为了在我的EC2镜像上编译程序而安装的必需glib库,在Lambda的操作系统上默认并不存在。这有点奇怪,可能是我知识不足,但我原本以为这会是一个完整/自包含的二进制文件……
/var/task/main: /lib64/libm.so.6: version `GLIBC_2.29' not found (required by /var/task/main)
/var/task/main: /lib64/libc.so.6: version `GLIBC_2.25' not found (required by /var/task/main)
/var/task/main: /lib64/libc.so.6: version `GLIBC_2.32' not found (required by /var/task/main)
/var/task/main: /lib64/libc.so.6: version `GLIBC_2.34' not found (required by /var/task/main)
2023/07/02 07:47:37 exit status 1
/var/task/main: /lib64/libm.so.6: version `GLIBC_2.29' not found (required by /var/task/main)
/var/task/main: /lib64/libc.so.6: version `GLIBC_2.25' not found (required by /var/task/main)
/var/task/main: /lib64/libc.so.6: version `GLIBC_2.32' not found (required by /var/task/main)
/var/task/main: /lib64/libc.so.6: version `GLIBC_2.34' not found (required by /var/task/main)
2023/07/02 07:47:37 exit status 1
这是一个典型的交叉编译问题,特别是当涉及到C依赖时。go run main.go 和 go build . 在本地工作是因为它们使用默认的本地环境(GOOS=darwin, GOARCH=arm64),并且能够找到本地的C库。当你设置 GOOS=linux 和 GOARCH=amd64 进行交叉编译时,CGO被禁用(CGO_ENABLED=0),但你的代码似乎依赖了C绑定。
从错误信息看,你使用了 confluent-kafka-go 库,这个库在底层依赖了 librdkafka 的C库。当 CGO_ENABLED=0 时,纯Go模式无法使用这些C绑定,导致 kafka.ConfigMap 和 kafka.NewProducer 等符号未定义。
解决方案是使用支持交叉编译的纯Go Kafka客户端,或者为交叉编译正确配置CGO。对于Lambda部署,推荐以下两种方法:
方法1:使用Docker进行构建(推荐)
创建Dockerfile,在Linux环境中构建:
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 在Linux容器内构建
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main .
# 使用scratch或alpine作为最终镜像
FROM alpine:latest
COPY --from=builder /app/main /main
ENTRYPOINT ["/main"]
方法2:使用纯Go的Kafka客户端
替换 confluent-kafka-go 为纯Go实现的库,如 segmentio/kafka-go:
// 替换前
import "github.com/confluentinc/confluent-kafka-go/kafka"
// 替换后
import "github.com/segmentio/kafka-go"
// 使用示例
func produceToKafka() {
writer := &kafka.Writer{
Addr: kafka.TCP("localhost:9092"),
Topic: "your-topic",
Balancer: &kafka.LeastBytes{},
}
err := writer.WriteMessages(context.Background(),
kafka.Message{
Key: []byte("key"),
Value: []byte("value"),
},
)
}
方法3:修改Makefile,在本地使用CGO交叉编译
需要安装交叉编译工具链:
BINARY_NAME=main
export GOOS=linux
export GOARCH=amd64
# 对于confluent-kafka-go,需要CGO
export CGO_ENABLED=1
# 设置交叉编译工具链(macOS上)
export CC=x86_64-linux-gnu-gcc
export CXX=x86_64-linux-gnu-g++
deploy:
# 需要先安装交叉编译工具:brew install FiloSottile/musl-cross/musl-cross
CC=x86_64-linux-musl-gcc CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "-linkmode external -extldflags -static" -o ${BINARY_NAME} .
zip -r function.zip main
aws lambda update-function-code --function-name "S3JSONDecomposer-Golang" --zip-file fileb://function.zip --region="af-south-1" | jq .
方法4:使用Lambda容器镜像
如果必须使用 confluent-kafka-go,考虑使用Lambda容器镜像,在构建时安装依赖:
FROM public.ecr.aws/lambda/provided:al2023 AS builder
# 安装构建依赖
RUN yum install -y gcc gcc-c++ make cmake
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# 构建
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o main .
# 最终镜像
FROM public.ecr.aws/lambda/provided:al2023
COPY --from=builder /app/main ${LAMBDA_TASK_ROOT}
CMD ["main"]
对于AWS Lambda部署,方法1(Docker构建)或方法2(使用纯Go库)是最简单可靠的。如果Kafka功能不是核心需求,强烈建议使用方法2切换到纯Go的Kafka客户端。


