01.Alpine編譯glibc

沙溪發表於2024-06-02

概要

本文件採用glibc2.28版本作為示例,模擬內網環境無法訪問github等開源社群

為精簡docker容器映象,採用Alpine映象,需要手動編譯glibc原始碼

製作編譯好的glibc二進位制檔案

獲取glibc二進位制檔案構建工具

# 內網環境可下載該工具包手動上傳到伺服器
git pull https://github.com/sgerrand/docker-glibc-builder.git

該構建工具中需要採用wget命令從第三方倉庫下載glibc原始碼,由於內網環境無法訪問第三方倉庫,我們需要對構建工具做一些修改

修改Dockerfile

本例採用修改過的openeuler-x86映象,在原生的基礎上安裝了一些常用的命令例如vimtartelnet

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倉庫地址。

  1. 獲取alpine映象
    可以直接從dockerhub拉取一個和alpine映象,也可以下載rootfs自己構建,此處不贅述拉取過程

  2. 配置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

參考資料

  1. GNU下載地址
  2. https://github.com/sgerrand/alpine-pkg-glibc
  3. https://github.com/sgerrand/docker-glibc-builder
  4. Alpine apk源
  5. Alpine Wiki
  6. https://www.youtube.com/watch?v=ibeqoQpO33w&t=697s

附錄

abuild -r 構建流程

abuild 是 Alpine Linux 的構建工具,用於構建 APK 包。執行 abuild -r 命令的全流程涉及多個步驟,包括準備構建環境、構建包、執行測試和建立最終的 APK 包。以下是 abuild -r 的詳細流程:

1. 準備構建環境

更新並安裝依賴

  • abuild 會根據 APKBUILD 檔案中的依賴定義安裝構建所需的包。

初始化環境變數

  • 設定必要的環境變數,如 srcdirbuilddirpkgdir 等。

2. 提取原始碼

  • 下載原始碼:根據 APKBUILD 檔案中的 source 欄位下載原始碼和補丁檔案。
  • 校驗檔案完整性:透過校驗和檔案(如 sha512sums)驗證下載的檔案是否完整。
  • 解壓原始碼:將下載的原始碼檔案解壓到 srcdir

3. 執行構建流程

prepare() 函式

  • 這是一個可選的函式,用於在實際構建之前應用補丁或進行其他準備工作。

build() 函式

  • 執行構建過程。這通常包括配置、編譯和連結原始碼。
  • 具體步驟由 APKBUILD 檔案中的 build() 函式定義。例如,可能使用 ./configure 指令碼、make 命令等。

4. 安裝步驟

package() 函式

  • 安裝構建好的檔案到 pkgdir
  • 設定檔案的許可權和屬性。
  • 安裝文件檔案(如 READMELICENSE 等)。

5. 建立 APK 包

  • abuild 會將 pkgdir 中的內容打包成一個 APK 檔案。
  • 生成的 APK 檔案會放在 packages 目錄下。

6. 簽名 APK 包

  • 如果你有簽名金鑰,abuild 會對生成的 APK 包進行簽名。簽名金鑰通常儲存在 ~/.abuild/ 目錄中。

7. 執行測試(可選)

  • abuild 也可以執行測試(如果定義了 check() 函式)。這通常在構建後執行,用於驗證包的正確性。

8. 清理

  • 清理臨時檔案和目錄,確保構建環境乾淨。

相關文章