GitLab Runner命令失败时如何检查所用镜像?附多阶段CI问题排查
问题分析与解决方案
首先看你的Job日志里的关键信息:Using Shell executor...——这就是问题的核心!GitLab Runner的Shell executor是直接在宿主机的系统Shell中执行脚本,完全不会识别image:配置项。只有Docker、Kubernetes这类容器化的Executor才会根据image:字段拉取镜像,并在容器内部执行命令。所以你的reload阶段根本没用到指定的MQTT客户端镜像,而是直接在宿主机上跑pub命令,宿主机没有这个命令,自然报错。
一、如何检查阶段是否正确使用了指定镜像?
给你几个通用的验证方法,无论用什么Executor都适用:
在脚本中添加环境检查命令
在reload阶段的script开头加入以下命令,直观查看当前运行环境:reload: stage: reload image: efrecon/mqtt-client script: # 查看当前系统发行版(容器和宿主机的信息会不同) - cat /etc/os-release # 查看当前用户、工作目录 - whoami && pwd # 打印PATH环境变量,确认命令所在路径是否在PATH中 - echo $PATH # 检查是否在容器内(容器的cgroup会包含容器ID) - cat /proc/self/cgroup | head -n1如果是在
efrecon/mqtt-client容器内运行,/etc/os-release会显示Alpine Linux的信息(这个镜像基于Alpine),而宿主机的系统信息会和它不一致;同时/proc/self/cgroup的输出会包含容器相关的标识。查看GitLab Runner配置
登录到你的服务器,打开Runner的配置文件/etc/gitlab-runner/config.toml,查看executor字段的值:- 如果是
shell:image:配置完全无效,不会启动任何容器 - 如果是
docker:才会处理image:配置,拉取镜像并在容器内执行脚本
- 如果是
二、若镜像已正确使用,pub命令失败的可能原因?
假设已经确认在efrecon/mqtt-client容器内运行,那pub命令找不到的常见原因有这些:
PATH环境变量缺失:
pub命令存在于镜像中,但它所在的目录不在当前的PATH环境变量里。你可以先在本地执行docker run --rm efrecon/mqtt-client which pub找到命令的绝对路径,然后在脚本中用绝对路径执行,比如:script: - /usr/bin/pub -h mqtt.mydomain -t dash/reload -m "`date`"镜像版本不一致:
你本地测试用的镜像版本和GitLab Runner拉取的版本不匹配。比如你本地是最新版,但Runner拉取了旧版镜像,而旧版中没有pub命令或者命令路径不同。解决方法是指定具体的镜像标签,比如efrecon/mqtt-client:latest(或者你测试过的稳定版本号),避免版本差异。命令名称错误:
有些MQTT客户端镜像会用不同的命令名,比如mosquitto_pub而不是pub。你可以本地执行docker run --rm efrecon/mqtt-client ls /usr/bin查看镜像内的所有可执行命令,确认准确的命令名称。权限问题:
虽然官方镜像很少出现这种情况,但如果pub命令没有可执行权限,也会提示“找不到命令”。可以在脚本中先执行chmod +x /path/to/pub再运行命令,但这种情况概率极低。
针对你的具体解决方案
因为你用的是Shell executor,有两种解决思路:
- 修改Runner的Executor为Docker:
编辑/etc/gitlab-runner/config.toml,把executor = "shell"改成executor = "docker",并配置好Docker相关的参数(比如image = "docker:latest"等),这样image:配置就会生效。 - 在脚本中手动调用Docker运行命令:
保持Shell executor不变,直接在reload阶段的脚本中用docker run执行MQTT命令,和你本地测试的逻辑一致:reload: stage: reload script: - docker run --init --rm efrecon/mqtt-client pub -h mqtt.mydomain -t dash/reload -m "`date`"
内容的提问来源于stack exchange,提问作者WoJ

