Alpine编译.apk安装包

  最近需要将某docker项目从centos系统迁移到alpine中,因此需要重新编译项目;参考国外某些项目frr已写好APKBUILD,Dockerfile和build.sh等文件,这里对其主要过程分析并记录一下,便于以后打包过程的回顾。本文最后一节,罗列了一些参考模板;
  本文最新状态可查看源站链接:Alpine编译.apk安装包

1. 构建发布压缩包

  该步骤主要同项目构建过程相关,需要根据不同情况进行编译;

# 1.make distribution tar.gz
./bootstrap.sh
./configure --with-pkg-extra-version=-r1
make dist
#会发现目录中生成XXX-MyVersion.tar.gz的源码压缩包

  这一步主要是构建代码编译采用的压缩包,确认版本号等过程;读者可以在宿主机上编译出压缩包或编写到Dockerfile和脚本,FROM alpine:edge as source-builder,并在其中安装所需依赖.

2. 编写APKBUILD等文件

  构建.apk包的时候,需要编写自己的APKBUILD文件和其他文件,如下图所示,其中frr为本文需要构建安装包的项目;.pre-install和pre-deinstall等文件,是安装和卸载apk包时所执行脚本;APKBUILD.in为生成APKBUILD文件的模板文件;编写APKBUILD文件时,检查项目中dep依赖库包可以通过alpinelinux官方地址查询。

alpine_abuild_deps

  读者也可以参考abuild官方gitlab地址某标准案例:https://gitlab.alpinelinux.org/alpine/abuild/-/blob/master/sample.APKBUILD
  根据项目实际情况,编写shell脚本以及其中必须的函数prepare(),build(),package(),check();具体可以索引到参考文章alpinelinux的abuild中APKBUILD设置

3. 创建alpine编译容器

#拉取镜像
docker pull alpine:edge
docker run -itd --name alpine-builder alpine:edge
#登录容器
docker exec -it alpine-builder sh
#配置源,如果源慢,可根据依赖库更换国内源;
echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories
#安装构建依赖包,使用本地缓存,升级资源库缓存
apk add --update-cache abuild alpine-conf alpine-sdk py-pip
pip install pytest

  因网速问题,这里容器拉取到上述编译工具包后,已做成容器镜像并上传DockerHub,便于用户拉取使用,读者可自行构造镜像或拉取该容器。

#turbock/alpine-builder:V2镜像已经添加各种编译依赖
docker pull turbock/alpine-builder:V2
#拉取到本地创建容器
docker run -itd --name=alpine-builder turbock/alpine-builder:V2

  在容器中创建编译apk包所需目录和相关配置

docker exec -it alpine-builder sh
setup-apkcache /var/cache/apk
#创建编译安装包目录
mkdir -p /dist/
#创建安装包输出目录
mkdir -p /pkgs/apk
echo 'builder ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

4. 准备alpine构建环境并编译打包

  拷贝源码tarball和apk编译文件到alpine容器相同目录下,此时该目录中应包含上述的XXX.pre-install,XXX.pre-deinstall,APKBUILD以及XXX.tar.gz文件;

#拷贝发布压缩包.tar.gz
docker cp ./*-*.tar.gz  alpine-builder:/dist/
#拷贝alpine目录下APKBUILD等文件到alpine docker中同一路径下,例如/dist/
docker cp ./alpine/. alpine-builder:/dist/
docker exec -it alpine-builder sh

#创建builer用户,并以该用户构建apk包;否则会报错>>> ERROR: : Do not run abuild as root
adduser -D -G abuild builder && chown -R builder /dist /pkgs
su builder
cd /dist
#更新依赖,如需要检验校验码,进行如下操作(可选操作)
abuild deps && abuild-keygen -a -n && abuild checksum && git init
#abuild默认不许用root用户构建,可添加-F参数强制;
#abuild -F deps && abuild-keygen -a -n && abuild -F checksum && git init
#构建APK
abuild -r -P /pkgs/apk
#abuild -F -r -P /pkgs/apk

最后可以在/pkgs/apk目录中查到apk包

  • 问题1:报错 Checking sanity of /dist/APKBUILD...ERROR: 7.3.1-2.6.1 is not a valid version
    解决方法:abuild对版本号格式有约束,通过下图可知对应版本号格式,例如1.0.0-r0,参考文章:APKBUILD_Reference
    abuild-version

  • 问题2:报错cc1plus: all warnings being treated as errors
    解决方法:设置环境变量;c工程设置export CFLAGS="-Wno-error";c++工程设置export CXXFLAGS="-Wno-error";

  • 问题3:报错Tracing dependencies... ERROR: /usr/lib/libprotobuf-c.so.1.0.0: Could not find owner package
    解决方法:依赖项可能为用户自定义依赖库,或第三方库。编译安装包最后步骤,会默认会检查依赖库关系;这个需要注意几个地方,需要在编译Makefile文件中添加-lprotobuf-c,安装该apk模块apk add protobuf-c,并写入APKBUILD文件的依赖项检查中。

5. Abuild范式了解(类似rpmbuild)

Abuild官方wiki文档,这里将abuild范式罗列如下

$ abuild -h
usage: abuild [options] [-P REPODEST] [-s SRCDEST] [-D DESCRIPTION] [cmd] ...
       abuild [-c] -n PKGNAME[-PKGVER]
Options:
 -A  Print CARCH and exit
 -c  Enable colored output
 -d  Disable dependency checking
 -D  Set APKINDEX description (default: $repo $(git describe))
 -f  Force specified cmd (skip checks: apk up to date, arch, libc)
 -F  Force run as root
 -h  Show this help
 -k  Keep built packages, even if APKBUILD or sources are newer
 -K  Keep buildtime temp dirs and files (srcdir/pkgdir/deps)
 -m  Disable colors (monochrome)
 -P  Set REPODEST as the repository location for created packages
 -q  Quiet
 -r  Install missing dependencies from system repository (using sudo)
 -s  Set source package destination directory
 -v  Verbose: show every command as it is run (very noisy)

Commands:
  build       Compile and install package into $pkgdir
  check       Run any defined tests concerning the package
  checksum    Generate checksum to be included in APKBUILD
  clean       Remove temp build and install dirs
  cleancache  Remove downloaded files from $SRCDEST
  cleanoldpkg Remove binary packages except current version
  cleanpkg    Remove already built binary and source package
  deps        Install packages listed in makedepends and depends
  fetch       Fetch sources to $SRCDEST (consider: 'abuild fetch verify')
  index       Regenerate indexes in $REPODEST
  listpkg     List target packages
  package     Install project into
  prepare     Apply patches
  rootbld     Build package in clean chroot
  rootpkg     Run 'package', the split functions and create apks as fakeroot
  sanitycheck Basic sanity check of APKBUILD
  snapshot    Create a $giturl or $svnurl snapshot and upload to $disturl
  sourcecheck Check if remote source package exists upstream
  srcpkg      Make a source package
  undeps      Uninstall packages listed in makedepends and depends
  unpack      Unpack sources to $srcdir
  up2date     Compare target and sources dates
  verify      Verify checksums

To activate cross compilation specify in environment:
  CHOST       Arch or hostspec of machine to generate packages for
  CTARGET     Arch or hostspec of machine to generate compiler for

6. 参考文件

  1. 运行build.sh,首先第一步是通过Dockerfile文件中配置构建alpine编译目标容器,并将相关的APKBUILD和dist压缩文件拷贝,进行编译;
  2. build.sh第二步是将编译好的apk安装包安装并制作镜像;
  • frr/docker/alpine/build.sh文件
set -e
set -x

##
# Package version needs to be decimal
##
GITREV="$(git rev-parse --short=10 HEAD)"
PKGVER="$(printf '%u\n' 0x$GITREV)"

docker build \
    --pull \
    --file=docker/alpine/Dockerfile \
    --build-arg="PKGVER=$PKGVER" \
    --tag="frr:alpine-builder-$GITREV" \
    --target=alpine-builder \
    .

CONTAINER_ID="$(docker create "frr:alpine-builder-$GITREV")"
docker cp "${CONTAINER_ID}:/pkgs/" docker/alpine
docker rm "${CONTAINER_ID}"

docker build \
    --file=docker/alpine/Dockerfile \
    --build-arg="PKGVER=$PKGVER" \
    --tag="frr:alpine-$GITREV" \
    .

docker rmi "frr:alpine-builder-$GITREV"
  • frr/docker/alpine/Dockerfile
FROM alpine:edge as source-builder

RUN mkdir -p /src/alpine
COPY alpine/APKBUILD.in /src/alpine
RUN source /src/alpine/APKBUILD.in \
    && echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories \
    && apk add \
        --no-cache \
        --update-cache \
        $makedepends \
        gzip \
    && pip install pytest

COPY . /src
ARG PKGVER
RUN cd /src \
    && ./bootstrap.sh \
    && ./configure \
        --enable-numeric-version \
        --with-pkg-extra-version="_git$PKGVER" \
    && make dist

# This stage builds an apk from the dist tarball
FROM alpine:edge as alpine-builder
# Don't use nocache here so that abuild can use the cache
RUN echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories \
    && apk add \
        --update-cache \
        abuild \
        alpine-conf \
        alpine-sdk \
        py-pip \
    && pip install pytest \
    && setup-apkcache /var/cache/apk \
    && mkdir -p /pkgs/apk \
    && echo 'builder ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

COPY --from=source-builder /src/frr-*.tar.gz /src/alpine/* /dist/
RUN adduser -D -G abuild builder && chown -R builder /dist /pkgs
USER builder
RUN cd /dist \
    && abuild-keygen -a -n \
    && abuild checksum \
    && git init \
    && abuild -r -P /pkgs/apk

# This stage installs frr from the apk
FROM alpine:edge
RUN mkdir -p /pkgs/apk
COPY --from=alpine-builder /pkgs/apk/ /pkgs/apk/
RUN echo 'http://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories \
    && apk add \
        --no-cache \
        --update-cache \
        tini \
    && apk add \
        --no-cache \
        --allow-untrusted /pkgs/apk/*/*.apk \
    && rm -rf /pkgs
COPY docker/alpine/docker-start /usr/lib/frr/docker-start
ENTRYPOINT [ "/sbin/tini", "--", "/usr/lib/frr/docker-start" ]
  • frr/alpine/APKBUILD文件
pkgname=frr
arch="all"
pkgver=@VERSION@
pkgrel=0
pkgdesc="FRRouting is a fork of quagga"
url="https://frrouting.org/"
license="GPL-2.0"
depends="json-c c-ares iproute2 python3 bash"
makedepends="ncurses-dev net-snmp-dev gawk texinfo perl
    acct autoconf automake bash binutils bison bsd-compat-headers build-base
    c-ares c-ares-dev ca-certificates cryptsetup-libs curl device-mapper-libs
    expat fakeroot flex fortify-headers gdbm git gmp isl json-c-dev kmod
    lddtree libacl libatomic libattr libblkid libburn libbz2 libc-dev
    libcap-dev libcurl libedit libffi libgcc libgomp libisoburn libisofs
    libltdl libressl libssh2 libstdc++ libtool libuuid libyang-dev
    linux-headers lzip lzo m4 make mkinitfs mpc1 mpfr4 mtools musl-dev
    ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre
    perl pkgconf python3 python3-dev readline readline-dev sqlite-libs
    squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev
    py3-sphinx"
checkdepends="pytest py-setuptools"
install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall"
subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
source="$pkgname-$pkgver.tar.gz"

builddir="$srcdir"/$pkgname-$pkgver

_sbindir=/usr/lib/frr
_sysconfdir=/etc/frr
_libdir=/usr/lib
_localstatedir=/var/run/frr
_user=frr

build() {
    cd "$builddir"

    ./configure \
        --prefix=/usr \
        --sbindir=$_sbindir \
        --sysconfdir=$_sysconfdir \
        --libdir=$_libdir \
        --localstatedir=$_localstatedir \
        --enable-systemd=no \
        --enable-rpki \
        --enable-vtysh \
        --enable-multipath=64 \
        --enable-vty-group=frrvty \
        --enable-user=$_user \
        --enable-group=$_user
    make
}

check() {
    cd "$builddir"
    make -j 1 check
}

package() {
    cd "$builddir"
    make DESTDIR="$pkgdir" install

    install -Dm644 "$builddir"/tools/etc/frr/daemons "$pkgdir"$_sysconfdir
    install -d "$pkgdir"/etc/init.d
    ln -s ${_sbindir}/frr "$pkgdir"/etc/init.d/frr
}

发表评论

邮箱地址不会被公开。 必填项已用*标注