This is a docker file repo to build a minimal busybox image with required glibc libraries, which can be used as a base image to build bussiness images.
You can add your requires lib, then 'Docker.builder' will search the list of requires lib in /lib/$(gcc -print-multiarch)/, if not exist in, you should modify 'Docker.builder' after line 115 to add it, it should be a list split by line seprator.
To build and test the image, you should run this command in terminal:
sh buildx.sh
After build, it will generate one image that supports multiple architectures such as amd64, arm64, arm/v7, arm/v6, i386, riscv64, ppc64le, s390x. The Image is named as: ${registry}/busybox:glibc
, $registry
is defined in file buildx.sh.
- busybox:glibc: it is the target image, which based on scratch image, and copy files from path
/usr/share/busybox/rootfs
that generated by stage 1 named builder, to root path '/'.
本仓库是用于构建glibc的最小化busybox基础镜像,大部分源代码来自busybox官方仓库:https://github.com/docker-library/busybox。
busybox:glibc镜像大小约28M。
2021-6-17更新,升级busybox版本,增加具有版本号表示的镜像tag并推送至仓库;
2021-4-22更新,新增多平台架构编译支持(buildx), 目前支持以下平台架构:
- arm64/aarch64
- amd64/x86_64
- riscv64
- ppc64le, linux/
- s390x, linux/
- i386
- arm/v7
- arm/v6
同时,整个构建过程不再分builder和run两个步骤,而是使用了多阶段编译。
2021-4-13更新,新增类库支持:
- libelf*.so.*
- libmnl*.so.*
- libcap*.so.*
- libpcre*.so.*
并增加了ip、ethtool命令,上面这几个类库也是为这两个命令提供支持的。
2021-4-2更新说明。
目前,构建的busybox镜像中含有以下类库:
- libnss*.so.*
- libc*.so.*
- libstdc++.so.
- libpthread*.so.*
- libgcc_s*.so.*
- libz*.so.*
- libdl*.so.*
- librt*.so.*
- libm*.so.*
- libnuma*.so.*
分别列出在lib.requires.txt文件中,按行分隔,每一行是一个类库的名称,注意:只需要给出类库名称前缀即可,不需要写“*.so.*”。
这些类库一般存在于/user/lib/$(gcc -print-multiarch)/
目录下,然后软连接到目录/lib/$(gcc -print-multiarch)
,因此,如果你需要的类库在这个目录下,只需要在lib-requres.txt文件中添加类库名称即可。
否则,你需要修改Dockerfile.builder文件,添加相关依赖库。具体修改方法见修改扩展说明。
构建之前,需要确保已安装最新版docker(包含buildx命令)。
首先验证binfmt_misc是否已经开启:
$ ls -al /proc/sys/fs/binfmt_misc/
总用量 0
drwxr-xr-x. 2 root root 0 6月 17 15:53 .
dr-xr-xr-x. 1 root root 0 5月 21 14:48 ..
-rw-r--r--. 1 root root 0 6月 17 15:53 qemu-aarch64
-rw-r--r--. 1 root root 0 6月 17 15:53 qemu-arm
-rw-r--r--. 1 root root 0 6月 17 15:53 qemu-mips64
-rw-r--r--. 1 root root 0 6月 17 15:53 qemu-mips64el
-rw-r--r--. 1 root root 0 6月 17 15:53 qemu-ppc64le
-rw-r--r--. 1 root root 0 6月 17 15:53 qemu-riscv64
-rw-r--r--. 1 root root 0 6月 17 15:53 qemu-s390x
--w-------. 1 root root 0 6月 17 15:53 register
-rw-r--r--. 1 root root 0 6月 17 15:53 status
在列出的"qemu-*"的文件中,输出看一下其状态:
$ cat /proc/sys/fs/binfmt_misc/qemu-arm
enabled
interpreter /usr/bin/qemu-arm
flags: OCF
offset 0
magic 7f454c4601010100000000000000000002002800
mask ffffffffffffff00fffffffffffffffffeffffff
显示为enabled,则表示该平台已经开启支持。
如果/proc/sys/fs/binfmt_misc/
目录下没有qemu-*
开头的文件,说明还未开启qemu支持,这需要开启binfmt_misc:
执行下面这个命令:
docker run --privileged --rm tonistiigi/binfmt --install all
然后在重新验证binfmt_misc是否已经开启。
然后创建多平台构建器,首先查看当前的构建器:
docker context ls
新增一个构建器:
docker buildx create --use --name mybuilder
启动该构建器:
docker buildx inspect mybuilder --bootstrap
查看构建器及其所支持的cpu架构:
docker buildx ls
最后直接在shell终端进入本目录执行脚本命令进行构建:
sh buildx.sh
执行完成后将会生成支持多平台架构的镜像(1个,同名)并推送到公有仓库或者私有仓库(具体取决于buildx.sh中定义的registry的值,它指定了镜像所属的注册服务器registry):
- busybox:glibc:这个镜像以scratch(一个虚拟镜像,实际上不存在)为基础镜像,编译生成一个busybox的最小化镜像,它仅包含必要的类库(具体有哪些可以看lib-requires.txt),。
注意:busybox:glibc是最终用于构建其他业务镜像的基础镜像。
编译构建过程不同于之前的方式,而是采用了Dockerfile多阶段编译支持。
- 第一阶段是以debian:buster-slim作为基础镜像,在其中安装相关类库、命令到环境中,然后创建一系列的软连接或拷贝到
/usr/src/busybox/rootfs
目录下。这一阶段称之为builder阶段,这一阶段的内容与Dockerfile.builder文件内容保持一致。 - 第二阶段以scratch这个虚拟镜像为基础,拷贝builder阶段做好的
/usr/src/busybox/rootfs
目录到根目录,从而构建出一个最小化的busybox镜像。这一阶段内容与Dockerfile.run文件内容保持一致。
构建Image的大部分源代码来自于busybox官方仓库,本仓库对其做了些修改,加入了一些额外的类库和命令。如果感兴趣,可以研究busybox官方仓库中的说明和源码,以了解整个busybox镜像打包制作的过程。
由于官方仓库中构建的glibc镜像缺少部分so类库,不太适合直接用来打包C/C++ 程序,尤其是用到一些其他类库,比如zlib、numactl、bzip、ssl时,我们可以很方便的修改builder阶段,将使用到的类库增加到镜像中,因此我对官方源文件Dockerfile.builder
其中部分逻辑进行了修改,修改的内容区域如下:
RUN set -eux; \
apt-get update; \
apt-get install -y \
bzip2 \
curl \
gcc \
gnupg dirmngr \
make \
numactl \
zlib1g \
; \
gccMultiarch="$(gcc -print-multiarch)"; \
ln -vL /usr/lib/$gccMultiarch/*.so.* /lib/$gccMultiarch/; \
rm -rf /var/lib/apt/lists/*
还有另外一部分:
gccMultiarch="$(gcc -print-multiarch)"; \
requiredLibs=$(cat lib-requires.txt | sed -e 's/[,:;\|\t\r\n ]*//g' | awk '{print "/lib/'"$gccMultiarch"'/"$0"*.so.*"}'); \
set -- \
rootfs/bin/busybox \
rootfs/bin/getconf \
/lib/"$gccMultiarch"/libnss*.so.* \
# needed libs of glibc, such as libc, libthread and others: https://stackoverflow.com/a/11210463/433558
$requiredLibs \
; \
说明:
requiredLibs=$(cat lib-requires.txt | sed -e 's/[,:;\|\t\r\n ]*//g' | awk '{print "/lib/'"$gccMultiarch"'/"$0"*.so.*"}');
这行命令用来获取lib-requires.txt中列出的类库名称,并保存在变量$requiredLibs中。
set -- args
命令的作用是:在当前执行上下文中,将--
后面的args
参数依次设置到当前的作用域中,后续就可以使用$0,$1,$2...$#来获取这些参数。
在这部分代码之后,有一个循环遍历操作:
while [ "$#" -gt 0 ]; do \
f="$1"; shift; \
fn="$(basename "$f")"; \
if [ -e "rootfs/lib/$fn" ]; then continue; fi; \
if [ "${f#rootfs/}" = "$f" ]; then \
if [ "${fn#ld-}" = "$fn" ]; then \
ln -vL "$f" "rootfs/lib/$fn"; \
else \
cp -v "$f" "rootfs/lib/$fn"; \
fi; \
fi; \
ldd="$(ldd "$f" | awk ' \
$1 ~ /^\// { print $1; next } \
$2 == "=>" && $3 ~ /^\// { print $3; next } \
')"; \
set -- "$@" $ldd; \
done;
目的很简单,就是依次取出上面设置的参数(即列出的类库名称),然后将其软连接或者拷贝到rootfs/lib
目录下,并挂载到动态链接库ldd.so
。
在了解到上述的说明之后,如果想要添加额外的依赖库,就很容易了,分为两种情况:
- 要添加的类库在
/lib/$gccMultiarch
目录下 - 要添加的类库在其他目录下
对于第一种情况,就比较简单,只需要在lib-requires.txt
中列出需要依赖的库的basename即可。
对于第二种情况,一种比较简单的处理办法是:
找到对应的类库位置,将其通过软连接连接到/lib/$gccMultiarch
目录下,在builder阶段中添加RUN
命令,添加软连接命令就可以,然后在lib-requires.txt
文件中添加这个类库名称。
或者,你也可以直接在Dockerfile.builder
(builder阶段)中修改,在第115行之后,直接添加该类库的位置,形如:
/user/local/lib/xxx*.so.*
最后执行sh buildx.sh
编译构建。
由于此分支采用多阶段编译,因此不会生成builder阶段的镜像(其实是存在的,不过是匿名的),如果想要对builder阶段进行修改,加入一些其他的类库,需要在编写builder阶段的逻辑之前,进行测试、调试,以便找到依赖的类库所在位置。
因此,Dockerfile.builder就是用来提供测试使用的。
首先,你需要构建一个busybox:glibc-builder镜像:
docker build -t "busybox:glibc-builder" -f Dockerfile.builder .
然后启动一个容器:
docker run -it --rm busybox:glibc-builder /bin/bash
在交互模式的shell终端中,进行下载安装类库,比如你可以使用apt-get
命令进行安装,也可以使用wget
命令下载dpkg
安装包,再使用dpkg -i [pkg]
来进行安装,或者,直接下载源代码编译安装。
安装完成后不知道相关类库在哪里?
whereis xxx
这个命令可以帮你查找相关类库存在的位置,或者使用find命令来查找。
一旦在这个容器中调试ok,那么就可以回来修改Dockerfile.builder
构建脚本了。
- 第一步,在一开始的
RUN
命令中的apt-get install -y
后面的列表中添加你需要安装的依赖库,或者重新写一条RUN
命令来安装相关依赖。 - 第二步,就是添加依赖包的软连接到
/lib/$gccMultiarch/
- 第三步,在lib-requires.txt添加类库名称
其中,二、三步可以直接合并为一步,在115行后面添加依赖库的位置。