GitLab CI运行容器时出现exec: "sh": executable file not found in $PATH错误,请求排查问题根源(Dockerfile或GitLab CI配置)
问题根源与解决方案
你的错误是Dockerfile特性与GitLab CI配置共同作用导致的,下面具体拆解并给出解决办法:
为什么会报错?
1. Dockerfile的核心限制:scratch镜像无任何系统工具
你的最终镜像基于scratch——这是一个完全空的基础镜像,不包含任何操作系统文件,既没有sh这类shell解释器,也没有ls、cp这类基础命令。
你本地用docker run --rm -ti ...能正常运行,是因为这个命令默认会触发镜像的ENTRYPOINT(也就是你的/usr/bin/app-cli二进制文件),不需要依赖shell。
2. GitLab CI的运行逻辑需要shell
在你的GitLab CI配置里:
- 你设置了
entrypoint: [""],覆盖了镜像原本的入口命令 - GitLab CI执行
script段时,默认会尝试调用sh -c "你的脚本内容"来解析执行命令(比如这里的ls)
但你的容器里根本没有sh,自然会抛出exec: "sh": executable file not found的错误。
两种解决方案,根据你的需求选择
方案一:如果Validation job只需要测试CLI工具功能
如果你的验证步骤不需要执行ls这类shell命令,只是要运行你的app-cli做校验,那可以直接调用二进制文件,不需要修改Dockerfile,调整.gitlab-ci.yml即可:
stages: - validation validation: image: gitlab.mycompany.net:4567/myteam/app-cli:latest stage: validation rules: - if: '$CI_MERGE_REQUEST_IID' script: - /usr/bin/app-cli [你的测试参数] # 比如/app-cli --version或者你的校验命令
这里不需要设置entrypoint: [""],让GitLab CI把script里的内容作为参数传给镜像原本的ENTRYPOINT即可。
方案二:如果确实需要在容器里执行shell命令
如果你的验证步骤必须用到ls、echo这类基础命令,那你需要修改Dockerfile,把最终镜像从scratch换成一个最小的带shell的镜像,比如alpine:latest(只有几MB大小,几乎不增加镜像体积):
FROM golang:1.16-alpine AS builder ENV \ OUTDIR='/out' \ GO111MODULE='on' WORKDIR /app COPY go.mod /app/ COPY go.sum /app/ RUN go mod download COPY . /app/ RUN CGO_ENABLED=0 GOBIN=${OUTDIR}/usr/bin/ go install . # 替换scratch为alpine FROM alpine:latest COPY --from=builder /out/ / ENTRYPOINT ["/usr/bin/app-cli"]
修改后,你的镜像会自带sh和基础工具,GitLab CI里的script: - ls就能正常执行了。
内容的提问来源于stack exchange,提问作者Roy
相关产品推荐
相关产品推荐

