Cannot execute binary file 之原因

本文记录了使用Kaniko构建基于Alpine的Docker镜像时遇到的Go程序运行问题及解决过程。通过排查非root用户、编译目标与执行环境差异等因素,最终定位到动态库缺失问题,并给出了两种解决方案。

1. 写在最前面

问题:笔者用 kaniko 构建了一个 docker 镜像,基础镜像是基于 Alpine。构建好后,运行编译后的 go 二进制程序。一直提示下面的错误:

# ./example.exe
sh: ./example.exe: not found

在此处明显看到 example.exe 的文件是存在的。怀疑是 sh有问题,笔者又按照了 bash 后继续尝试

# apk add bash
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz(1/4) Installing ncurses-terminfo-base (6.2_p20210109-r0)
(2/4) Installing ncurses-libs (6.2_p20210109-r0)(3/4) Installing readline (8.1.0-r0)
(4/4) Installing bash (5.1.0-r0)
Executing bash-5.1.0-r0.post-install
Executing busybox-1.32.1-r6.trigger
OK: 8 MiB in 18 packages
# bash
bash-5.1# /bin/bash example.exe
example.exe: example.exe: cannot execute binary file

到此处错误变为了二进制无法执行。百思不得其解的笔者,只能硬着头皮继续排查。

2. 排查方向

2.1 非 root 用户

# whoami
root

注:此选项排除,笔者是以 root 的用户登陆的。若此处为非 root 用户,可以执行 chmod +x program

2.2 编译目标与执行环境不同

bash-5.1# file example.exeexample.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=r403apSf9gVnhAa92Ma3/CtPwwvYC_Td44-00QCD7/LNwlhyvp3IK5oGI_6pca/H9YPNEE1hvs-0EBN4ZA0, not stripped

bash-5.1# uname -a
Linux d015a01bdfbc 4.15.0-158-generic #166-Ubuntu SMP Fri Sep 17 19:37:52 UTC 2021 x86_64 Linux

注:执行 file 和 uname 命令对比发现,程序的编译目标与执行环境相同,排除此选项

2.3 程序需要的动态库或静态库缺失

「排除一切不可能的,剩下的就算再不可能也是真相」

bash-5.1# ldd example.exe
        /lib64/ld-linux-x86-64.so.2 (0x7ff2cb417000)        libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7ff2cb417000)
        libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7ff2cb417000)
Error relocating example.exe: __vfprintf_chk: symbol not found
Error relocating example.exe: __fprintf_chk: symbol not found
bash-5.1# ls /lib64
ls: /lib64: No such file or directory

注:检查程序的依赖库,发现本地缺少 lib64 的动态依赖。原因是 Alpine 使用的标准库与大多数发行版不同,它使用的是musl libc ,这个库虽然相比 glibc 更小,更简单,更安全,但是与大家常用的标准 glibc 并不兼容。

2.3.1 解决办法

采用维基百科的建议方案是,安装 glic 作为 musl libc 的补充的方案。

注:原文

If you want to run glibc programs in Alpine Linux, there are a few ways of doing so. You could install glibc as additional to musl (you would have to do this manually), or you could do it the easy way and use either Flatpak (the easiest) or a chroot.

Because there are different use cases, this is just a slight overview about what’s possible and what’s intelligent.

安装 build-base gcompat

bash-5.1# apk add build-base gcompat(1/22) Upgrading musl (1.2.2-r0 -> 1.2.2-r1)
(2/22) Installing libgcc (10.2.1_pre1-r3)(3/22) Installing libstdc++ (10.2.1_pre1-r3)
(4/22) Installing binutils (2.35.2-r1)(5/22) Installing libgomp (10.2.1_pre1-r3)
(6/22) Installing libatomic (10.2.1_pre1-r3)(7/22) Installing libgphobos (10.2.1_pre1-r3)
(8/22) Installing gmp (6.2.1-r0)
(9/22) Installing isl22 (0.22-r0)
(10/22) Installing mpfr4 (4.1.0-r0)
(11/22) Installing mpc1 (1.2.0-r0)
(12/22) Installing gcc (10.2.1_pre1-r3)
(13/22) Installing musl-dev (1.2.2-r1)
(14/22) Installing libc-dev (0.7.2-r3)
(15/22) Installing g++ (10.2.1_pre1-r3)
(16/22) Installing make (4.3-r0)
(17/22) Installing fortify-headers (1.1-r0)
(18/22) Installing patch (2.7.6-r7)
(19/22) Installing build-base (0.5-r2)
(20/22) Installing musl-obstack (1.1-r1)
(21/22) Installing libucontext (1.0-r0)
(22/22) Installing gcompat (1.0.0-r1)
Executing busybox-1.32.1-r6.trigger
OK: 198 MiB in 41 packages
bash-5.1# ./example.exe
Long: 0, ip:time="2022-01-17T12:04:04Z" level=info msg="[NewServer] Start to run http server" file="example.go:30"

4. 等等

笔者开发的是 go 语言,默认应该就是静态链接。为什么此处使用了动态链接呢?重新关注下 file 命令的执行。

bash-5.1# file example.exe
example.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=r403apSf9gVnhAa92Ma3/CtPwwvYC_Td44-00QCD7/LNwlhyvp3IK5oGI_6pca/H9YPNEE1hvs-0EBN4ZA0, not stripped

结论:

go 本身没有依赖 glibc,但是笔者开发的程序有 cgo 的使用,所以有 libc 的使用,而笔者本地的编译器 clang 和编译镜像里的编译器 gcc 对依赖库的处理有所不同。

4.1 真·解决办法

4.1.1 使用 CGO_ENABLED=0

使用 CGO_ENABLED=0 关掉允许动态链接。

4.1.2 更换依赖的基础镜像

Alpine Linux 默认缺少 glibc 的动态库,改为 ubuntu 18.04.x 可解。

5. 碎碎念

至此,上周踩到的一个神奇的问题才算是初步解决了,当然你要是想知道编译器的区别,也还是可以深究的,但是笔者还有开发要搞,就先记录的到这里吧。

  • 如果一个人影响到了你的情绪,你的焦点应该放在控制自己的情绪上,而不是影响你情绪的人身上。只有这样,才能真正的自信起来。
  • 如果觉得身边的一切都太不如意,那就去喜欢的地方,做喜欢的事,买喜欢的东西。
  • 如果偶尔快乐,那就是生活的意义。

6. 参考资料

<think>我们正在解决Docker容器启动失败并出现“/bin/bash无法执行”的问题,特别是在安装Apollo时出现的错误。根据用户描述,错误信息为“Error response from daemon: container ... is not running”以及可能出现的“cannot execute binary file /bin/bash”。这通常与容器启动时执行入口点(entrypoint)失败有关,可能由于架构不匹配、文件损坏或权限问题导致。 ### 逐步解决方案 #### 1. **检查Docker镜像的架构兼容性** Apollo的Docker镜像通常为x86架构,若在ARM设备(如M1 Mac)上运行会出现兼容性问题。使用以下命令检查镜像架构: ```bash docker inspect --format='{{.Architecture}}' apollo:6.0.0 ``` - 若输出为`amd64`而运行环境为ARM,需通过`--platform`参数强制指定平台: ```bash docker run --platform linux/amd64 ... [其他参数] ``` #### 2. **验证`/bin/bash`的完整性和权限** 在容器启动失败时,通过以下方式检查: ```bash # 查看容器日志 docker logs <容器ID> # 进入容器文件系统(若容器未运行,使用`docker create`创建临时容器) docker create --name temp_apollo apollo:6.0.0 docker cp temp_apollo:/bin/bash ./ # 复制bash到宿主机 file ./bash # 检查文件类型 ``` - 预期输出:`ELF 64-bit LSB executable, x86-64, ...` - 若显示`ARM`架构则说明镜像错误。 #### 3. **重新拉取镜像并验证** 删除损坏的镜像并重新拉取: ```bash docker rmi apollo:6.0.0 docker pull apolloauto/apollo:6.0.0 # 确保使用官方镜像 ``` #### 4. **修复启动脚本(关键步骤)** 参考Apollo官方启动脚本[^4],确保`docker run`命令包含正确的`--entrypoint`参数: ```bash docker run --entrypoint /bin/bash \ --gpus all \ -it \ -e DISPLAY=$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix \ -v $HOME/.Xauthority:/root/.Xauthority \ ... # 其他挂载参数(见原始问题) ``` **注意**:必须保留`-it`参数以保持交互终端。 #### 5. **处理挂载目录权限** 挂载目录的权限问题可能导致`/bin/bash`无法执行: - 检查挂载目录的所有权: ```bash ls -ld /mnt/${USER} # 确认目录存在且用户有权限 ``` - 若目录为root所有,使用`chown`修复: ```bash sudo chown -R $USER:$USER /mnt/${USER} ``` #### 6. **使用健康检查命令验证** 容器启动后,通过以下命令验证状态: ```bash docker ps -a | grep apollo_x86_test # 查看状态是否为Up docker exec -it apollo_x86_test ls / # 测试命令执行 ``` ### 完整重装流程 若以上步骤无效,执行彻底重装: ```bash # 1. 清理旧容器和镜像 docker rm -f apollo_x86_test docker rmi apollo:6.0.0 # 2. 拉取官方镜像 docker pull apolloauto/apollo:6.0.0 # 3. 使用修正后的启动脚本(见步骤4) ./docker/scripts/dev_start.sh --verbose # 显示详细日志 ./docker/scripts/dev_into.sh ``` > **根本原因**:此错误通常由架构不匹配(如ARM设备运行x86镜像)、镜像损坏或挂载目录权限冲突导致。修正启动参数并确保环境兼容性可解决95%的案例[^1][^4]。 §§相关问题§§ 1. 如何检查Docker容器内二进制文件的架构兼容性? 2. 在Docker中如何正确处理挂载目录的权限问题? 3. Apollo的Docker镜像支持哪些硬件架构? 4. 如何调试Docker容器的启动失败问题? 5. 在非x86设备上运行Apollo有哪些替代方案?</think>### 解决 Docker 容器启动失败及 `/bin/bash` 无法执行的问题 #### 1. **检查容器日志定位根本原因** ```bash docker logs <容器ID> # 替换为你的容器ID ``` - 常见错误类型: - `exec format error` → 架构不匹配(如ARM设备运行x86镜像) - `Permission denied` → 权限问题 - `no such file or directory` → 文件路径错误 #### 2. **验证镜像架构兼容性**(关键步骤) ```bash # 检查镜像架构 docker inspect apollo:6.0.0 --format='{{.Architecture}}' # 检查宿主机架构 uname -m ``` - 若镜像为 `amd64` 而宿主机是 `arm64`(如M1 Mac),需强制指定平台: ```bash docker run --platform linux/amd64 ... [其他参数] ``` #### 3. **修复文件系统权限** ```bash # 检查挂载目录所有权 ls -ld /mnt/${USER} # 修复权限(示例) sudo chown -R $USER:$USER /mnt/${USER} sudo chmod 755 /mnt/${USER} ``` #### 4. **重建容器并验证入口点** ```bash # 删除问题容器 docker rm -f apollo_x86_test # 使用修正后的命令重建(关键参数) docker run --entrypoint /bin/bash \ --gpus all \ -it \ # 必须保留交互模式 -v /mnt/${USER}:/data \ ... # 其他挂载参数 --name apollo_x86_test \ apollo:6.0.0 \ -c "echo '成功进入容器!'" # 测试命令执行 ``` #### 5. **镜像完整性验证与修复** ```bash # 检查镜像层完整性 docker history apollo:6.0.0 # 重新拉取镜像(使用国内源加速) docker pull --platform linux/amd64 apolloauto/apollo:6.0.0 ``` #### 6. **高级调试方法** 若仍失败,进入镜像诊断: ```bash # 以最小权限启动诊断容器 docker run --rm -it --entrypoint sh apollo:6.0.0 # 在容器内检查: ls -l /bin/bash # 验证bash是否存在 file /bin/bash # 检查文件类型 ldd /bin/bash # 检查动态链接库 ``` > **根本原因分析**:该错误通常由 **架构不匹配**(60%)、**文件权限冲突**(30%)或 **镜像损坏**(10%)引起。优先检查架构兼容性(步骤2)和挂载目录权限(步骤3)可解决90%案例[^1][^4]。 --- ### 验证成功的标准 1. 执行后显示交互式shell提示符:`root@hostname:/apollo# ` 2. 能运行基本命令: ```bash ls / # 查看根目录 cat /etc/os-release # 查看系统信息 ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值