概要
为精简docker容器镜像,采用Alpine镜像,需要手动编译glibc源代码
制作编译好的glibc二进制文件
获取glibc二进制文件构建工具
# 内网环境可下载该工具包手动上传到服务器
git pull https://github.com/sgerrand/docker-glibc-builder.git
该构建工具中需要采用wget命令从第三方仓库下载glibc源代码,由于内网环境无法访问第三方仓库,我们需要对构建工具做一些修改
修改Dockerfile
本例采用修改过的openeuler-x86镜像,在原生的基础上安装了一些常用的命令例如vim
、tar
、telnet
等
FROM euleros-x86:202405V1
ENV DEBIAN_FRONTEND=noninteractive \
GLIBC_VERSION=2.28 \
PREFIX_DIR=/usr/glibc-compat
COPY configparams /glibc-build/configparams
COPY builder /builder
# 内网环境 手动下载glibc源代码上传至根目录
COPY glibc-2.28.tar.gz /glibc-2.28.tar.gz
# gcc bison make m4 都是编译所必须的命令
RUN yum -y install gcc bison make m4
ENTRYPOINT ["/builder"]
修改bulider脚本
#!/usr/bin/env bash
set -eo pipefail; [[ "$TRACE" ]] && set -x
main() {
# 声明 version 和 prefix 如果不存在入参指定version和prefix就使用环境变量 GLIBC_VERSION 和 PREFIX_DIR
declare version="${1:-$GLIBC_VERSION}" prefix="${2:-$PREFIX_DIR}"
: "${version:?}" "${prefix:?}"
{
# 此处只删除了使用wget从第三方仓库下载glibc的步骤
tar zxf /glibc-$version.tar.gz
mkdir -p /glibc-build && cd /glibc-build
"/glibc-$version/configure" \
--prefix="$prefix" \
--libdir="$prefix/lib" \
--libexecdir="$prefix/lib" \
--enable-multi-arch \
--enable-stack-protector=strong
make && make install
tar --dereference --hard-dereference -zcf "/glibc-bin-$version.tar.gz" "$prefix"
} >&2
[[ $STDOUT ]] && cat "/glibc-bin-$version.tar.gz"
}
main "$@"
构建编译glibc的镜像
# 首先进入Dockerfile文件同级目录
docker build -t "euler-glibc:2.28" .
运行 euler-glibc:2.28 镜像
# docker run --rm --env STDOUT=1 euler-glibc:2.28 {version} {prefix} > glibc-bin.tar.gz
# 上述命令的 {version} 和{prefix}即 builder脚本的入参,如果在Dockerfile中写好了环境变量,这两个值可以不要
# 直接运行该镜像,编译时间可能比较长,要稍微等待一段时间
docker run --rm --env STDOUT=1 euler-glibc:2.28 > glibc-bin.tar.gz
# 编译完成就会在当前文件夹内获得 名为 glibc-bin.tar.gz 的 libc二进制文件
制作alpine镜像apk包
获取apk包构建工具
# 内网环境同样手动下载 2.28 版本APKBUILD 有问题,建议直接使用2.35版本
git https://github.com/sgerrand/alpine-pkg-glibc.git
获取alpine镜像并配置apk源
获取alpine镜像
可以直接从dockerhub拉取一个和alpine镜像,也可以下载rootfs自己构建,此处不赘述拉取过程配置apk源
# 此处是我自己构建的alpine镜像所以tag是v1,具体名字根据实际的填写 FROM alpine:v1 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && apk update && apk add build-base alpine-sdk
编写完成Dockerfile之后构建该镜像并命名为
alpine:v2
修改APKBUILD文件
# Maintainer: Sasha Gerrand <[email protected]>
pkgname="glibc"
pkgver="2.28"
_pkgrel="0"
pkgrel="0"
pkgdesc="GNU C Library compatibility layer"
arch="x86"
url="https://github.com/sgerrand/alpine-pkg-glibc"
license="LGPL"
source="$pkgname-bin.tar.gz
nsswitch.conf
ld.so.conf"
subpackages="$pkgname-bin $pkgname-dev $pkgname-i18n"
triggers="$pkgname-bin.trigger=/lib:/usr/lib:/usr/glibc-compat/lib"
package() {
echo "----------------------------package start"
echo "$pkgdir $srcdir $builddir"
mkdir -p "$pkgdir/lib" "$pkgdir/usr/glibc-compat/lib/locale" "$pkgdir"/usr/glibc-compat/lib64 "$pkgdir"/etc
cp -a "$srcdir"/usr "$pkgdir"
cp "$srcdir"/ld.so.conf "$pkgdir"/usr/glibc-compat/etc/ld.so.conf
rm "$pkgdir"/usr/glibc-compat/etc/rpc
rm -rf "$pkgdir"/usr/glibc-compat/bin
rm -rf "$pkgdir"/usr/glibc-compat/sbin
rm -rf "$pkgdir"/usr/glibc-compat/lib/gconv
rm -rf "$pkgdir"/usr/glibc-compat/lib/getconf
rm -rf "$pkgdir"/usr/glibc-compat/lib/audit
rm -rf "$pkgdir"/usr/glibc-compat/share
rm -rf "$pkgdir"/usr/glibc-compat/var
ln -s /usr/glibc-compat/lib/ld-linux-x86-64.so.2 ${pkgdir}/lib/ld-linux-x86-64.so.2
ln -s /usr/glibc-compat/lib/ld-linux-x86-64.so.2 ${pkgdir}/usr/glibc-compat/lib64/ld-linux-x86-64.so.2
ln -s /usr/glibc-compat/etc/ld.so.cache ${pkgdir}/etc/ld.so.cache
echo "----------------------------package end"
}
bin() {
echo "----------------------------bin start"
depends="$pkgname libgcc"
mkdir -p "$subpkgdir"/usr/glibc-compat
cp -a "$srcdir"/usr/glibc-compat/bin "$subpkgdir"/usr/glibc-compat
cp -a "$srcdir"/usr/glibc-compat/sbin "$subpkgdir"/usr/glibc-compat
echo "----------------------------bin end"
}
i18n() {
echo "----------------------------i18n start"
depends="$pkgname-bin"
arch="noarch"
mkdir -p "$subpkgdir"/usr/glibc-compat
cp -a "$srcdir"/usr/glibc-compat/share "$subpkgdir"/usr/glibc-compat
echo "----------------------------i18n end"
}
编写构建apk包的Dockerfile
FROM alpine:v2
LABEL maintainer="su.yingjun" email="[email protected]"
VOLUME ["/home/docker/alpine-pkg-glibc","/tmp"]
# 创建构建用户
RUN adduser -D packager && addgroup packager abuild && echo 'packager ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/packager && chmod o+w /tmp
#RUN adduser -D -s /bin/sh build && echo "build:build2024" | chpasswd && adduser build abuild && apk add alpine-sdk build-base abuild cmake git
COPY APKBUILD /home/packager/APKBUILD
COPY glibc-bin.tar.gz /home/packager/glibc-bin.tar.gz
COPY glibc-bin.trigger /home/packager/glibc-bin.trigger
COPY ld.so.conf /home/packager/ld.so.conf
COPY nsswitch.conf /home/packager/nsswitch.conf
USER packager
WORKDIR /home/packager
RUN abuild-keygen -n --append --install
运行构建apk包容器
# 此处挂载目录的原因是方便取出apk包和公钥文件,也可以使用docker cp取出
docker run --rm -it -v /home/docker/alpine-pkg-glibc:/tmp alpine-apk-build:v1 sh
# 下方为登录容器后的命令 保证处于 /home/packager 目录下
abuild checksum
abuild -r
# 执行完成之后会在构建目录下出现 packages目录,该目录中存放打包好的apk文件,公钥文件存放在当前用户家目录下的.abuild 文件夹中,执行cp命令拷贝到挂载的目录中
cp ~/.abuild/*.rsa.pub ~/packages/glibc*.apk /tmp
# 退出容器
exit
参考资料
- GNU下载地址
- https://github.com/sgerrand/alpine-pkg-glibc
- https://github.com/sgerrand/docker-glibc-builder
- Alpine apk源
- Alpine Wiki
- https://www.youtube.com/watch?v=ibeqoQpO33w&t=697s
附录
abuild -r 构建流程
abuild
是 Alpine Linux 的构建工具,用于构建 APK 包。执行 abuild -r
命令的全流程涉及多个步骤,包括准备构建环境、构建包、运行测试和创建最终的 APK 包。以下是 abuild -r
的详细流程:
1. 准备构建环境
更新并安装依赖
abuild
会根据APKBUILD
文件中的依赖定义安装构建所需的包。
初始化环境变量
- 设置必要的环境变量,如
srcdir
、builddir
、pkgdir
等。
2. 提取源代码
- 下载源代码:根据
APKBUILD
文件中的source
字段下载源代码和补丁文件。 - 校验文件完整性:通过校验和文件(如
sha512sums
)验证下载的文件是否完整。 - 解压源代码:将下载的源代码文件解压到
srcdir
。
3. 运行构建流程
prepare()
函数
- 这是一个可选的函数,用于在实际构建之前应用补丁或进行其他准备工作。
build()
函数
- 执行构建过程。这通常包括配置、编译和链接源代码。
- 具体步骤由
APKBUILD
文件中的build()
函数定义。例如,可能使用./configure
脚本、make
命令等。
4. 安装步骤
package()
函数
- 安装构建好的文件到
pkgdir
。 - 设置文件的权限和属性。
- 安装文档文件(如
README
、LICENSE
等)。
5. 创建 APK 包
abuild
会将pkgdir
中的内容打包成一个 APK 文件。- 生成的 APK 文件会放在
packages
目录下。
6. 签名 APK 包
- 如果你有签名密钥,
abuild
会对生成的 APK 包进行签名。签名密钥通常存储在~/.abuild/
目录中。
7. 运行测试(可选)
abuild
也可以运行测试(如果定义了check()
函数)。这通常在构建后执行,用于验证包的正确性。
8. 清理
- 清理临时文件和目录,确保构建环境干净。