Docker 使用 alpine 构建镜像时出现添加的可执行文件无法使用的问题

Docker 使用 alpine 构建镜像时出现添加的可执行文件无法使用的问题

问题起因

笔者今天第一次使用 alpine 作为 docker 的基础镜像,只知道 alpine 是一个极其精简的 Linux 版本,没有更多地去了解。笔者像平时一样用 g++ 编译程序,在本地运行时正常,但在 Dockerfile 中使用 COPY 命令添加后,却出现了无法运行的问题,具体体现为:

$ docker run mazeprog:latest 
/bin/sh: /opt/maze: not found

对应的 Dockerfile 为:

From alpine:latest
COPY ./maze /opt/
COPY ./m2 /opt
RUN chmod 0777 /opt/maze
CMD /opt/maze /opt/m2

其中,maze 是一个 x86_64 的可执行 ELF 文件,m2 是一个主程序使用的文本文件。

解决思路

出现了 not found 的问题后,立刻想到两个可能的原因,可以试着先排查一下。
首先是,是否正确地放置到了预期的位置?这个通过基于构建的镜像运行一下 ls 命令就可以知道:

$ docker run mazeprog ls /opt -l 
total 28
-rw-r--r--    1 root     root           641 Apr 11 14:01 m2
-rwxrwxrwx    1 root     root         23448 Apr 11 14:01 maze

可以看到确实是正确放到了预期的位置 /opt 中。第二个原因是权限问题,上面的输出中可以看到我已经给可执行程序 maze 赋予了 0777 最高权限,但还是无法运行,是什么原因呢?
搜索了很多相同的问题后,问题并没有解决,于是突然想到第三种可能,会不会是平台或运行时环境的问题?关于平台无需多虑,既然是在一台机器上编译和拉取基础镜像制作的,应该不会有问题,但是当我用 file 命令查看 maze 的文件类型时,有一个信息引起了我的注意:dynamically linked
这表示我的程序是动态链接的(在大多数时候我们都默认用 gcc/g++ 编译动态链接的程序,具体不展开介绍),而动态链接就需要依赖运行时环境。在正常的系统中,我们肯定都是安装了 C 程序所需要的那些库的,然而 alpine 不一样。
作为最精简的 Linux 基础镜像,alpine 只有 5MB 多一点,只带了最基本的一些命令所对应的 GNU 程序,并没有携带 C 的运行时环境,然而在执行程序时系统或 docker 并不会告诉你因为缺少运行时环境所以跑不起来,取而代之的则是 not found
要解决这个问题,就要使用静态链接来编译这个程序,在 gcc/g++ 中则加入 -static 选项:

g++ -o ./maze -static main.cpp MazeStack.cpp 

保持上面的 Dockerfile 不变,build 一下然后 run,果然程序成功在 docker 里运行起来了,问题解决。

原理剖析

这里涉及到一个程序的加载运行原理:动态链接的程序在编译阶段,只将调用的库函数名和相关重定位信息记录到 ELF 可执行文件中,当程序载入内存运行时,加载器和链接器会根据记录的动态链接信息调入 OS 中的相关运行时的库,和程序动态链接后运行,所以这里就有一个对运行时动态链接库的依赖,但是在 alpine 中并不带有。
反之,静态链接的程序则在编译阶段即将程序会用到的所有链接库函数全部取出来放到程序中作为程序的一部分。静态链接的好处就是不需要程序的运行环境有运行时环境的支持,即可用自身所带的最小需要的库函数等载入运行,这在嵌入式环境中是非常频繁用到的,此外在对运行时环境中某些库版本有需求时,为了保证程序在不同环境下的运行会用到。其坏处则在于当程序涉及的库函数较多时,会花费更多的时间来编译(其实时间就是花在了往程序里附带这些库函数的机器码上)且编译出来的程序和相同版本的动态链接出来的程序相比体积更大,这个很容易理解,毕竟带了那些库函数在自身的 ELF 文件中。

个人主页
2020.4.12

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了构建 Kafka 的 Docker 镜像,你可以按照以下步骤进行操作: 1. 首先,创建一个新的目录来存放你的 Dockerfile 和其他相关文件。 2. 在该目录下创建一个名为 Dockerfile 的文件,并使用文本编辑器打开。 3. 在 Dockerfile 中,使用以下基础镜像作为起点: ```Dockerfile FROM openjdk:8-jre-alpine ``` 4. 安装 Kafka 的依赖项和其他必要的工具,可以使用以下命令: ```Dockerfile RUN apk add --no-cache bash curl jq ``` 5. 下载并解压 Kafka 的二进制文件。你可以在 Kafka 的官方网站上找到可用的版本。使用以下命令: ```Dockerfile ENV KAFKA_VERSION=<kafka_version> ENV SCALA_VERSION=<scala_version> RUN wget https://downloads.apache.org/kafka/${KAFKA_VERSION}/kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz && \ tar -xzf kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz -C /opt && \ rm kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz ``` 请将 `<kafka_version>` 和 `<scala_version>` 替换为适当的 Kafka 版本和 Scala 版本。 6. 设置 Kafka 相关的环境变量,例如 ZooKeeper 的连接地址等。使用以下命令: ```Dockerfile ENV KAFKA_HOME=/opt/kafka_${SCALA_VERSION}-${KAFKA_VERSION} ENV PATH=${KAFKA_HOME}/bin:$PATH ENV KAFKA_ZOOKEEPER_CONNECT=localhost:2181 ENV KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 ENV KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 ``` 你可以根据你的需求修改这些环境变量。 7. 将 Kafka 的启动脚本添加到容器中,并设置可执行权限。使用以下命令: ```Dockerfile COPY start-kafka.sh /usr/bin/start-kafka.sh RUN chmod +x /usr/bin/start-kafka.sh ``` 8. 创建一个用于存储 Kafka 数据的目录,并设置适当的权限。使用以下命令: ```Dockerfile RUN mkdir -p /var/lib/kafka/data RUN chmod -R 777 /var/lib/kafka/data ``` 9. 定义容器启动要执行的命令。使用以下命令: ```Dockerfile CMD ["start-kafka.sh"] ``` 10. 保存并关闭 Dockerfile 文件。 11. 在相同的目录下,创建一个名为 `start-kafka.sh` 的脚本文件,并使用文本编辑器打开。 12. 在 `start-kafka.sh` 脚本文件中,添加以下内容: ```bash #!/bin/bash $KAFKA_HOME/bin/zookeeper-server-start.sh $KAFKA_HOME/config/zookeeper.properties & $KAFKA_HOME/bin/kafka-server-start.sh $KAFKA_HOME/config/server.properties ``` 13. 保存并关闭 `start-kafka.sh` 文件。 14. 在终端中,导航到你的 Dockerfile 所在的目录。 15. 使用以下命令来构建 Docker 镜像: ```bash docker build -t kafka:latest . ``` 16. 构建完成后,你可以使用以下命令来运行 Kafka 容器: ```bash docker run -d --name kafka -p 9092:9092 kafka:latest ``` 以上就是构建 Kafka Docker 镜像的基本步骤。你可以根据需要进行调整和优化。注意确保你已经安装了 Docker 并具有适当的权限来执行这些操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值