Apptainerことはじめ

# Apptainer
[Apptainer - Portable, Reproducible Containers](https://apptainer.org/): Singularityと呼ばれていたものが、Linux Foundationに入ることによってApptainerに変わった。

関連ポスト: [Singularityことはじめ #初心者 - Qiita](https://qiita.com/Yasushi-Shinohara/items/e3870079829dbab39abe)

# Ubuntu22.04へのインストール
`cat /etc/lsb-relase`で
```console:出力
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.4 LTS"
```
で、入れるディストリビューションはUbuntu 22.04。
chatGPT先生の教えに従って、関連ライブラリをインストール:
```bash
sudo apt update
sudo apt install -y build-essential libseccomp-dev pkg-config squashfs-tools cryptsetup
```

あとは公式[Installing Apptainer &mdash; Apptainer Admin Guide main documentation](https://apptainer.org/docs/admin/main/installation.html#installation-on-linux)のInstall Debian packagesの[箇所](https://apptainer.org/docs/admin/main/installation.html#install-debian-packages)に従って、以下のコマンドでGithubから`deb`ファイルをとってきてインストール:
```bash
export VERSION=1.4.0  # 2025/5/5時点最新安定版
wget https://github.com/apptainer/apptainer/releases/download/v${VERSION}/apptainer_${VERSION}_amd64.deb
sudo dpkg -i apptainer_${VERSION}_amd64.deb
```
## トラブルシューティング
### `conflicting packages - not installing apptainer`
```console:error message
dpkg: error processing archive apptainer_1.4.0_amd64.deb (--install):
 conflicting packages - not installing apptainer
Errors were encountered while processing:
 apptainer_1.4.0_amd64.deb
```
でインストール未達だった。
前の記事[Singularityことはじめ #初心者 - Qiita](https://qiita.com/Yasushi-Shinohara/items/e3870079829dbab39abe)で、`singularity-ce`を入れていたので、そことコンフリクトしていた。
`sudo apt remove singularity-ce`で消した後に`sudo dpkg -i apptainer_${VERSION}_amd64.deb`でインストール完了。

例えば`apptainer --version`、`singularity --version`で
```console
apptainer version 1.4.0
```
が返ってくる。

# oneapi-hpckitのコンテナ`oneapi-hpckit.sif`をつくって使ってみる
```def:oneapi-hpckit.def
Bootstrap: docker
From: intel/oneapi-hpckit:2025.1.1-0-devel-ubuntu22.04

%post
    apt-get update && apt-get install -y build-essential
    echo 'source /opt/intel/oneapi/setvars.sh' >> /environment
    echo 'source /opt/intel/oneapi/setvars.sh' >> /etc/profile.d/oneapi.sh

%environment
    source /opt/intel/oneapi/setvars.sh

%runscript
    echo "Intel oneAPI HPC Toolkit container is ready."
    exec "$@"
```
`apptainer build oneapi-hpckit.sif oneapi-hpckit.def`でコンテナを作成。このコマンドには特に`sudo`は必要ない。

## `apptiner shell oneapi-hpckit.sif`でコンテナに入って操作してみる
`apptiner shell oneapi-hpckit.sif`でコンテナに入る。
ここでの環境は、基本的にホストと無関係にコンテナ内に構築されたものだが、`$HOME`と`$PWD`、`/tmp`はホストと共有されたものになっている。<font color="red">この共有はread+writeで、コンテナ内で作成した`$HOME`, `$PWD`, `/tmp`のファイルはコンテナを閉じた後も残る。</font>この点が、dockerのコンテナとは決定的に違うポイント。

oneapi-hpckitのdockerコンテナをベースにしているので、
```console:
Apptainer> which ifx
/opt/intel/oneapi/compiler/2025.1/bin/ifx
Apptainer> mpiifx --version
ifx (IFX) 2025.1.1 20250418
Copyright (C) 1985-2025 Intel Corporation. All rights reserved.
```
といった出力が得られる。

## トラブルシューティング
### ネットワークの不安定?
~~何度やっても以下のエラーが出る。~~ 20回くらい試みても、ほとんどの試行は以下のエラーで止まる:
```console
FATAL:   While performing build: conveyor failed to get: error writing layer: local error: tls: bad record MAC
```
エラーが出る時間が違うので、多分ネットワークの不安定性によるものと判断。一旦`docker pull`で落としてそこから作ることにした。
```bash
sudo docker pull intel/oneapi-hpckit:2025.1.1-0-devel-ubuntu22.04
sudo docker save intel/oneapi-hpckit:2025.1.1-0-devel-ubuntu22.04 -o oneapi-hpckit.tar
sudo apptainer build oneapi-hpckit.sif docker-archive://oneapi-hpckit.tar
```
で`oneapi-hpckit.sif`ができる。`docker pull`の方が何となく、ネットワークの不安定性に強いよう。
こちらのコマンドでは、`docker-archive://oneapi-hpckit.tar`にアクセスするために、最後のコマンドに`sudo`が必要。

# oneapi-hpckitのコンテナ`oneapi-hpckit.sif`を改造する
なんでoneapi-hpckitのコンテナを作ったかって、そりゃoneapi-hpckitに入っているMPIライブラリをリンクしたインテルのコンパイラでビルドしたアプリの入ったコンテナを作りたかったからである。というわけで、`oneapi-hpckit.sif`を改造して、自分がビルドしたいアプリケーションを入れ込んだsifコンテナを作る。

基本的な流れは(ChatGPT先生による提案により)
```bash
# 1. 展開
apptainer build --sandbox my_sandbox/ base.sif

# 2. 修正
apptainer shell --writable my_sandbox/
# → apt install, ファイル編集, ビルドなど
# apt installする際はコマンドをsudo apptainer...にしておいたほうがよい

# 3. 再パッケージ化（任意）
sudo apptainer build my_modified.sif my_sandbox/
```

## Elk-10.4.9を`/opt/elk/src/elk`として追加した`oneapi-hpckit4elk.sif`を作る
まず適当なディレクトリでsandboxを作成:
```bash
apptainer build --sandbox my_sandbox/ oneapi-hpckit.sif
```

次に`vim`と`less`を入れるために管理者権限で入る:
```bash
sudo apptainer shell --writable my_sandbox/
```
```console
Apptainer> apt update
Apptainer> apt install vim
Apptainer> apt install less
```
でファイル書き換えや閲覧の準備完了。

[The Elk Codeビルドの備忘録 #DFT - Qiita](https://qiita.com/Yasushi-Shinohara/items/cd95262beb2629a11ef2#oneapi-hpckit-202510%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Felk-1049%E3%81%AE%E3%83%93%E3%83%AB%E3%83%89libxc%E3%81%AE%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%AF%E7%84%A1%E3%81%97)
に則って、Elk-10.4.9をsandbox内の`/root/sources/elk-10.4.9`にインストールする。(Elkは古式ゆかしいアプリケーションなので、prefix等でインストール先を指定するようなことはしない)
```bash
ln -s /root/sources/elk-10.4.9 /opt/elk
```
で`/opt/elk`にシンボリックリンクを貼る。つまり、実行するファイルは`/opt/elk/src/elk`としてアクセスできるようにした。ここにPATHを通したい。

Apptainerコンテナ内でPATHを通すためのお作法としては`/.singularity.d/env/90-environment.sh`に環境変数の更新案を記入するのが適切な方法の一つであるとchatGPTに習ったので、
```bash:/.singularity.d/env/90-environment.sh(抜粋)
export PATH="/opt/elk/src:$PATH"
```
を追記した。これで`which elk`を入力すると、PATHが通っていることが確認できる。

このsandboxをsifコンテナにまとめる:
```bash
sudo apptainer build oneapi-hpckit4elk.sif ./my_sandbox
```
こうしてできたコンテナに`apptainer shell oneapi-hpckit4elk.sif`で入って、`which elk`で適切にPATHが通っているかどうか確認できる。

### トラブルシューティング
#### `dpkg: error: requested operation requires superuser privilege`
`vim`をインストールする際に`apptainer shell --writable my_sandbox/`として`apt install vim`を実行すると
```console:出力(抜粋)
perl: warning: Falling back to a fallback locale ("C.UTF-8").
debconf: delaying package configuration, since apt-utils is not installed
dpkg: error: requested operation requires superuser privilege
E: Sub-process /usr/bin/dpkg returned an error code (2)
```
となって、インストールできない。これを解決するためにはsandboxに入るときに`sudo apptainer shell --writable my_sandbox/`で実行する必要がある。

## Elk-10.4.9を`/opt/elk/src/elk`として追加した`oneapi-hpckit4elk.sif`を実行
上で作ったsifコンテナを動かしてみる。前提として、ホストにはoneapi-hpckitの2025.1.0を入れてあり、ランタイムの`mpirun`はインストールされている。

今回作ったコンテナでは`%runscript`に`elk`は追加していないので、自動実行はされない。従って、`apptainer exec oneapi-hpickit4elk.sif elk`等として、明示的に実行コマンドを指定することになる。これをslurmで実行するスクリプトは以下の通り(参考URL:[Slurmことはじめ #初心者 - Qiita](https://qiita.com/Yasushi-Shinohara/items/f52f0f144e6605d5765b)):
```bash:calc.sh
#!/bin/bash
#SBATCH --job-name=ngridk_888
#SBATCH --time=02:00:00
#SBATCH --partition=debug
#SBATCH --ntasks=1 
#SBATCH --cpus-per-task=4

. /home/shinohara/intel/oneapi/setvars.sh

export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK

EXE=elk
MPIEXE=mpirun
SIF=/path/for/oneapi-hpickit4elk.sif

$MPIEXE apptainer exec $SIF $EXE > log.log
```
として、`sbatch calc.sh`で期待通りに実行ができた