LoginSignup
2
1

コンテナ(docker/podman, singularity)入門

Last updated at Posted at 2023-09-25

コンテナ入門

大学の共有サーバーで機械学習させるときにsingularityを使うことを推奨していたので、導入がスムーズになるように研究室向けに書きました。

コンテナの導入と使い方、 docker/podman のイメージ作成、singularity のイメージ作成と docker/podman のコンテナから singularity で使える形式への変換について記載します。
singularity のコンテナファイルをローカルでビルドするために docker/podman を使用するので、docker/podman についての解説もしています。
なお、注釈は完全な余談です。

singularity Quick Start

# 対話シェルの起動: --nvオプションでGPUの使用
singularity shell --containall --nv <name>.sif
# コンテナでコマンドを実行: --nvオプションでGPUの使用
singularity exec --containall --nv <name>.sif <cmd>
# ホストファイル・ディレクトリのマウント (パスは絶対パス)
singularity shell --containall -B /path/to/file/or/dir <name>.sif
# ,で繋げると複数マウント
singularity shell --containall -B /path/to/file/or/dir,/path/to/file/or/dir <name>.sif
# マウントする場所を指定する (read onlyにする場合、末尾に:roを追加)
singularity shell --containall -B /path/to/host:/path/to/container,/example:/mnt:ro <name>.sif

docker/podman Quick Start

  • CLI 操作のみ記述 (GUI を言葉で説明するのは難しい...)
  • ローカルマシンで開発や検証をしたい人向け
  • docker を使っているならpodmandockerに読み替えてください
# コンテナイメージのpull (<username> = <image_name>のときはどちらか一方だけでいい)
podman pull (<remote>/)<username>/<image_name>:<tag>
# ex) podman pull docker.io/ubuntu/ubuntu:22.04
# コンテナのバックグラウンド起動
podman run -id --name <name> <username>:<tag>
# コンテナにホストのファイルやディレクトリをマウントしてバックグラウンド起動
podman run -id -v /absolute/path/to/host:/absolute/path/to/container --name <name> <username>/<image_name>:<tag>
# コンテナでシェルの起動
podman exec -it <name> bash
# コンテナの終了
podman stop <name>
# 起動コンテナの確認
podman ps
# 作成した全てのコンテナの確認
podman ps -a
# コンテナの破棄
podman rm <name>
# コンテナイメージの破棄
podman rmi <username>/<image_name>:<tag>

用語など

  • 説明にあるコマンドと実行結果の見方
# コメントアウト
<cmd>  # 実行コマンド ($を含めないで実行する)
> foobar # 実行結果
  • Linux ディストリビューション
    Linux は思想の違いで様々な形で配布されています。
    特にパッケージ管理の仕方が大きく違います。
    ubuntu・debian で使われているaptについて知っておけばとりあえず大丈夫

コンテナとは

A container is a standard unit of software that packages up code and
all its dependencies so the application runs quickly and
reliably from one computing environment to another.

(https://www.docker.com/resources/what-container/ より)

あるコードとそれを動かすための依存関係をパッケージ化して、どのマシン環境でも関係無く動かすための技術です。
現在主流のコンテナ1は、基本的には 1 サービス 1 コンテナで動かします。

使う分には実験(使う機械学習モデル)ごとにホストと隔離された root が使える環境を作ってその中で作業するんだなくらいで大丈夫です。

大分余談: docker/podmanとsingularity

docker は Docker Inc が、podman は Redhat が開発しているコンテナです。
どちらもコンテナの業界標準である OCI (Open Container Initiative)に準拠しています。
細かい仕様の違い2はありますが、コマンドとオプションは高い互換性があります。

singularity は HPC (High Perfomance Computer)上で実行することを想定し開発されたコンテナです。
環境をファイルに出力し、より可搬性の高いコンテナ環境を提供します。
また、コンテナ内外で同じユーザーで実行されます。 (docker/podman は設定しなければコンテナ内では root ユーザー)
OCI とは互換性がないため docker/podman で作成した環境を直接 singularity で使うことはできません。

singularity のセットアップ

Ubuntu on WSL2にsingularityをインストールします。

WSL2のセットアップ

  • コマンドラインから

    1. 管理者権限でpowershellもしくはwindows terminalを開きwsl --installを実行
    2. 一般ユーザーでpowershellもしくはwindows terminalを開き直してwsl --install Ubuntuを実行
    3. wsl -d Ubuntuで起動 (2. の時点でユーザーの登録を求められたらここはスキップ)
    4. 初回セットアップを行う (ユーザーの登録とパスワードの設定)
  • windows storeから

    1. windows storeを開きwslで検索
    2. Windows Subsystem for Linuxをインストール
    3. ubuntuで検索
    4. Ubuntu 22.04 LTSをインストール後、Openで起動 (ターミナルまたはpowershellが開く)
    5. 初回セットアップを行う (ユーザーの登録とパスワードの設定)

singularityのインストール

  1. 依存パッケージのインストール

    sudo apt update
    sudo apt install -y libfuse2 uidmap squashfs-tools-ng
    
  2. singularityのインストール

    • 導入したubuntuのバージョンが22.04であることが前提
    • 異なるバージョンはjammyを適切なものに変更
    VERSION=4.0.1
    wget https://github.com/sylabs/singularity/releases/download/v$VERSION/singularity-ce_$VERSION-jammy_amd64.deb
    sudo dpkg -i ./singularity-ce_$VERSION-jammy_amd64.deb
    rm ./singularity-ce_$VERSION-jammy_amd64.deb
    

作業ディレクトリの作成

  • 作業ディレクトリの下に`containersというディレクトリを作成
  • Dockerfilerecipe.def はこの後で作成
workdir
└── recipe.def

定義ファイル(recipe.def)の作成

  • pytorch と tensorflow に加えgitwgetが使えるコンテナを作成する
BootStrap: docker
From: pytorch/pytorch:2.0.0-cuda11.7-cudnn8-devel

%setup

%files

%environment

%post
    apt update
    apt install -y \
        git \
        wget
    conda install \
        pip
    pip install \
        tensorboard
定義ファイルの詳細
必須
  • Bootstrap
    • ベースイメージが存在する外部リポジトリを定義、基本的には docker 、他は公式の Docs 参照
  • From
    • 使用するイメージ。docker であればDockerHubから探す
  • %post
    • 必要なパッケージのインストールや git リポジトリのクローンなど
    • シェルスクリプトを書くのと同様に
良く使う
  • %setup
    • コンテナ外つまりホスト上で行なう操作を記述
    • シェルスクリプトを書くのと同様に
    • 非推奨
  • %files
    • ホストに存在するファイルをコンテナにコピーする
    • GitHub にない自作スクリプトやデータセットをコンテナで使うときに使用
    • <source> <target>のように記述: source=ホストのデータへのパス、target=コンテナ内のパス、Linux のディレクトリ構造と同じ
      • Ex. README.md /workspace/README.md
      • ビルドを実行したディレクトリからの相対パスで記述可能
  • %environment
    • 環境変数の定義
    • export foo=varのように記述

使わなくても困らない

  • %test
    • ファイルが適切にクローンやコピーされたかなどのテストプログラムを定義
    • シェルスクリプトを書くのと同様に
  • %startscript
    • ビルド時のみに実行されるスクリプトの定義
  • %runscript
    • コンテナを呼び出した時に必ず実行されるスクリプトの定義
    • jupyter の起動などに
  • %labels
    • メタデータの定義
    • name valueで定義

環境のビルドと確認

  1. 環境のビルド
    singularity build --fakeroot sample.sif recipe.def 
    
  2. 環境の確認
    singularity run sample.sif
    

docker/podmanイメージからのビルド

docker/podmanで作成したイメージをsingularityで使える形式へビルドします。
これは、singularityとdocker/podmanが同一のインスタンス(ここではUbuntu-22.04 on WSL2)で実行されていることが前提です。

  • dockerの場合

     singularity build --fakeroot test.sif docker-daemon://<name>(/<imagename>:<tag>)
    
  • podmanの場合

    • rootlessで動作している場合: --docker-host unix:///var/run/user/<user_id>/podman/podman.sock
    • rootfullで動作している場合: sudoを付けて--docker-host unix:///var/run/podman/podman.sock
    singularity build --docker-host unix:///var/run/<path/to/each/user>/podman/podman.sock --fakeroot test.sif docker-daemon://<name>(/<imagename>:<tag>)
    

windows でコンテナを使えるようにする

ここでは podman-desktop3を使って windows マシンでコンテナを使えるようにします。
共用のマシンがあると思いますが、以下の点から導入することをオススメします。

  • 共用マシンには root 権限が付与されていないが、自分のマシンでは root 権限を持てる。
  • 手元のマシンだと環境の構築とテストがしやすい。
  • 共用マシンの OS のバージョンに左右されずに作業ができる。
  • Linux の勉強がしやすい。

導入手順

windows 10 version 2004 以上が必要です。

  1. podman-desktop のダウンロードページからダウンロード

    • winget を使ってるならwinget install -e --id RedHat.Podman-Desktop
  2. podman-desktop を起動

    • WSL2の設定など必要なコンポーネントが無いときはpodman-desktopの指示に沿って設定すればいい
  3. windows-terminal を起動 (スタートメニューでterminalと入力すると出てくる)

    • windows のバージョンによってはインストールされていないことがあります。
      その時は powershell を起動してください。
  4. podman -vと入力してバージョンが出てくれば OK

docker/podman の操作

singularityはコンテナイメージを固定するために柔軟な変更が困難です。
そのため、docker/podmanを使って環境をテストしてからsingularityで固定すると良いでしょう。
まだ、

コンテナイメージ の pull

コンテナを使うには、コンテナイメージを持ってくる必要があります。
今回は ubuntu 22.04 を使用します。
別のイメージを使う場合はubuntu:22.04を置き換えます。

  • podman-desktop

    1. サイドパネルから Images -> Pull an Image
      2023-06-30_14-37.png

    2. Image to Pull にubuntu/ubuntu:22.04と入力
      2023-06-30_14-39.png

  • CLI

    1. podman pull ubuntu/ubuntu:22.04と入力
    2. 選択肢が出てきたらdocker.ioを選択

解説

ubuntu:22.04を見るとname/image_name:tagの形式になっています。
name は image_name を管理している人や団体、image_name は使いたいソフトや Linux ディストリビューションの名前、tag でバージョンを指定します。
image はDockerHubから検索します。
tag は指定しないことも可能です。
その場合、最新のバージョンが pull されます。
実験環境を再現可能なものにするため tag の利用を強く勧めます。

  • ubuntu を検索した例 (docker pull ubuntu/ubuntu:<tag>の docker を podman に置き換えると pull できる)
    2023-06-30_14-50.png

コンテナの起動

Container Name は任意の文字列です。
指定しないと適当な文字列が割り振られます。

  • podman-desktop

    1. サイドパネルから Images -> ubuntu:22.04 の再生ボタン

    2. Container nameHelloWorldと入力

    3. Environment variablesに Name->FOO、Value->BARと入力

    4. Start Container で起動
      2023-06-30_14-57.png

    5. サイドパネルから Containers -> HelloWorld -> Terminal
      lsと入力して出力があれば OK
      2023-06-30_15-13.png

  • CLI

    1. podman run -id --name HelloWorld ubuntu:22.04
    2. podman exec -it HelloWorld bash
      root@<hash>:/# という表示が出れば OK

ホストのファイルやディレクトリをマウントしての起動

ホストのファイルやディレクトリは起動時にオプションで指定することでコンテナにマウントしてコンテナ内から使用できるようにします。
データセットはコンテナに含めることによるコンテナの肥大化を防ぐため、またモデルの学習結果をホストから利用するためにマウントを利用します。

  • podman-desktop

    • コンテナ作成時の画面のVolumesから絶対パスで指定する
      左がホスト、右がコンテナ内の絶対パス
      2023-07-03_18-20.png
  • CLI

    • -v, --volumeオプションをpodman runに追加する ex)
      podman run -id -v /home/user/study/exp1:/tmp/exp1 --name HelloWorld ubuntu:22.04
      

解説

デスクトップからの操作はそのままなので割愛します。
podman runでコンテナを起動します。
コンテナイメージで常駐するようなアプリ(web サーバーなど)が指定されていない場合、起動したコンテナはすぐにストップします。
これを防ぐためにオプション-i, --interactiveで起動状態を維持します。
また、バックグラウンドで起動させるためにオプション-d, --detachを指定しています。

起動したコンテナ環境に入るにはpodman execを使います。
podman execではコマンドが実行されるとコンテナから弾き出されます。
今回は bash を起動してそのままコンテナに留まりたいので、podman runの時と同じようにオプション-i, --interactiveを指定して維持しています。
また、-t, --ttyを指定してシェルで対話できるようにしています。

その他のオプションはpodman run --helppodman exec --helpで出力できます。

大体は
podman run -id --name <name> <image>:<tag>でコンテナを起動、
podman exec -it <name> bashでコンテナ環境に入るので事足りると思います。
また、podman run --rm -it <image>:<tag> bash使い捨ての環境を作成できます。(オプション--rmは remove)
これはコンテナの bash から Exit した時に起動したコンテナも破棄します。

コンテナの確認

  • podman-desktop

    • 非起動状態 (status が枠のみ)
      2023-07-03_18-04.png

    • 起動状態 (status が緑色になる)
      2023-07-03_18-04_1.png

  • CUI

    • 起動コンテナの確認 →podman ps
      STATUS が Up <time>になっている
      2023-07-03_18-07.png

    • 作成した全てのコンテナの確認 →podman ps -a
      STATUS が Exit になっている
      2023-07-03_18-09.png

コンテナの停止と破棄、コンテナイメージの破棄

実験が一通り終わり次の実験に移るときはコンテナを破棄することが良いでしょう。
コンテナは起動するほどディスク容量を消費します。

  • podman-desktop
    1. サイドパネルから Containers -> HelloWorld の停止ボタン
    2. statusが黒くなったら、ゴミ箱ボタン
      2023-06-30_15-41.png
    3. サイドパネルから Images -> docker.io/ubuntu:22.04 のゴミ箱ボタン
  • CLI
    1. podman stop HelloWorldでコンテナを停止
    2. podman rm HelloWorldでコンテナを破棄
    3. podman rmi ubuntu:22.04

コンテナへパッケージを追加

image の pullコンテナの起動を元に ubuntu:22.04 のコンテナで bash が実行されていることを前提に進めます。
始めてコンテナに入るとlessunzipなどのコマンドが無いことに気付くでしょう。(less -hunzip -hを実行してみてください。)
コンテナは必要最小限の環境であり、パッケージを追加することで実験環境を構築します。
ここではpytorchtensorflowのベースイメージで使われるaptについてのみ説明します。
なお、文献によってはapt-getと書かれていることがありますが、直近の ubuntu ではaptに変更されました。

リモートリポジトリのアップデート

apt update

パッケージのインストール

-yを付けるとパッケージをインストールするかの確認がスキップされる

apt install -y less
# 複数パッケージを一度に追加 (末尾に\で繋ぐ)
apt install -y \
  git \
  python-is-python3

キャッシュの削除

apt clean

パッケージの検索

あいまい検索でヒットしたものが列挙される。
あまり使わない。
web で docs に書いてある方法や先人のやっているインストールパッケージに従った方が早い。

apt search libsound
> Sorting... Done
> Full Text Search... Done
> libsoundio-dev/jammy 2.0.0-2 amd64
>   cross platform audio input and output library (development files)
>
> libsoundio2/jammy 2.0.0-2 amd64
>   cross-platform audio input and output library
> ...

ppa の追加

ubuntu や debian のリモートリポジトリには古くて安定しているバージョンが載せられている。
最新バージョンを使いたい場合、そのパッケージが独自にリモートリポジトリを持っていてそこから利用できることがある。

# neovimの最新バージョンをインストールする
# ppa追加の準備
apt install -y software-properties-common
# ppaの追加
add-apt-repository ppa:neovim-ppa/stable
apt update
apt install -y neovim

コンテナの保存

実行中のコンテナからコンテナイメージを作成します。

podman commit <container-hash or name> <name>(/<imagename>:<tag>)

Dockerfile を使ったコンテナイメージの作成

ここまでで CLI から環境を作ることができます。
しかし、コミットを忘れるとその環境は全て破棄され再現できません。
再現可能な環境を作るためにはDockerfileを用いて環境を定義することが望ましいです。
ここではpythoncondaが使える環境を作りながら説明します。
下のようなディレクトリ構造にします。

example
├── Dockerfile
└── requirement.txt

Dockerfile の作成

適当なエディタで下のファイルを作成
pipcondaが混在していますが、説明用ということで...。

FROM docker.io/ubuntu/ubuntu:22.04

RUN apt update && \
    apt install -y \
      wget && \
    apt clean
RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \
    bash Miniconda3-latest-Linux-x86_64.sh -b -p /opt/miniconda3 && \
    rm -r Miniconda3-latest-Linux-x86_64.sh
ENV PATH /opt/miniconda3/bin:$PATH

WORKDIR /workdir
COPY requirement.txt requirement.txt
RUN conda update -n base -c defaults conda && \
   pip install -r requirement.txt && \
   conda install -c conda-forge \
     matplotlib

CMD ["/usr/bin/env", "bash"]

解説

  • FROM: コンテナのベースイメージを指定。
    podman pullと同じように<remote>/<username>/<image_name>:<tag>で指定。
  • RUN: コマンドを実行。
    &&で複数コマンドを繋げて列挙していく。
    \で改行を解釈させる。(複数行にして人に見やすいようにしている。)
    apt update && \  # && はコマンドの実行が成功したら次のコマンドを実行するという意味
    apt install -y \ # 改行。apt install -y wgetと同義で解釈される。
        wget
    
  • ENV: 環境変数を定義。
    ENV 変数名 値
  • WORKDIR: 指定したパスに移動する。存在しない場合は作成する。
    新しくWORKDIRが指定されるまでそのパスの元でコマンドが実行される。
  • COPY: ホストのファイルをコンテナにコピーする。
    COPY path/to/host path/to/container
  • CMD: podman runで実行されるコマンド
    podman run <image_name> <cmd>で上書き可能

上記を知っていれば大体は対応できます。
対応できないときはリファレンスの参照や都度知らべてください。(特に何をベースイメージにするかなど)

コンテナイメージのビルド

  • podman-desktop
  • CLI
    1. podman build -t example/python:22.04 .

解説

-t, --tagでコンテナイメージに名前を付けます。
Dockerfile が存在するディレクトリを指定して実行します。
コンテナの起動は先に解説した通りです。

Dockerfile 作成の Tips

コンテナイメージのサイズを小さくするために、RUNをできるだけ少なくすることが推奨されます。
コードの視認性が落ちること、デバッグがしにくくなるので慣れてから意識するといいでしょう。
例えば、先のDockerfileは下のように書き直されます。

FROM docker.io/ubuntu/ubuntu:22.04

ENV PATH /opt/miniconda3/bin:$PATH
WORKDIR /workdir
COPY requirement.txt requirement.txt
RUN apt update && \
    apt install -y \
      wget && \
    apt clean && \
    wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \
    bash Miniconda3-latest-Linux-x86_64.sh -b -p /opt/miniconda3 && \
    rm -r Miniconda3-latest-Linux-x86_64.sh && \
    conda update -n base -c defaults conda && \
    pip install -r requirement.txt && \
    conda install -c conda-forge \
      matplotlib

CMD ["/usr/bin/env", "bash"]

余談: Linux の操作

コンテナは何かしらの Linux ディストリビューションなので操作は UNIX コマンドで行います。

pwd, env, echo, ls, find, mkdir, cp, ln, chmod, chown, grep, egrep, xargs

パイプ・リダイレクトについては覚えておいて損はないでしょう。

下に簡単な例と実行結果をまとめました。
image の pullコンテナの起動を元に ubuntu:22.04 のコンテナで bash が実行されていることを前提に進めます。

ディレクトリの作成

# mkdir -p <path/to/dir>
mkdir workdir            # workdirを作成
mkdir -p workdir/foo/bar # 親ディレクトリが無い時、親ディレクトリも作成する

current dir の移動

# cd <dir name>
cd workdir

空ファイルの作成

# touch <file name>
touch foo.txt
touch .hidden.txt  # .から始めると隠れファイル(ディレクトリ)となってls では表示されない

ファイル・ディレクトリの列挙

ls    # 列挙
ls -a # .から始まる隠れファイル(ディレクトリ)も列挙

シンボリックリンクの作成

シンボリックリンクは別のパスにあるファイルやディレクトリを参照します。
散けているファイルを容量を圧迫せずに別のディレクトリにまとめることができます。

ln -sf foo.txt foobar.txt
# 参照元が→で表わされている
ls -l
> lrwxrwxrwx 1 root root    7 Jul  4 05:47 foobar.txt -> foo.txt

ファイル・ディレクトリの検索

find ./ -name "foo.txt"             # "foo.txt"を配下のディレクトリ全てから検索
find ./ -name "*.txt"               # 拡張子が ".txt"であるファイルを配下のディレクトリ全てから検索
find ./ -name "foo.txt" -maxdepth 2 # "foo.txt"を配下のディレクトリのうち深さ2までで検索
find ./ -type f                     # 配下のディレクトリにある全てのファイルを検索
find ./ -type d                     # 配下のディレクトリにある全てのディレクトリを検索

パイプとリダイレクト

Linux ではパイプやリダイレクトを使うことでコマンドの実行結果を他のコマンドに与えることができます。
複数ファイルに対して同じ操作を行なうときに便利です。

  • リダイレクト
# >がリダイレクト (echoの結果をbar.txtに出力)
echo "Hello World !" > bar.txt
cat bar.txt # bar.txtの内容を表示
> Hello World !
# bar.txtに追記
echo "This is container tutorial." >> bar.txt
# bar.txtを新しい出力で置き換える
echo "Replace !" >| bar.txt
# EOFが再度出現するまでの文字列をsample.shに出力
# 簡単なスクリプトならエディタが無くても書ける
cat <<EOF > sample.sh
echo $FOO
FOO="Hello World"
echo $FOO
>>EOF
bash sample.sh
> BAR
> Hello World
  • パイプ
# catの出力をgrepに渡している
# grep <語>で語にヒットする行を出力
cat sample.sh | grep Hello
> FOO="Hello World"

特定の文字列を含むファイルの検索

例えば、numpyを import しているファイルを知りたいというような状況で役立ちます。

egrep -r echo ./  # 配下のファイル全てに対してechoを含むファイルを検索

文字列の置換

sedを用います。
正規表現を使って文字列の置換をします。
文字列の挿入や特定の行に対しての置換もできます。

# s/置換前/置換後/g (置換前の語にヒットした全てに対して置換する)
cat sample.sh | sed -e 's/Hello/New/g'
echo $FOO
FOO="New World"
echo $FOO
# 置換して上書き (-i オプション)
sed -i -e 's/Hello/New/g' sample.sh
# 最終行に"echo END"を追記 ($a<追加文字列>)
sed -i '$aecho END' sample.sh

複数ファイルの操作

xargsで並列処理ができる。
拡張子の一括変更や複数ファイルの移動に便利

# ファイル名だけ表示
find ./*.txt -type f | xargs baesname
# -I% は渡された結果を%がある位置に置くオプション
# 検索でヒットしたファイルを別のディレクトリにコピーする
find ./*.txt -type f | xargs -I% cp % ./foo/bar
# 拡張子の一括変更
find ./*.txt -type f | xargs -I% sh -c 'cp % ./foo/bar/$(basename % | sed -e "s/.txt/.md/g")'
ls ./foo/bar
> foo.txt bar.txt foo.md bar.md

参考文献

  1. Linux Containers (lxc)のようにコンテナを超軽量の VM として用いる場合もあります。
    (docker のようなアプリケーションコンテナと対比してシステムコンテナと呼ばれます。)

  2. docker はデーモンが常駐(実行ユーザーに root 権限が必要)しますが、podman はデーモンレス・ルートレスコンテナとして実行できます。
    自分のマシンで使う分にはあまり問題ではありませんが...。

  3. docker-desktopがよく使われますが、私が普段使っているのが podman なので布教も兼ねて podman-desktop を選択しました。2023/06/30 現在 v1.1 がリリースされているので使用に耐えうると判断したのも要因です。
    え?docker-compose が使いたい?podman-compose もあるし podman pod もあるよ...。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1