Skip to content

Latest commit

 

History

History
278 lines (204 loc) · 10.5 KB

README.md

File metadata and controls

278 lines (204 loc) · 10.5 KB

A docker base image builder for busybox:glibc

introduction

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.

Usage

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 docker基础镜像的仓库

本仓库是用于构建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多阶段编译支持。

  1. 第一阶段是以debian:buster-slim作为基础镜像,在其中安装相关类库、命令到环境中,然后创建一系列的软连接或拷贝到/usr/src/busybox/rootfs目录下。这一阶段称之为builder阶段,这一阶段的内容与Dockerfile.builder文件内容保持一致。
  2. 第二阶段以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

修改扩展说明

在了解到上述的说明之后,如果想要添加额外的依赖库,就很容易了,分为两种情况:

  1. 要添加的类库在/lib/$gccMultiarch目录下
  2. 要添加的类库在其他目录下

对于第一种情况,就比较简单,只需要在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构建脚本了。

  1. 第一步,在一开始的RUN命令中的apt-get install -y后面的列表中添加你需要安装的依赖库,或者重新写一条RUN命令来安装相关依赖。
  2. 第二步,就是添加依赖包的软连接到/lib/$gccMultiarch/
  3. 第三步,在lib-requires.txt添加类库名称

其中,二、三步可以直接合并为一步,在115行后面添加依赖库的位置。