Alpine 基础

Alpine 是一个与 OpenWrt 类似的以 Musl + Busybox 为基础的精简 Linux 发行版,其包构建与管理结合了 Debian/Ubuntu apt、RHEL/CentOS yum 与 FreeBSD ports 等多个系统的特点。

由于我不做嵌入式开发,因此 Alpine 主要用于容器应用,以及构建全静态 C/C++ 程序。

apk 是 Alpine 的包管理工具,集成了类似 dpkg/apt, rpm/yum 的功能,对比下 apk -hapt -h 的输出,开发者还是挺有意思的:

$ apk -h
...
This apk has coffee making abilities.
$ apt -h
...
This APT has Super Cow Powers.

修改软件源

# vi /etc/apk/repositories
http://mirrors.ustc.edu.cn/alpine/v3.10/main
http://mirrors.ustc.edu.cn/alpine/v3.10/community

显然,apk 源的类型就是一个文件夹,类似于 Ubuntu 源的 main restricted universe multiverse 等各种类型。

可以为源命名,如:

# vi /etc/apk/repositories
http://mirrors.ustc.edu.cn/alpine/v3.10/main
http://mirrors.ustc.edu.cn/alpine/v3.10/community
@ceph /ceph/apk

这样在安装软件包时可以安装指定源里面的包,如:

$ sudo apk add ceph-common@ceph

更新软件包索引

# apk update
fetch http://mirrors.ustc.edu.cn/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://mirrors.ustc.edu.cn/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
v3.10.2-36-g2fa274fe2c [http://mirrors.ustc.edu.cn/alpine/v3.10/main]
v3.10.2-37-g3ee31e5e22 [http://mirrors.ustc.edu.cn/alpine/v3.10/community]
OK: 10334 distinct packages available

列出所有可更新的版本

# apk list -u

更新已安装软件包

# apk upgrade

判断软件包版本号

检测版本号是否合法,有输出则表示非法:

$ apk version -c 12.2.12-r2.43-g204a43f004
12.2.12-r2.43-g204a43f004
$ echo $?
1
$ apk version -c 12.2.12-r2
$ echo $?
0

比较版本号:

$ apk version -t 12.2.12-r1 12.2.12-r2
<

Alpine 的软件包版本号规则与 Gentoo 下的软件包版本号规则保持一致(相比 deb、rpm 非常之不灵活,在构建 apk 包时需要特别注意):

{digit}{.digit}...{letter}{_suf{#}}...{-r#}

具体算法可以参考如下代码:

https://github.com/alpinelinux/apk-tools/blob/v2.10.4/src/version.c

安装软件包

安装最新的软件包:

# apk add ceph-common

安装指定版本的软件包:

# apk add 'ceph-common=14.2.1-r0'
# apk add 'ceph-common<14.2.1-r0'

注意 Alpine 的源与 yum 源类似,只保留软件包最新的版本,如需要安装老版本则需要增加老版本的源。

列出所有已安装包

# apk list -I

列出软件包内容

列出已安装软件包内容:

# apk info -L ceph-common

列出本地软件包内容:

$ apk manifest ./ceph-common-12.2.12-r2.43-g204a43f004.apk
(null):  .pre-install
sha1:7876192f4a86d7e8e6ba00f8ba7665d16000e027  etc/ceph/rbdmap
sha1:728ed29877636f9a1f7e523dc00f8dc3de086d32  etc/udev/rules.d/50-rbd.rules
sha1:d6a80d93b67b857373ea6630de4059531faef18f  sbin/mount.ceph
sha1:e674ede90c25e57523e280c95fab5f23e2d20bf1  usr/bin/ceph
sha1:976e8bfeed14c759d046ee7c8cd372bbad3d7638  usr/bin/ceph-authtool
sha1:f2664e9d538c47e5b60f3269ecfac977a056013b  usr/bin/ceph-conf
sha1:9471e4cd2faed6d0b44781e06571752437fd1a3f  usr/bin/ceph-dencoder
...

通过文件查询所属软件包

# apk info -W /usr/lib/librados.so.2

rpm -qf 类似。

删除已安装软件包

# apk del ceph-common

查询软件包信息

# apk info ceph-common

比较遗憾的是没有查询本地 apk 包信息的命令。

搜索软件包

# apk search ceph-common

下载软件包

# apk fetch ceph-common

安装本地软件包

# apk add ./ceph-common-14.2.1-r0.apk

验证本地软件包签名

# apk verify ./ceph-common-14.2.1-r0.apk

注意:用于签名的私钥对应的公钥必须拷贝至 /etc/apk/keys 目录,否则会有如下的错误:

$ apk verify alpine/ceph/x86_64/librados-12.2.12-r2.43-g5cb60f2c3c.apk
alpine/ceph/x86_64/librados-12.2.12-r2.43-g5cb60f2c3c.apk: 0 - UNTRUSTED

生成 apk 签名密钥对

$ sudo apk add alpine-sdk
$ abuild-keygen -a -i
>>> Generating public/private rsa key pair for abuild
Enter file in which to save the key [/home/runsisi/.abuild/luo.runbing@example.com-5d735ebc.rsa]: /home/runsisi/.abuild/runsisi@hust.edu.cn.rsa
Generating RSA private key, 2048 bit long modulus (2 primes)
................................+++++
....+++++
e is 65537 (0x010001)
writing RSA key
>>> Installing /home/runsisi/.abuild/runsisi@hust.edu.cn.rsa.pub to /etc/apk/keys...
>>>
>>> Please remember to make a safe backup of your private key:
>>> /home/runsisi/.abuild/runsisi@hust.edu.cn.rsa
>>>
$ ls /etc/apk/keys/
alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub  alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub
alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub  runsisi@hust.edu.cn.rsa.pub
$ ls ~/.abuild/
abuild.conf                  runsisi@hust.edu.cn.rsa      runsisi@hust.edu.cn.rsa.pub
$ cat ~/.abuild/abuild.conf
PACKAGER_PRIVKEY="/home/runsisi/.abuild/runsisi@hust.edu.cn.rsa"

-i 选项把公钥拷贝至 /etc/apk/keys 目录。

为本地 apk 文件创建本地源

# apk index -o x86_64/APKINDEX.tar.gz x86_64/*.apk

使用本地源

# tree /home/runsisi/ceph/alpine/ceph
/home/runsisi/ceph/alpine/ceph
└── x86_64
    ├── APKINDEX.tar.gz
    ├── ceph-12.2.12-r2.43-g204a43f004.apk
    ├── ceph-base-12.2.12-r2.43-g204a43f004.apk
    ├── ceph-bash-completion-12.2.12-r2.43-g204a43f004.apk
    ├── ceph-common-12.2.12-r2.43-g204a43f004.apk
    ...
# vi /etc/apk/repositories
http://dl-cdn.alpinelinux.org/alpine/v3.10/main
http://dl-cdn.alpinelinux.org/alpine/v3.10/community
/home/runsisi/ceph/alpine/ceph

注意源的目录结构必须是 /path/to/repo/$arch/{APKINDEX.tar.gz, *.apk},且 repositories 文件中的 URL 仅指定到 $arch 的父目录这一级,这是和 yum baseurl 的最大区别。

同时需要特别注意的是,当前 apk 还不能处理 no_proxy 环境变量,请见 PR27

# apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
WARNING: Ignoring /home/runsisi/ceph/alpine/ceph/x86_64/APKINDEX.tar.gz: UNTRUSTED signature
v3.10.2-36-g2fa274fe2c [http://dl-cdn.alpinelinux.org/alpine/v3.10/main]
v3.10.2-38-g39a872f50f [http://dl-cdn.alpinelinux.org/alpine/v3.10/community]
OK: 10334 distinct packages available

显然,索引文件需要进行签名,如有需要,所有 apk 相关的命令都可以通过指定 --allow-untrusted 选项进行规避:

# apk update --allow-untrusted
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
v3.10.2-36-g2fa274fe2c [http://dl-cdn.alpinelinux.org/alpine/v3.10/main]
v3.10.2-38-g39a872f50f [http://dl-cdn.alpinelinux.org/alpine/v3.10/community]
OK: 10356 distinct packages available
# apk policy librados --allow-untrusted
librados policy:
  14.2.1-r0:
    http://dl-cdn.alpinelinux.org/alpine/v3.10/community
  12.2.12-r2.43-g204a43f004:
    /home/runsisi/ceph/alpine/ceph

为源索引文件签名

# abuild-sign -k ~/.abuild/runsisi@hust.edu.cn.rsa x86_64/APKINDEX.tar.gz

签名之后 apk 命令就不再需要指定 --allow-untrusted 选项进行规避了。

注意:

  1. 用于签名的私钥对应的公钥必须拷贝至 /etc/apk/keys 目录,否则 apk update 仍然会报错 UNTRUSTED signature(公钥文件名不要修改);
  2. -k 指定的签名私钥必须用绝对地址,abuild-sign 不支持相对地址;
  3. 用于给 apk 包签名的私钥与给索引文件签名的私钥可以不同,但需要保证第一条;
  4. 索引文件不支持多次重复签名,否则 apk update 时会报错 BAD signature。

解决找不到 dynamic loader 的问题

# ./go
/bin/sh: ./go: not found
# ldd go
        /lib64/ld-linux-x86-64.so.2 (0x7f7d53a64000)
        libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f7d53a64000)
        libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f7d53a64000)
# ls /lib64/ld-linux-x86-64.so.2
ls: /lib64/ld-linux-x86-64.so.2: No such file or directory
# apk add libc6-compat
# apk manifest libc6-compat
sha1:b261ba9eb77e829f4b27f114716b844939d292a1  lib/libm.so.6
sha1:b261ba9eb77e829f4b27f114716b844939d292a1  lib/libc.so.6
sha1:b261ba9eb77e829f4b27f114716b844939d292a1  lib/libcrypt.so.1
sha1:b261ba9eb77e829f4b27f114716b844939d292a1  lib/librt.so.1
sha1:b261ba9eb77e829f4b27f114716b844939d292a1  lib/libutil.so.1
sha1:b261ba9eb77e829f4b27f114716b844939d292a1  lib/libpthread.so.0
sha1:b261ba9eb77e829f4b27f114716b844939d292a1  lib64/ld-linux-x86-64.so.2
# ls -l /lib64/ld-linux-x86-64.so.2
lrwxrwxrwx    1 root     root            26 Sep 26 05:33 /lib64/ld-linux-x86-64.so.2 -> /lib/libc.musl-x86_64.so.1

参考资料

Alpine Linux package management

https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management

Package policies

https://wiki.alpinelinux.org/wiki/Package_policies

File Naming Rules

https://devmanual.gentoo.org/ebuild-writing/file-format/index.html#file-naming-rules

Creating a Package Repository for Alpine Linux

https://engineering.fundingcircle.com/blog/2015/04/28/create-alpine-linux-repository/

Static linking for C++ with Docker and Alpine Linux

https://www.reddit.com/r/cpp/comments/a3raez/static_linking_for_c_with_docker_and_alpine_linux/

Lightweight containers for building C++

https://github.com/foonathan/docker

Static binaries for a C++ application

https://www.arangodb.com/2018/04/static-binaries-c-plus-plus-application/


最后修改于 2019-09-07