最近需要将某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官方地址查询。
读者也可以参考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
-
问题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. 参考文件
- 运行build.sh,首先第一步是通过Dockerfile文件中配置构建alpine编译目标容器,并将相关的APKBUILD和dist压缩文件拷贝,进行编译;
- 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
}