Singularity(Apptainer)容器技术
Singularity 更名为 Apptainer
Apptainer官方地址
一. 安装
- ubuntu22.04环境下编译安装
为了方便演示,我们使用singularity容器环境实现下面的操作,实际上你需要将下列shell在宿主机上执行,毕竟目的就是为了安装singularity
下面操作会在后续讲解,暂时会用即可,不需要懂,我是在包含singularity的容器中进行的, 你可以直接跳转到shell脚本处,在宿主机上安装即可
singularity pull ubuntu:22.04
singularity build –sandbox install-sing ubuntu_22.04.sif
singularity shell -w install-sing/
安装脚本:
1 | install.sh |
安装:
1 | chmod +x install.sh |
- centos7环境下编译安装
安装脚本:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46!/bin/bash
install system dependence
yum update && yum install -y glib2-devel \
libseccomp-devel \
squashfs-tools \
make \
mutter \
wget \
pkg-config \
git \
cryptsetup
yum install p7zip p7zip-plugins -y
install go env
if [ ! -d "/usr/local/go" ]; then
export VERSION=1.17.13 OS=linux ARCH=amd64 && \
wget https://go.dev/dl/go$VERSION.$OS-$ARCH.tar.gz && \
tar -C /usr/local -xzvf go$VERSION.$OS-$ARCH.tar.gz && \
rm go$VERSION.$OS-$ARCH.tar.gz
echo 'export PATH=/usr/local/go/bin:$PATH' >> /etc/profile
source /etc/profile
fi
export PATH=/usr/local/go/bin:$PATH
go version
install singularity
export VERSION=3.10.2 && \
wget https://github.com/sylabs/singularity/releases/download/v${VERSION}/singularity-ce-${VERSION}.tar.gz && \
tar -xzf singularity-ce-${VERSION}.tar.gz && \
cd singularity-ce-${VERSION}
./mconfig
修复Makefile中go proxy代理
cd builddir
sed -i 's|proxy.golang.org|goproxy.cn,direct|g' Makefile
cd ..
make -C builddir
make -C builddir install
sed -i 's/shared loop devices = no/shared loop devices = yes/g' /usr/local/etc/singularity/singularity.conf
singularity version - centos7.9离线安装
在有的内网环境下是不能直接连接外网使用编译安装,那么通常使用的手段有两种,第一种使用docker将singularity安装在容器中,然后使用docker save成
tar包在私有内网环境下使用,在docker中的安装方法同上,还有一种方案就是使用离线安装,其实就是将singularity二进制程序及其依赖的包安装,目前笔者
在centos7.9环境下使用纯净环境下安装,然后将依赖的包以及配置进行修改即可,大致安装的内容如下所示:注意: singularity使用的二进制程序其实不止一个,还有1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56!/bin/bash
root_dir=$(
cd $(dirname ${BASH_SOURCE[0]})
pwd
)
install libseccomp
if [ "$(rpm -qa libseccomp | wc -l)" = "1" ]; then
echo "package libseccomp is exists"
else
echo "package libseccomp is not exists"
echo "now install libseccomp..."
rpm -ihv ${root_dir}/libseccomp-2.3.1-3.el7.x86_64.rpm
if [ ! $? -eq 0 ]; then
echo "libseccomp install failed."
exit 1
fi
echo "libseccomp install successful."
fi
install squashfs-tools
if [ "$(rpm -qa squashfs-tools | wc -l)" = "1" ]; then
echo "package squashfs-tools is exists"
else
echo "package squashfs-tools is not exists"
echo "now install squashfs-tools..."
rpm -ihv ${root_dir}/squashfs-tools-4.3-0.21.gitaae0aff4.el7.x86_64.rpm
if [ ! $? -eq 0 ]; then
echo "squashfs-tools install failed."
exit 1
fi
echo "squashfs-tools install successful."
fi
install singularity
singularity config file
mkdir -pv /usr/local/etc/singularity/
cp -r ${root_dir}/etc/singularity/* /usr/local/etc/singularity/
singularity bin
cp -r ${root_dir}/bin/* /usr/local/bin/
singularity cni bin
mkdir -pv /usr/local/libexec/singularity/
cp -r ${root_dir}/libexec/singularity/* /usr/local/libexec/singularity/
chmod u+s /usr/local/libexec/singularity/bin/starter-suid
singularity mnt session
mkdir -pv /usr/local/var/singularity/mnt/session
singularity bash_completion.d
mkdir -pv /usr/local/etc/bash_completion.d/
cp -r ${root_dir}/bash/singularity /usr/local/etc/bash_completion.d/
singularity version
singularity --versioncni
网络插件程序以及starter
等程序,所以离线安装的时候,需要将相关的内容全部拷贝到离线环境,
安装目录就保持默认安装路径即可。
二. 基本使用
搜索镜像
singularity search 镜像名称
镜像拉取
1
2
3
4
5
6
7
8
9
10
11
12从官方仓库拉取镜像
singularity pull library://alpine
从docker仓库拉取镜像
singularity pull docker://ubuntu:22.04
从docker私有仓库拉取镜像 设置私有仓库登录的用户名和密码的环境变量
export SINGULARITY_DOCKER_USERNAME='username'
export SINGULARITY_DOCKER_PASSWORD='password'
singularity pull docker://hub.your.com/project/app:tag
拉取后的镜像会自动进行容器格式转换,保存为sif结尾的文件拉取并构建一个新镜像(重命名)
1
2没什么太大的价值
singularity build image.sif docker://ubuntu:22.04基于基础底包实现容器镜像构造(常用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35将容器构建为一个沙盒(以ubuntu:22.04做底包)
singularity build --sandbox cache_dir docker://ubuntu:22.04
已可编辑状态进入沙盒
shell 交互式方式进入容器 且可编辑
singularity shell --writable cache_dir/
构建自己的应用程序(以python运行hello world 为例子)
注意,当前的linux命令都是已经进入容器环境内部,目录前带 Singularity>
apt update -y
apt install python3 -y
cat << EOF > /opt/main.py
import datetime
print("hello singularity, now is: %s" % datetime.datetime.now())
EOF
退出沙盒环境
exit
打包镜像
注意: 当前阶段已经回到宿主机环境
singularity build python.deom.sif cache_dir/
运行容器
singularity run python.deom.sif
发现并没有生效果 runscript
singularity inspect --runscript python.deom.sif
替代方案 挂载run script EOF转义,要么转$符号,前面加\ 或者将 EOF 变成字面量 仿真shell语句被解析
cat << 'EOF' > runscript
!/bin/bash
exec "${@}"
EOF
chmod +x runscript
singularity run --bind ./runscript:/.singularity.d/runscript python.deom.sif python3 /opt/main.py
也可以实现脚本的替换 但是相对比较繁琐注意: 直接在沙盒中修改容器的默认runscript执行的脚本 是无法在build成不可编辑的sif文件时生效,同理 环境变量也不会生效,解决方案是基于sif文件 使用def完成定义与构建
三. 基于Def文件构造镜像(最佳实践)
定义构建镜像的脚本,实现软件的安装、应用安装、环境变量、启动脚本设置、从主机系统添加文件、容器元数据等,真正实现一键全自动,本案例以上述案例为例子,将重新实现上述镜像构建过程
需要了解以下两个部分
Header(头信息)
主要声明当前应用构建需要的操作系统Sections(脚本块)
部分都由%字符定义,后跟特定部分的名称。所有节都是可选的,并且一个 def 文件可以包含给定节的多个实例
将上述改成使用def文件构建的定义文件如下:
1 | cat << 'EOF' > python.demo.def |
然后进行构建:
1 | sudo singularity build python.demo.sif python.demo.def |
构建成功后,就可以执行该镜像:
1 | singularity run python.demo.sif python3 /opt/main.py |
- header详解
Header引导通常使用的是docker、localimage、docker-daemon等
- docker(从docker hub或者私有仓库拉取)
1
2
3
4
5
6
7
8
9
10默认是从docker hub拉
Bootstrap: docker
From: ubuntu:22.04
私有仓库拉取 需要在执行前设置仓库的登录用户和密码
export SINGULARITY_DOCKER_USERNAME='username'
export SINGULARITY_DOCKER_PASSWORD='password'
需要提前将环境变量设置好
Bootstrap: docker
From hub.your.com/project/app:tag - localimage(基于已有的sif构建)
1
2Bootstrap: localimage
From: ubuntu_22.04.sif # 镜像路径 - docker-daemon(从docker后台拉取镜像做底包)
1
2Bootstrap: docker-daemon
From: hub.your.com/emulate/debug-tools:v1 - docker-archive(基于docker的tar包构建)
1
2
3
4先使用docker save 指令实现容器的打包
docker save -o test.tar.gz debug-tools:v1
Bootstrap: docker-archive
From: dockertar.tar.gz # docker容器压缩包路径
- sections详解
介绍每一个章节的使用功能
%setup
(在构建容器之前设置)通常在这个阶段用来初始化一些文件或者容器内部的文件等
1
2
3
4
5
6
7
8
9
10
11
12setup
# 在宿主机上创建一个文件
touch a.txt
cat << 'EOF' > a.txt
hello world!
EOF
# 在容器内部创建文件
# 会使用到`SINGULARITY_ROOTFS`环境变量,指向了容器内部的根
touch $SINGULARITY_ROOTFS/root.txt
cat << 'EOF' > $SINGULARITY_ROOTFS/root.txt
hello root os!
EOF构建底包后执行结果如下:
files
(在构建期间实现宿主机文件复制到沙盒)通常在构建镜像时,都会提前将需要的依赖的包或者相关文件准备好,在构建阶段,直接复制到容器中,文件的复制不止是从宿主机复制,也可以从多阶段构建的
的其他阶段复制文件1
2
3
4
5
6
7
8
9
10从宿主机拷贝文件到容器内部 为了做实验 我们从空开始
Bootstrap: localimage
From: ubuntu_22.04.sif
setup
touch a.txt
touch b.txt
files
./a.txt # 将当前构建目录下的a.txt复制到容器根下 /a.txt
./b.txt /opt/ # 将当前构建目录下的b.txt复制到容器根下 /opt/b.txt构建:
sudo singularity build file.sif file.def
验证:singularity exec file.sif ls /a.txt
注意: 最佳实践,通常我们都会构建最小镜像,如果使用完安装包后,记得在%post
中清理复制的包使用多阶段构建过程中,比如在某一个阶段编译了一个二进制文件,在另外一个干净的环境中进行运行,给镜像瘦身,下面以编译go程序为例,和在Dockerfile中
进行多阶段构建类似:
go源代码main.go
:1
2
3
4
5
6
7
8
9package main
import (
"fmt"
"time"
)
func main() {
fmt.Printf("current time: %s\n", time.Now().Format(time.DateTime))
}现在我们准备一个go的编译环境,然后将编译后的文件放到最干净的环境中执行,也就是实现文件的多阶段中复制功能,def文件定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31mutil-file.def
Bootstrap: docker
From: golang:1.23-alpine
Stage: build
setup
cat << 'EOF' > main.go
package main
import (
"fmt"
"time"
)
func main() {
fmt.Printf("current time: %s\n", time.Now().Format(time.DateTime))
}
EOF
files
./main.go /opt/
post
export PATH="/go/bin:/usr/local/go/bin:$PATH"
go build -o /opt/ct /opt/main.go
Bootstrap: library
From: alpine:3.9
Stage: prod
files from build
/opt/ct /bin/ct
runscript
/bin/ct ${@}解决镜像拉取问题请参考docker-singularity镜像代理文章,本地盖不赘述
构建:sudo singularity build mutil-file.sif mutil-file.def
执行:singularity run mutil-file.sif
post
(构建应用程序步骤)通常我们将构建镜像所有的shell命令都会写在这个阶段,用来构建应用程序,安装依赖包,下载相关依赖,编译程序,清理中间文件等,如下面的常见操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15post
导入需要的环境变量
export LC_ALL="C.UTF-8"
换源
sed -i 's/archive.ubuntu.com/mirrors.huaweicloud.com/g' /etc/apt/sources.list
sed -i 's/security.ubuntu.com/mirrors.huaweicloud.com/g' /etc/apt/sources.list
sed -i 's/ports.ubuntu.com/mirrors.huaweicloud.com/g' /etc/apt/sources.list
apt update
安装软件
apt install -y cmake make bison flex
apt install -y gcc gfortran g++ python3 python3-dev
apt install -y zlib1g zlib1g-dev
apt
编译程序
清理中间包environment
(设置镜像启动时的环境变量)注意,这个环境变量是容器运行时使用,在构建阶段即post阶段时不可以使用的,post阶段如果将环境变量追加到
$SINGULARITY_ENVIRONMENT
,post阶段的
环境变量会被写到/.singularity.d/env/91-environment.sh
,在运行时优先级高于 environment 阶段设置的环境变量
environment 的内容最后是写入到了/.singularity.d/env/90-environment.sh
文件中,在容器启动或者运行之前,env下的环境变量都会被执行从而载入到环境变量中1
2
3
4
5通常我们会把依赖库的环境变量 可执行文件的路径 语言等环境变量进行设置
environment
export LC_ALL="C.UTF-8"
export LANG="C.UTF-8"
export PATH=/opt/package/bin/:$PATH查询:
singularity exec python.demo.sif env | grep -E 'LC_ALL|LC_ALL'
startscript
(镜像启动时执行的脚本,会自动被写入到/.singularity.d/startscript中)startscript 在构建阶段会将所有内容写入
/.singularity.d/startscript
,当使用 start 时 会自动执行该脚本1
2startscript
echo "running startscript ..."使用:
singularity start python.demo.sif
runscript
(镜像运行时执行的脚本,会自动被写入到/.singularity.d/runscript中)runscript 在构建阶段将所有内容写入
/.singularity.d/runscript
,当使用 run 时 会自动执行脚本,通常我们会将该脚本全部放开,增强镜像的灵活
性1
2runscript
exec "${@}"使用:
singularity run python.demo.sif
labels
(给镜像打标签,键值对形式)为镜像打标签,给镜像增加元数据, 使用空格分割,前面是key 后面是value
1
2
3
4labels
Author 武安君
Version v0.1.0
Time 2024-11-1查看镜像的标签:
singularity inspect python.demo.sif
help
(给使用镜像增加帮助信息)通常在help中写容器的功能以及如何使用容器
1
2
3help
only show singularity how to use
created by 武安君查询帮助:
singularity run-help python.demo.sif
test
(镜像测试)验证是否打包镜像成功,可以验证功能 或者验证文件等
1
2
3
4
5
6test
if [ `ls -hl /bin/ct | wc -l` -eq 2 ]; then \
echo "install successfully"; \
else \
echo "install failed" ; \
fi说明: %test部分在构建过程的最后运行
验证:singularity test python.demo.sif
- 运行构建参数详解
--fakeroot
(无根模式) 可以模拟root用户权限实现容器镜像的构建1
2
3
4
5
6开始时需要sudo权限执行如下构建
sudo singularity build python.demo.sif python.demo.def
使用无根模式
singularity build --fakeroot python.demo.sif python.demo.def
singularity build --sandbox --fakeroot temp python.demo.sif
singularity shell --fakeroot python.demo.sif说明: 无根模式和root在容器内与命名空间权限完全相同,可以在多用户环境下,无sudo权限也可以实现容器镜像的构建,比较推荐使用
--sandbox
(构建一个沙盒)将sif容器镜像构建在基于当前名称的rootfs系统,然后可以进行沙盒中实现命令行构建
1
singularity build --sandbox --fakeroot tmp python.deom.sif
--force
(强制覆盖构建)1
singularity build --fakeroot --force python.deom.sif tmp/
- 运行启动参数详解
以下列为例,阐述常用的配置参数:说明:1
2
3
4
5
6
7
8
singularity run --cleanenv \
--env SXK=111 \
--env-file a.conf \
--pwd=/home/erebus \
--bind /usr/local --bind /usr/local/bin/:/usr/local/bin/ \
--fakeroot \
ubuntu_22.04.sif bash __command.sh
--cleanenv
: 清除容器运行之前的环境变量--env
: 设置环境变量,通常在HPC环境下会使用较多--env-file
: 通过文件的方式实现环境变量的配置,形式为key=value--pwd
: 指定容器运行时的工作目录--bind
: 映射宿主机和容器中的路径,与docker类似--fakeroot
: 普通用户模拟root用户执行--nv
: 启用Nvidia GPU支持,针对容器打包可视化容器时会使用
四. 镜像加密
通过加密文件系统来构建安全、保密的容器环境,避免只是产权外泄。
- 镜像加密
- 密码加密
1
2
3export SINGULARITY_ENCRYPTION_PASSPHRASE=`openssl rand -base64 16`
echo ${SINGULARITY_ENCRYPTION_PASSPHRASE}
sudo SINGULARITY_ENCRYPTION_PASSPHRASE=${SINGULARITY_ENCRYPTION_PASSPHRASE} singularity build --encrypt --force e-python.sif python.demo.def - 非对称密钥对RSA加密
1
2
3
4
5
6
7
8
9
10
11
12
13生成密钥对
ssh-keygen -t rsa -b 2048 -N "" -f "./singularity"
将公钥转换为PEM格式
ssh-keygen -f ./singularity.pub -e -m pem >singularity_pub.pem
mv singularity singularity_pri.pem
统一转换密钥的格式
ssh-keygen -p -f singularity_pri.pem -m PEM -N ""
使用非对称加密
使用文件路径
sudo singularity build --pem-path=singularity_pub.pem --encrypt --force e1-python.sif python.demo.def
使用环境变量
sudo SINGULARITY_ENCRYPTION_PEM_PATH=singularity_pub.pem singularity --encrypt --force e1-python.sif python.demo.def
- 使用加密镜像
- 密码加密
1
2
3
4SINGULARITY_ENCRYPTION_PASSPHRASE=${SINGULARITY_ENCRYPTION_PASSPHRASE} singularity run e-python.sif
如果使用交互命令方式(基本不使用)
singularity run --passphrase e-python.sif - 公钥加密使用方法
1
2
3
4
5--fakeroot 选项来运行加密的 SIF,因为 --fakeroot 选项创建了一个用户级的命名空间,它与需要 setuid 支持的加密容器功能不兼容
singularity run --pem-path=singularity_pri.pem e1-python.sif
使用环境变量方式执行
SINGULARITY_ENCRYPTION_PEM_PATH=singularity_pri.pem singularity run e1-python.sif
五. 多阶段构建与使用
多阶段构建是使用一个def文件,在编译环境下构建二进制产物,然后在部署环境下运行,可以极大的缩小容器的体积,在基于上面的section的基础上,将多个定义
写到一个定义文件即可,可参考下面的go程序编译:
1 | mutil-file.def |
注意: 多阶段构建中,不能从上一个阶段获取下一阶段的数据文件
六. 多应用构建与使用
多应用构建就是将多个具有相同运行环境的应用构建到一个环境中进行敷用,也可以给每个应用配置各自特有的运行环境
其实就是给每个section加上app前缀,同时对操作的名称进行改变,如下:
1 | touch foo.txt |