在M1芯片的Docker上构建时遇到exec格式错误问题

在M1芯片的Docker上构建时遇到exec格式错误问题 大家好。

有点卡住了……我正在调用下面的 makefile,同时也包含我的 Dockerfile。 我遇到了以下错误:

docker run -it --network devlab  georgelza/goprod-avro:1.0.0 runs_avro.sh
exec /app/runs_avro.sh: exec format error

Dockerfile

FROM bitnami/golang:1.22 AS base_arm64

LABEL Author="George Leonard (georgelza@gmail.com)"
ENV GOPROXY=https://proxy.golang.org,direct
ENV GOSUMDB=sum.golang.org

WORKDIR /app

# pre-copy/cache go.mod for pre-downloading dependencies and only redownloading them in subsequent builds if they change

COPY go.mod ./
COPY go.sum ./
RUN go mod tidy

# RUN go get github.com/TylerBrock/colorjson
# RUN go get github.com/brianvoe/gofakeit
# RUN go get github.com/confluentinc/confluent-kafka-go/v2/kafka
# RUN go get github.com/confluentinc/confluent-kafka-go/v2/schemaregistry
# RUN go get github.com/confluentinc/confluent-kafka-go/v2/schemaregistry/serde
# RUN go get github.com/confluentinc/confluent-kafka-go/v2/schemaregistry/serde/avrov2
# RUN go get github.com/google/uuid
# RUN go get github.com/tkanos/gonfig
# RUN go get go.mongodb.org/mongo-driver/bson/bsonrw
# RUN go get go.mongodb.org/mongo-driver/mongo
# RUN go get go.mongodb.org/mongo-driver/mongo/options
# RUN go get go.mongodb.org/mongo-driver/mongo/readpref
# RUN go get google.golang.org/grpc/grpclog

###########START NEW IMAGE###################

FROM base_arm64 AS client

WORKDIR /app
COPY . .

RUN go build -v -o /app/cmd/ /app/cmd/main.go

#RUN GOOS=linux GOARCH=arm64 go build -v -o /app/cmd/ /app/cmd/main.go

###########START NE W IMAGE###################

FROM bitnami/golang:1.22 AS prod

WORKDIR /app
COPY --from=client /app/cmd/main /app/cmd/

COPY runs_avro.sh /app
ENV PATH=/app:/app/cmd:$PATH

CMD ["runs_avro.sh"]

Makefile

sudo docker build --platform linux/arm64 --no-cache -t ${BINARY_NAME}:${VERSION} .


5 回复

这看起来像是 runs_avro.sh 脚本的bug。


我尝试了这两种调用应用程序的方式:

#CMD ["main", "avro"]
# or
CMD ["runs_avro.sh"]

这是 runs_avro.sh 的内容:

. ./.pwd
#go run -v cmd/main.go avro
/app/cmd/main avro

虽然不完全确定具体原因……但这个方案确实可行…… 此外,在生产阶段使用 ubuntu:jammy 镜像,将最终生成的镜像大小从之前使用 golang:1.22 时的 500+ MB 降低到了 90 MB。

构建命令:

sudo docker build --platform linux/arm64 --no-cache -t ${BINARY_NAME}:${VERSION} .

Dockerfile

FROM golang:1.22 AS builder

ENV GOPROXY=https://proxy.golang.org,direct
ENV GOSUMDB=sum.golang.org

WORKDIR /build

COPY go.mod .
COPY go.sum .

RUN go mod download
COPY cmd/main.go .
COPY types ./types/
RUN go build -v -o ./main ./main.go

###########START NEW IMAGE###################

FROM ubuntu:jammy AS production

LABEL Author="George Leonard (georgelza@gmail.com)"

WORKDIR /app
COPY --from=builder /build/main .

CMD ["/app/main", "avro"]

我还尝试了以下内容作为 Dockerfile:

FROM bitnami/golang:1.22 AS base_arm64

LABEL Author="George Leonard (georgelza@gmail.com)"
ENV GOPROXY=https://proxy.golang.org,direct
ENV GOSUMDB=sum.golang.org
WORKDIR /app

# 预复制/缓存 go.mod,以便预下载依赖项,并且仅在后续构建中依赖项发生变化时才重新下载

COPY go.mod ./
COPY go.sum ./
RUN go mod tidy

###########开始新镜像###################

FROM base_arm64 AS client
WORKDIR /app
COPY . .
RUN GOOS=linux GOARCH=arm64 go build -v -o /app/cmd/ /app/cmd/main.go

###########开始新镜像###################
FROM bitnami/golang:1.22 AS prod
WORKDIR /app
COPY --from=client /app/cmd/main /app/cmd/

COPY runs_avro.sh /app
ENV PATH=/app:/app/cmd:$PATH

CMD ["main", "avro"]

这是一个典型的架构不匹配问题。你在M1芯片(ARM64架构)上构建了ARM64的Docker镜像,但runs_avro.sh脚本可能是在x86_64系统上创建的,或者脚本本身包含了不兼容的格式。

问题分析:

  1. runs_avro.sh脚本可能包含Windows换行符(CRLF)而不是Linux换行符(LF)
  2. 脚本可能没有正确的shebang(#!/bin/bash)
  3. 脚本文件权限不正确
  4. 脚本本身可能是为不同架构编译的二进制文件

解决方案:

1. 首先修复Dockerfile中的脚本处理:

FROM bitnami/golang:1.22 AS base_arm64

LABEL Author="George Leonard (georgelza@gmail.com)"
ENV GOPROXY=https://proxy.golang.org,direct
ENV GOSUMDB=sum.golang.org

WORKDIR /app

# 复制并修复脚本的换行符
COPY runs_avro.sh /app/
RUN sed -i 's/\r$//' /app/runs_avro.sh && \
    chmod +x /app/runs_avro.sh

# pre-copy/cache go.mod for pre-downloading dependencies
COPY go.mod go.sum ./
RUN go mod tidy

###########START NEW IMAGE###################

FROM base_arm64 AS client

WORKDIR /app
COPY . .

# 明确指定ARM64架构构建
RUN GOOS=linux GOARCH=arm64 go build -v -o /app/cmd/main /app/cmd/main.go

###########START NEW IMAGE###################

FROM bitnami/golang:1.22 AS prod

WORKDIR /app

# 确保脚本有正确的权限和格式
COPY --from=client /app/runs_avro.sh /app/
RUN chmod +x /app/runs_avro.sh && \
    sed -i 's/\r$//' /app/runs_avro.sh

COPY --from=client /app/cmd/main /app/cmd/
ENV PATH=/app:/app/cmd:$PATH

# 使用exec格式执行
CMD ["/app/runs_avro.sh"]

2. 或者,如果脚本很简单,直接在Dockerfile中创建:

# 在prod阶段创建脚本
FROM bitnami/golang:1.22 AS prod

WORKDIR /app

# 直接创建脚本,避免换行符问题
RUN echo '#!/bin/bash' > /app/runs_avro.sh && \
    echo 'cd /app' >> /app/runs_avro.sh && \
    echo './cmd/main' >> /app/runs_avro.sh && \
    chmod +x /app/runs_avro.sh

COPY --from=client /app/cmd/main /app/cmd/
ENV PATH=/app:/app/cmd:$PATH

CMD ["/app/runs_avro.sh"]

3. 检查并修复本地脚本文件:

在构建之前,先修复本地的runs_avro.sh文件:

# 转换换行符
sed -i 's/\r$//' runs_avro.sh

# 确保有正确的shebang
if ! head -1 runs_avro.sh | grep -q "^#!"; then
    echo '#!/bin/bash' | cat - runs_avro.sh > temp && mv temp runs_avro.sh
fi

# 设置执行权限
chmod +x runs_avro.sh

# 验证脚本格式
file runs_avro.sh

4. 使用更简单的CMD格式:

# 改为使用shell格式,让/bin/bash处理脚本
CMD /app/runs_avro.sh

关键修改:

  1. 添加了sed -i 's/\r$//'来移除可能的Windows换行符
  2. 明确添加了chmod +x设置执行权限
  3. 在CMD中使用绝对路径/app/runs_avro.sh
  4. 确保构建时明确指定GOARCH=arm64

构建命令保持不变:

sudo docker build --platform linux/arm64 --no-cache -t ${BINARY_NAME}:${VERSION} .
回到顶部