Post

NVIDIA Container ToolkitとDocker/Podmanでディープラーニング開発環境を構築する(2)- GPU活用のためのコンテナランタイム構成、Dockerfileの作成およびコンテナイメージのビルド

このシリーズではローカルにNVIDIA Container Toolkitでコンテナベースのディープラーニング開発環境を構築し、リモートサーバーとして活用できるようにSSHおよびJupyter Labを設定する方法を扱います。この投稿はシリーズの2番目の記事で、Dockerfileを作成しコンテナイメージをビルドする過程を扱います。

NVIDIA Container ToolkitとDocker/Podmanでディープラーニング開発環境を構築する(2)- GPU活用のためのコンテナランタイム構成、Dockerfileの作成およびコンテナイメージのビルド

概要

このシリーズではNVIDIA Container ToolkitとDockerまたはPodmanをインストールし、Docker Hubのnvidia/cudaリポジトリから提供されるCUDAおよびcuDNNイメージをベースにDockerfileを作成してディープラーニング開発環境を構築する過程を扱います。必要な方が自由に利用できるよう、この過程を経て完成したDockerfileイメージをGitHubとDocker Hubを通じて共有し、さらにリモートサーバーとして活用するためのSSHおよびJupyter Lab設定ガイドを提供します。
シリーズは3つの記事で構成される予定で、この記事はそのシリーズの2番目の記事です。

x86_64 Linuxの環境でCUDAをサポートするNVIDIAグラフィックカードを搭載したシステムを前提として進め、UbuntuまたはFedora以外のディストリビューションでは直接テストしていないため、いくつかの細部は若干異なる場合があります。
(12025.02.18. 内容更新)

エラー訂正のお知らせ
人類紀元 12024年8月にアップロードしたこの記事の初稿では、Dockerfileの作成部分の記述および該当Dockerfileからビルドしたイメージに一部エラーがありました。問題があった部分は次の通りです:

  • remoteアカウント作成部分でパスワードを設定する部分が間違っており、本来なら「000000」をパスワードとして入力してログインできるはずですが、そうなっていませんでした
  • コンテナ起動時にSSHデーモンが自動実行されない

上記の問題点を最近認識し、韓国時間(UTC+9)基準12025年2月16日午前2時頃、問題のあったDockerfileとDockerイメージを問題を解決したファイルにGitHubリポジトリDocker Hubで置き換えました。
その時刻以前にDockerfileまたはDockerイメージをPullした場合は、修正されたバージョンに置き換えてください。
既にこの記事を参考にされた方々の中で、誤った内容で混乱を経験された方々がいらっしゃれば、お詫び申し上げます。

始める前に

この記事は第1回からの続きですので、まだ読んでいない場合は、まず前の記事から読むことをお勧めします。

4. コンテナランタイムの構成

Podmanを使用する場合

CDI(Container Device Interface)を活用して構成します。

次のコマンドを実行してCDI仕様ファイルを/etc/cdiディレクトリに生成します。

1
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

グラフィックカードデバイスを変更したり、CUDAドライバ構成を変更(バージョンアップグレードを含む)する場合は、CDI仕様ファイルを新たに生成する必要があります。

NVIDIA Container Runtime hookをCDIと一緒に使用すると衝突する可能性があるため、もし/usr/share/containers/oci/hooks.d/oci-nvidia-hook.jsonが存在する場合は、そのファイルを削除するか、またはNVIDIA_VISIBLE_DEVICES環境変数が設定された状態でコンテナを実行しないよう注意してください。

Dockerを使用する場合

ルートレス(rootless)モードを基準に説明します。

4-Docker-1. nvidia-ctkコマンドでコンテナランタイム設定を構成

1
nvidia-ctk runtime configure --runtime=docker --config=$HOME/.config/docker/daemon.json

上記のコマンドは、DockerがNVIDIA Container Runtimeを活用できるように/etc/docker/daemon.jsonファイルを修正します。

4-Docker-2. Dockerデーモンの再起動

変更した設定を適用するためにDockerデーモンを再起動します。

1
systemctl --user restart docker

4-Docker-3. sudo nvidia-ctkコマンドで/etc/nvidia-container-runtime/config.toml設定ファイルを構成

1
sudo nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place

正常に構成されたか確認

サンプルCUDAコンテナを実行してみます。

Podmanを使用する場合は次のコマンドを実行します。

1
podman run --rm --device nvidia.com/gpu=all --security-opt=label=disable ubuntu nvidia-smi

Dockerを使用する場合は次のコマンドを実行します。

1
docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi

おおよそ以下のような画面が表示されれば成功です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 555.58.02              Driver Version: 555.58.02      CUDA Version: 12.5     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 3090        Off |   00000000:01:00.0  On |                  N/A |
|  0%   46C    P8             29W /  350W |     460MiB /  24576MiB |      2%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|  No running processes found                                                             |
+-----------------------------------------------------------------------------------------+

5. Dockerfileの作成

Docker Hubのnvidia/cudaリポジトリから提供されるCUDAおよびcuDNNイメージをベースにして、開発環境として使用するDockerfileを作成します。

  • 必要とするCUDAおよびcuDNNバージョン、Linuxディストリビューションの種類およびバージョンなどを考慮して使用するイメージを決定する必要があります。
  • PyTorch 2.4.0がサポートするCUDAバージョンこの記事の執筆時点である12024年8月末を基準に、PyTorch最新バージョンである2.4.0バージョンはCUDA 12.4をサポートしています。したがって、ここでは12.4.1-cudnn-devel-ubuntu22.04イメージを使用します。PyTorchホームページでPyTorch最新バージョンおよびサポートするCUDAバージョンを確認できます。

完成したDockerfileのソースはyunseo-kim/dl-env-docker GitHubリポジトリに公開しています。以下に該当Dockerfileを作成した過程をステップごとに説明します。

5-1. baseイメージの指定

1
FROM nvidia/cuda:12.4.1-cudnn-devel-ubuntu22.04

5-2. 基本ユーティリティとPython prerequisitesのインストール

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Install basic utilities and Python-related packages, gosu, and SSH server
RUN apt-get update -y && apt-get install -y --no-install-recommends \
    apt-utils \
    curl \
    gosu \
    openssh-server \
    python3 \
    python-is-python3 \
    python3-pip \
    ssh \
    tmux \
    && rm -rf /var/lib/apt/lists/* \
# verify that the binary works
    && gosu nobody true

5-3. システムタイムゾーンの設定(この記事では’Asia/Seoul’で進行)

1
2
3
# Set up time zone
ARG TZ="Asia/Seoul"  # If necessary, replace it with a value that works for you.
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

5-4. リモート接続のためのSSHサーバー設定

セキュリティのためにSSHリモート接続時にrootアカウントログインができないように設定します。

1
2
3
4
# Set up SSH server
RUN mkdir /var/run/sshd
RUN echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
    echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config

SSH接続時に使用する’remote’という名前のnon-rootユーザーを作成します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create remote user (password can be passed to --build-arg at build time)
#
# This default password is very weak. Make sure to change it to your own unique
# password string!
#
# This Dockerfile assumes that the built image will only be used by yourself or
# a small group of trusted insiders, and if you need to distribute the image
# without exposing sensitive information, using --build-arg is dangerous.
# See the official Docker documentation.
ARG USER_NAME="remote"
ARG USER_PASSWORD="000000"
ARG HOME_DIR="/home/$USER_NAME"
RUN useradd --create-home --home-dir $HOME_DIR --shell /bin/bash $USER_NAME \
    && echo "$USER_NAME:$USER_PASSWORD" | chpasswd

このDockerfileを利用してDockerイメージをビルドする際に別途オプションを指定しない場合、’remote’ユーザーのアカウントパスワードの初期値は000000です。これはセキュリティ上非常に脆弱なので、Dockerイメージビルド時に--build-argオプションを利用してアカウントログインパスワードを別途指定するか、またはコンテナを初めて実行した後すぐに設定を変更するようにしましょう。セキュリティのためには、SSH接続時にパスワードログインを無効化し、別途のキーファイルを通じてのみログインが可能なように後で設定することが望ましく、Yubikeyなどのハードウェアキーまで活用すれば理想的です。 SSHサーバー構成については、このシリーズの次回で多少扱う予定ですが、より詳しく知りたい場合は次のリストにある文書を参考にするとよいでしょう。

またこのDockerfileはビルドしたイメージを個人または信頼できる少数の内部者のみが使用することを想定しており、もしビルドしたイメージを外部に配布する必要がある場合は、--build-argを通じたパスワード設定は危険なので、他の方法を使用する必要があります。この文書を参考にしてください。

5-5. setuptools、pipのインストールとPATH環境変数の登録

1
2
3
4
5
6
7
8
# Switch to remote user
ENV USER_NAME="$USER_NAME"
USER $USER_NAME
WORKDIR $HOME_DIR

# Install pip and ml/dl related packages
RUN python3 -m pip install -U setuptools pip
ENV PATH="$HOME_DIR/.local/bin:$PATH"

5-6. 開発環境で使用する機械学習およびディープラーニングパッケージのインストール

1
2
3
4
RUN python3 -m pip install -U \
        jupyterlab numpy scipy pandas matplotlib seaborn[stats] scikit-learn tqdm \
    && python3 -m pip install -U torch torchvision torchaudio \
        --index-url https://download.pytorch.org/whl/cu124

Cupy、cuDF、cuML、そしてDALIを使用する場合は、次の内容もDockerfileに追加します。

1
2
3
RUN python3 -m pip install -U cupy-cuda12x \
    && python3 -m pip install -U --extra-index-url=https://pypi.nvidia.com \
        cudf-cu12==24.8.* cuml-cu12==24.8.* nvidia-dali-cuda120

5-7. 作業スペースとして使用するディレクトリの作成

1
2
3
# Create a workspace directory to locate jupyter notebooks and .py files
ENV WORK_DIR="$HOME_DIR/workspace"
RUN mkdir -p $WORK_DIR

5-8. ポートの開放とコンテナ起動時に実行するENTRYPOINTの設定

SSHとJupyter Lab接続のために22、8888ポートを開放します。
またコンテナ起動時にSSHデーモンを自動実行するためにはルート権限が必要なので、次の方法を使用します。

  1. コンテナ起動時にrootアカウントでログインされた状態
  2. コンテナ起動直後に/entrypoint.shスクリプトを実行
  3. 該当スクリプトでSSHサービスを開始した後、gosuを使用してremoteアカウントに切り替え
  4. コンテナ実行時に別途指定したコマンドがなければ、デフォルト値としてJupyter Labをremoteアカウント(non-root権限)で実行

一般的にDockerやPodmanコンテナ内でのsudoまたはsuの使用は推奨されず、ルート権限が必要な場合はここで説明しているように、まずrootアカウントでコンテナを起動し、ルート権限が必要な作業を実行した後にgosuを使用してnon-rootユーザーに切り替えることが良いとされています。このようにする理由は以下の資料に詳しく説明されていますので、必要な場合は参考にすると役立つでしょう。

まずDockerfileの最後の部分に次の内容を入力します。

1
2
3
4
5
6
7
8
9
# Expose SSH and Jupyter Lab ports
EXPOSE 22 8888

# Switch to root
USER root

# Copy the entry point script and grant permission to run it
COPY --chmod=755 entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

次に、作成したDockerfileと同じパスにentrypoint.shという名前でスクリプトファイルを作成し、内容は以下のように作成します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
set -e

# Run SSH daemon in the background
service ssh start

# Move to the workspace directory and run Jupyter Lab
cd "$WORK_DIR"
if [ $# -gt 0 ];then
    #su ${USER_NAME} -c "exec $@"
    exec gosu ${USER_NAME} $@
else
    #su ${USER_NAME} -c "exec jupyter lab --no-browser --autoreload --ip=0.0.0.0 --notebook-dir="${WORK_DIR}""
    exec gosu ${USER_NAME} jupyter lab --no-browser --autoreload --ip=0.0.0.0 --notebook-dir="${WORK_DIR}"
fi

6. Dockerイメージのビルドとコンテナの実行

6-1. イメージビルド

Dockerfileが位置するディレクトリでターミナルを開き、以下のコマンドを実行します。

1
2
docker build -t dl-env:cuda12.4.1-cudnn9.1.0-ubuntu22.04 -f ./Dockerfile . \
--build-arg USER_PASSWORD=<password>

<password>の部分にSSH接続時に使用するログインパスワードを入力すれば良いです。

6-2. サンプルワークロードの実行

ビルドを完了したら、使い捨てコンテナを実行して正常に動作するか確認します。

Podmanの場合は次のコマンドを実行します。

1
2
3
podman run -itd --rm --name test-container --device nvidia.com/gpu=all \
--security-opt=label=disable -p 22:22 -p 88:8888 \
dl-env:cuda12.4.1-cudnn9.1.0-ubuntu22.04

Dockerの場合は次のコマンドを実行します。

1
2
3
docker run -itd --rm --name test-container \
--gpus all -p 22:22 -p 88:8888 \
dl-env:cuda12.4.1-cudnn9.1.0-ubuntu22.04

ターミナルで上記のコマンドを入力すると、先ほどビルドしたdl-env:cuda12.4.1-cudnn9.1.0-ubuntu22.04イメージからtest-containerという名前のコンテナを実行した後、ホストシステムの22番ポートと該当コンテナの22番ポート、ホストシステムの88番ポートとコンテナの8888番ポートをそれぞれ接続します。前のステップでDockerイメージが正常にビルドされ、コンテナが問題なく起動されていれば、test-containerコンテナ内でJupyterLabがデフォルト値のhttp:127.0.0.1:8888アドレスで実行中のはずです。したがって、Docker Engineが動作しているホストシステムでブラウザを開き、http://127.0.0.1:88にアクセスすると、コンテナ内部のhttp://127.0.0.1:8888アドレスに接続され、以下のような画面が表示されるはずです。

JupyterLab Interface Screenshot

ホストシステムでターミナルを開き、ssh [email protected]コマンドを実行して、コンテナ内部で実行中のUbuntuシステムのremoteアカウントにリモートログインしてみましょう。
初めてログインする際は、接続先の暗号キーに関する情報がなく認証ができないという警告が表示され、続行するかどうか尋ねられますが、「yes」と入力して続行します。
その後、ログインのためにパスワード(イメージビルド時に別途変更していなければデフォルト値の「000000」のはずです)を入力します。

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
$ ssh [email protected]
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ED25519 key fingerprint is {フィンガープリント(各キーごとに異なる固有の値を持つ)}.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '127.0.0.1' (ED25519) to the list of known hosts.
[email protected]'s password: 
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 6.12.11-200.fc41.x86_64 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

おおよそ上記のように出力されれば、SSHを通じたリモートログインに成功しています。接続を終了する際はexitコマンドを入力します。

6-3. (オプション)Docker Hubにプッシュする

前述の過程を経て作成した開発環境イメージを必要な時にいつでもPullして活用するには、ビルドしたイメージをDocker Hubにプッシュしておくと良いでしょう。

Docker Hubに自分のイメージをプッシュするには自分のDockerアカウントが必要なので、まだない場合はhttps://app.docker.com/signupで会員登録を先に完了させてください。

6-3-1. Docker Hubにログイン

Podmanの場合
1
podman login docker.io
Dockerの場合
1
docker login

6-3-2. イメージタグの指定

<dockerhub_username><repository_name>、(選択):TAG部分には自分に該当する内容を入力すれば良いです。
例:「yunseokim」、「dl-env」、「rapids-cuda12.4.1-cudnn9.1.0-ubuntu22.04」

Podmanの場合
1
podman tag IMAGE_ID docker.io/<dockerhub_username>/<repository_name>[:TAG]
Dockerの場合
1
docker tag IMAGE_ID <dockerhub_username>/<repository_name>[:TAG]

6-3-3. イメージのプッシュ

最後に、以下のコマンドを実行して該当イメージをDocker Hubにプッシュします。

Podmanの場合
1
podman push docker.io/<dockerhub_username>/<repository_name>[:TAG]
Dockerの場合
1
docker push <dockerhub_username>/<repository_name>[:TAG]

https://hub.docker.com/で以下のように正常にプッシュされたことを確認できます。
Docker Hub Screenshot

前述の過程を経て完成したイメージはDocker Hubのyunseokim/dl-env公開リポジトリに公開しており、誰でも自由に使用できます。

イメージをPullするためには、先ほどプッシュする際に使用したコマンドのpush部分をpullに変えて実行すれば良いです。

This post is licensed under CC BY-NC 4.0 by the author.