概要
本文件採用glibc2.28版本作為示例,模擬內網環境無法訪問github等開源社群
為精簡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源
構建apk包需要用的apk-sdk,apk是alpine的包管理工具,此處需要獲取一個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 <alpine-pkgs@sgerrand.com>
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="i9xswanan@gmail.com"
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
dockerbuild -t alpine-apk-build:v1 .
執行構建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. 清理
- 清理臨時檔案和目錄,確保構建環境乾淨。