Singularity 更名为 Apptainer
Apptainer官方地址

一. 安装

  1. 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
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
#  install.sh
export GOPROXY=https://goproxy.cn,direct \
TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive \
GO_VERSION=1.22.6 \
ARCH=amd64 \
SINGULAARITY_VERSION=4.1.5

sed -i "s@http://.*archive.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list \
&& sed -i "s@http://.*security.ubuntu.com@http://repo.huaweicloud.com@g" /etc/apt/sources.list \
&& apt-get update

apt-get install -y --no-install-recommends apt-utils \
&& apt-get install tzdata -y \
&& apt-get install vim -y \
&& apt-get install net-tools iputils-ping iproute2 wget psmisc openssh-client curl -y

ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

wget https://go.dev/dl/go${GO_VERSION}.linux-${ARCH}.tar.gz -O go.tar.gz && \
tar -C /usr/local -xzf go.tar.gz && \
rm go.tar.gz && \
export PATH=/usr/local/go/bin:$PATH; \
go version;

apt-get install -y libglib2.0-dev \
build-essential \
libssl-dev \
uuid-dev \
make \
libgpgme11-dev \
squashfs-tools \
libseccomp-dev \
wget \
pkg-config \
cryptsetup \
libfuse3-dev \
autoconf \
libtool \
fuse

export PATH=/usr/local/go/bin:$PATH && \
wget https://github.com/sylabs/singularity/releases/download/v${SINGULAARITY_VERSION}/singularity-ce-${SINGULAARITY_VERSION}.tar.gz && \
tar -xzf singularity-ce-${SINGULAARITY_VERSION}.tar.gz && \
cd singularity-ce-${SINGULAARITY_VERSION} && \
./mconfig && \
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

安装:

1
2
chmod +x install.sh  
sudo ./install.sh
  1. 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
  2. centos7.9离线安装

    在有的内网环境下是不能直接连接外网使用编译安装,那么通常使用的手段有两种,第一种使用docker将singularity安装在容器中,然后使用docker save成
    tar包在私有内网环境下使用,在docker中的安装方法同上,还有一种方案就是使用离线安装,其实就是将singularity二进制程序及其依赖的包安装,目前笔者
    在centos7.9环境下使用纯净环境下安装,然后将依赖的包以及配置进行修改即可,大致安装的内容如下所示:

    安装脚本:
    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 --version
    注意: singularity使用的二进制程序其实不止一个,还有cni网络插件程序以及starter等程序,所以离线安装的时候,需要将相关的内容全部拷贝到离线环境,
    安装目录就保持默认安装路径即可。

二. 基本使用

  1. 搜索镜像

    singularity search 镜像名称

  2. 镜像拉取

    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结尾的文件
  3. 拉取并构建一个新镜像(重命名)

    1
    2
    # 没什么太大的价值
    singularity build image.sif docker://ubuntu:22.04
  4. 基于基础底包实现容器镜像构造(常用)

    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文件构造镜像(最佳实践)

定义构建镜像的脚本,实现软件的安装、应用安装、环境变量、启动脚本设置、从主机系统添加文件、容器元数据等,真正实现一键全自动,本案例以上述案例为例子,将重新实现上述镜像构建过程
需要了解以下两个部分

  1. Header(头信息)
    主要声明当前应用构建需要的操作系统

  2. Sections(脚本块)
    部分都由%字符定义,后跟特定部分的名称。所有节都是可选的,并且一个 def 文件可以包含给定节的多个实例

将上述改成使用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
31
32
33
34
35
36
37
38
39
40
cat << 'EOF' > python.demo.def
# 基于docker仓库拉取镜像构建
#Bootstrap: docker
#From: ubuntu:22.04

# 基于本地镜像进行构建
Bootstrap: localimage
From: ubuntu_22.04.sif
# 镜像的密钥
Fingerprints:

%setup
touch main.py
cat << 'EOF' > main.py
import datetime
print("hello singularity, now is: %s" % datetime.datetime.now())
'EOF'

%files
./main.py /opt/

%runscript
echo "the version is v0.1.0"
exec "${@}"

%environment
export NAME=sxk

%post
apt update
apt install python3 -y

%labels
Author: sxk
Version: demo

%help
exec python demo scripts
usage: singularity run python.demo.sif python3 /opt/main.py
EOF

然后进行构建:

1
sudo singularity build python.demo.sif python.demo.def

构建成功后,就可以执行该镜像:

1
2
singularity run python.demo.sif python3 /opt/main.py
# run 就会执行runscript的脚本作为启动 `python3 /opt/main.py` 作为运行参数传入runscript 输出结果如下图所示

python.demo结果

  1. 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
    2
    Bootstrap: localimage
    From: ubuntu_22.04.sif # 镜像路径
  • docker-daemon(从docker后台拉取镜像做底包)
    1
    2
    Bootstrap: 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容器压缩包路径
  1. sections详解
    介绍每一个章节的使用功能
  • %setup(在构建容器之前设置)

    通常在这个阶段用来初始化一些文件或者容器内部的文件等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    %setup
    # 在宿主机上创建一个文件
    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

    构建底包后执行结果如下:
    singularity.setup.sections

  • 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
    9
    package 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
    31
    # mutil-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
    15
    %post
    # 导入需要的环境变量
    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
    2
    %startscript
    echo "running startscript ..."

    使用: singularity start python.demo.sif

  • runscript(镜像运行时执行的脚本,会自动被写入到/.singularity.d/runscript中)

    runscript 在构建阶段将所有内容写入/.singularity.d/runscript,当使用 run 时 会自动执行脚本,通常我们会将该脚本全部放开,增强镜像的灵活

    1
    2
    %runscript
    exec "${@}"

    使用: singularity run python.demo.sif

  • labels(给镜像打标签,键值对形式)

    为镜像打标签,给镜像增加元数据, 使用空格分割,前面是key 后面是value

    1
    2
    3
    4
    %labels
    Author 武安君
    Version v0.1.0
    Time 2024-11-1

    查看镜像的标签: singularity inspect python.demo.sif

  • help(给使用镜像增加帮助信息)

    通常在help中写容器的功能以及如何使用容器

    1
    2
    3
    %help
    only show singularity how to use
    created by 武安君

    查询帮助: singularity run-help python.demo.sif

  • test(镜像测试)

    验证是否打包镜像成功,可以验证功能 或者验证文件等

    1
    2
    3
    4
    5
    6
    %test
    if [ `ls -hl /bin/ct | wc -l` -eq 2 ]; then \
    echo "install successfully"; \
    else \
    echo "install failed" ; \
    fi

    说明: %test部分在构建过程的最后运行
    验证: singularity test python.demo.sif

  1. 运行构建参数详解
  • --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. 运行启动参数详解
    以下列为例,阐述常用的配置参数:
    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. 镜像加密
  • 密码加密
    1
    2
    3
    export 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. 使用加密镜像
  • 密码加密
    1
    2
    3
    4
    SINGULARITY_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
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
# mutil-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 ${@}

注意: 多阶段构建中,不能从上一个阶段获取下一阶段的数据文件

六. 多应用构建与使用

多应用构建就是将多个具有相同运行环境的应用构建到一个环境中进行敷用,也可以给每个应用配置各自特有的运行环境
其实就是给每个section加上app前缀,同时对操作的名称进行改变,如下:

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
touch foo.txt
echo "hello world" >> foo.txt

cat << 'EOF' > apps.def
Bootstrap: localimage
From: ubuntu_22.04.sif
# 全局配置
%environment
GLOBAL=variables
AVAILABLE="to all apps"

##############################
# foo
##############################

%apprun foo
exec echo "RUNNING FOO"

%applabels foo
BESTAPP FOO

%appinstall foo
touch foo.exec

%appenv foo
SOFTWARE=foo
export SOFTWARE

%apphelp foo
This is the help for foo.

%appfiles foo
#./foo.txt /scif/apps/foo/

##############################
# bar
##############################

%apphelp bar
This is the help for bar.

%applabels bar
BESTAPP BAR

%appinstall bar
touch bar.exec

%appenv bar
SOFTWARE=bar
export SOFTWARE

%apprun bar
echo "RUNNING BAR"
EOF
  • apphelp 和单独的help一样
  • applabels 和单独的labels一样
  • appinstall 和单独的post一样
  • appenv 和单独的environment一样
    使用特定app运行:
    1
    2
    3
    singularity build --fakeroot --force apps.sif apps.def
    singularity run --app foo apps.sif
    singularity run --app bar apps.sif

    七. 并行计算(ON HOLD)