すべてのプロダクト
Search
ドキュメントセンター

Elastic High Performance Computing:標準 SLURM クラスタを使用したコンテナジョブのスケジューリング

最終更新日:Jun 11, 2025

このトピックでは、SLURM クラスタでコンテナ化されたタスクを実行する必要があるユーザーのために、クラスタ作成からジョブスケジューリングまでのプロセス全体について説明します。

ステップ 1:クラスタを作成する

  1. 標準 SLURM クラスタを作成する

    次の表は、このトピックで使用されているクラスタ構成の例を示しています。必要に応じて他のパラメータを構成します。

    パラメータ

    構成

    クラスタ構成

    シリーズ

    Standard Edition

    デプロイメントモード

    パブリッククラウドクラスタ

    クラスタタイプ

    SLURM

    管理ノード

    • インスタンスタイプ: 4 vCPU と 32 GiB のメモリを搭載した ecs.r7.xlarge インスタンスタイプを使用します。

    • イメージ: centos_7_6_x64_20G_alibase_20211130.vhd

    計算ノードとキュー

    キュー計算ノード

    初期ノード数: 1

    ノード間相互接続

    VPC ネットワーク

    インスタンスタイプグループ

    • インスタンスタイプ: 56 vCPU と 346 GiB のメモリを搭載した ecs.gn7i-c56g1.14xlarge インスタンスタイプを使用します。

      重要

      GPU をサポートするインスタンスタイプを使用する必要があります。詳細については、「インスタンスファミリ」をご参照ください。

    • イメージ: centos_7_6_x64_20G_alibase_20211130.vhd

    共有ファイルストレージ

    /home クラスタマウントディレクトリ

    デフォルトでは、コントロールプレーンノードの /home ディレクトリと /opt ディレクトリは、共有ストレージディレクトリとしてファイルシステムにマウントされます。

    /opt クラスタマウントディレクトリ

    ソフトウェアとサービスコンポーネント

    インストールするソフトウェア

    docker を選択します。

    インストール可能なサービスコンポーネント

    ログオンノード:

    • インスタンスタイプ: 4 vCPU と 32 GiB のメモリを搭載した ecs.r7.xlarge インスタンスタイプを使用します。

    • イメージ: centos_7_6_x64_20G_alibase_20211130.vhd。

  2. クラスタユーザーを作成する

    このトピックでは、usertest ユーザーを例として使用します。

ステップ 2:基本的なソフトウェア環境をセットアップする

  1. 計算ノードに EIP をアタッチします。

  2. 計算ノードに接続する

  3. CUDA をダウンロードしてインストールします。

    1. CUDA インストールパッケージをダウンロードします。

      cd /opt
      wget https://developer.download.nvidia.com/compute/cuda/12.4.1/local_installers/cuda_12.4.1_550.54.15_linux.run
    2. CUDA をインストールします。

      yum install -y git
      sh /opt/cuda_12.4.1_550.54.15_linux.run

      次の図が表示されたら、CUDA がインストールされています。

      image

    3. 環境変数を構成します。

      echo 'export PATH=/usr/local/cuda-12.4/bin:$PATH' >> ~/.bashrc
      echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
      
      source ~/.bashrc
    4. NVIDIA CUDA ツールキットと GPU ドライバのインストールステータスとバージョン情報を表示します。

      # NVIDIA CUDA コンパイラドライバのバージョン情報
      nvcc --version
      
      # GPU の詳細なステータス情報
      nvidia-smi

      次の図が表示されたら、CUDA と GPU ドライバは正常に動作しています。

      image

  4. NVIDIA Container Toolkit をインストールして構成します。

    distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
    curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
    sudo yum install -y nvidia-container-toolkit
    sudo systemctl restart docker
  5. Singularity をダウンロードしてインストールします。

    Singularity は、ユーザー環境を変更せずにコンテナを実行できるコンテナ化ツールです。HPC 環境でよく使用されます。

    cd /opt
    wget https://public-ehs.oss-cn-hangzhou.aliyuncs.com/softwares/packages/CentOS_7.2_64/singularity-3.8.3-1.el7.x86_64.rpm
    yum install -y /opt/singularity-3.8.3-1.el7.x86_64.rpm
  6. ジョブ依存データを作成します。

    1. PyTorch コンテナイメージをプルします。以下のコマンドについては何も変更する必要はありません。

      docker pull ac2-registry.cn-hangzhou.cr.aliyuncs.com/ac2/pytorch:2.4.0-cuda12.1.1-py310-alinux3.2104
    2. usertest ユーザーを使用して、main.py ファイルを作成します。

      vim /home/usertest/main.py
    3. main.py ファイルのスクリプト内容は次のとおりです。

      # -*- coding: utf-8 -*-
      
      import torch
      import torchvision
      import torchvision.transforms as transforms
      from torch import nn
      from torch.utils.data import DataLoader
      from torch.optim import SGD
      
      class SimpleNet(nn.Module):
          def __init__(self):
              super(SimpleNet, self).__init__()
              self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
              self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
              self.fc1 = nn.Linear(64 * 8 * 8, 128)
              self.fc2 = nn.Linear(128, 10)
              self.pool = nn.MaxPool2d(2, 2)
              self.relu = nn.ReLU()
              self.dropout = nn.Dropout(0.5)
      
          def forward(self, x):
              x = self.pool(self.relu(self.conv1(x)))
              x = self.pool(self.relu(self.conv2(x)))
              x = x.view(-1, 64 * 8 * 8)
              x = self.relu(self.fc1(x))
              x = self.dropout(x)
              x = self.fc2(x)
              return x
      
      device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
      print(f"Using device: {device}")
      
      transform = transforms.Compose([
          transforms.ToTensor(),
          transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
      ])
      
      train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
      test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
      
      train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
      test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)
      
      model = SimpleNet().to(device)
      criterion = nn.CrossEntropyLoss()
      optimizer = SGD(model.parameters(), lr=0.001, momentum=0.9)
      
      num_epochs = 10
      for epoch in range(num_epochs):
          model.train()
          running_loss = 0.0
          for i, data in enumerate(train_loader, 0):
              inputs, labels = data
              inputs, labels = inputs.to(device), labels.to(device)
      
              optimizer.zero_grad()
              outputs = model(inputs)
              loss = criterion(outputs, labels)
              loss.backward()
              optimizer.step()
      
              running_loss += loss.item()
              if i % 100 == 99: 
                  print(f"[{epoch + 1}, {i + 1}] loss: {running_loss / 100:.3f}")
                  running_loss = 0.0
      
          model.eval()
          correct = 0
          total = 0
          with torch.no_grad():
              for data in test_loader:
                  images, labels = data
                  images, labels = images.to(device), labels.to(device)
                  outputs = model(images)
                  _, predicted = torch.max(outputs.data, 1)
                  total += labels.size(0)
                  correct += (predicted == labels).sum().item()
      
          print(f"Accuracy on test set: {100 * correct / total:.2f}%")
      
      print("Training finished.")

ステップ 3:ジョブをスケジュールする

Docker ジョブをスケジュールする

展開して Docker スクリプトのサンプルとその説明を表示する

#!/bin/bash
#SBATCH --job-name=your_job_name
#SBATCH --output=your_output
#SBATCH --nodes=1
#SBATCH --gres=gpu:1
#SBATCH --ntasks=2

# args
iamges="your_image"
run_cmd="your_command"
work_dir="your_workdir"
share_dir="/ehpcdata/:/mnt"
devices="device=$CUDA_VISIBLE_DEVICES"

# cleanup docker handle
function cleanup {
    echo "Caught signal, stopping Docker container: " $SLURM_JOB_NAME
    docker ps -q --filter label=$SLURM_JOB_NAME | xargs -r docker stop
    docker ps -qa --filter label=$SLURM_JOB_NAME | xargs -r docker rm
}
trap cleanup SIGINT SIGTERM

# start docker
docker pull $iamges
srun docker run \
  --label $SLURM_JOB_NAME \
  --rm --net=host \
  --gpus '"'$devices'"' \
  -v $share_dir \
  $iamges \
  /bin/bash -c "$run_cmd" &

# wait to complete
wait
cleanup
説明

スクリプトの説明:

  1. Docker ランタイムリソースは、Slurm スケジューラによって割り当てられます。

  2. GPU ID(CUDA_VISIBLE_DEVICES)は Slurm によって割り当てられ、--gpus パラメータを介して Docker ランタイムに渡されます。このメソッドは、GPU の物理的な分離と GPU ID インデックスマッピング(nvidia-smi は 0 から開始)をサポートしています。

  3. root ユーザーのみが Docker イメージ内でコマンドを実行できます。Slurm スケジューリングパラメータの環境変数は、コンテナに自動的に渡されません。Docker run のコマンドを介して明示的に渡す必要があります。これは Singularity とは異なります。

  4. ジョブに対して scancel を使用しても Docker コンテナは終了しないため、ジョブが完了または終了したときに Slurm ジョブによって開始されたイメージが自動的に停止するように信号メカニズムが設計されています。このメカニズムは、ジョブ名をイメージラベルとして使用し、ジョブが終了すると、ジョブ名でフィルタリングして対応するイメージを停止します。

  5. ジョブスクリプトには、次の部分が含まれています。

    1. リソース要件、ジョブ名(必須)、入出力情報など、Slurm スケジューリングパラメータ。

    2. 実行コマンドやイメージアドレス(リポジトリまたはローカル)など、環境変数の設定。これらは Docker 起動パラメータとして使用されます。

    3. クリーンアップハンドル。これは固定されており、変更する必要はありません。

    4. Docker 起動コマンド。

    5. 終了(完了を待つ)。

  6. Docker run コマンド:

    1. --name: コンテナ名。Slurm ジョブ名と同じです。

    2. --gpus: コンテナが使用する GPU ID。Slurm スケジューラによって割り当てられます。

    3. -v: コンテナ共有ディレクトリを指定します。コードやモデルを含む作業ディレクトリをコンテナにマッピングすることをお勧めします。注: コンテナは内部で root 権限を使用し、デフォルトで作成されるファイルは root に属します。

    4. イメージとコマンド: イメージ名と実行コマンドを設定します。

E-HPC ポータルからジョブを送信する

  1. E-HPC ポータルにログオンする

  2. NCCL ジョブを送信します。

    1. 上部のナビゲーションバーで、[タスク管理] を選択し、ページ上部の [送信者] をクリックし、[ジョブの作成] ページで、[ノード数]1 に、[タスク数 (ノードごと)]2 に、[GPU 数]1 に設定します。

    2. ジョブスクリプトの内容は次のとおりです。

      docker images コマンドを使用してイメージ名とバージョン番号を取得し、3 行目の your_image を置き換えます。

      #!/bin/bash
      
      image="your_image"
      run_cmd="python main.py"
      share_dir="/home/usertest/:/root"
      
      # cleanup docker handle
      function cleanup {
          echo "Caught signal, stopping Docker container: " $SLURM_JOB_NAME
          docker ps -q --filter label=$SLURM_JOB_NAME | xargs -r docker stop
          docker ps -qa --filter label=$SLURM_JOB_NAME | xargs -r docker rm
      }
      trap cleanup SIGINT SIGTERM
      cleanup
      
      # start docker
      # docker pull $image
      
      docker run \
        --label $SLURM_JOB_NAME \
        --gpus "device=0" \
        -v $share_dir \
        $image \
        /bin/bash -c "$run_cmd" &
      
      # wait to complete
      wait
      cleanup
  3. ジョブをクエリします。

    [タスク管理] ページに移動して、ジョブステータスとアクションを含むジョブリストを表示します。詳細については、「ジョブのクエリ」をご参照ください。

コマンドラインからジョブを送信する

  1. コマンドラインからジョブを送信します。詳細については、「SLURM」をご参照ください。

  2. ジョブスクリプトの内容は次のとおりです。

    docker images コマンドを使用してイメージ名とバージョン番号を取得し、13 行目の your_image を置き換えます。

    #!/bin/bash
    
    #SBATCH --job-name=tf_sample_job
    #SBATCH --nodes=1
    #SBATCH --nt
    #SBATCH --gpus-per-task=1
    #SBATCH --time=01:00:00
    #SBATCH --partition=comp
    #SBATCH --output=tf_sample_job_%j.out
    #SBATCH --error=tf_sample_job_%j.err
    
    # Define variables
    image="your_image"
    run_cmd="python main.py"
    share_dir="/home/usertest/:/root"
    
    # Clean up Docker processes
    function cleanup {
        echo "Caught signal, stopping Docker container: " $SLURM_JOB_NAME
        docker ps -q --filter label=$SLURM_JOB_NAME | xargs -r docker stop
        docker ps -qa --filter label=$SLURM_JOB_NAME | xargs -r docker rm
    }
    trap cleanup SIGINT SIGTERM
    cleanup
    
    # Start Docker container
    docker pull $image
    docker run \
      --label $SLURM_JOB_NAME \
      --gpus "device=$CUDA_VISIBLE_DEVICES" \
      -v $share_dir \
      $image \
      /bin/bash -c "$run_cmd" &
    
    # Wait for task completion
    wait
    cleanup
  3. Slurm を介してジョブをクエリします。

    squeue コマンドを使用して、現在実行中またはキューに入っているジョブリストをクエリします。

    squeue

    sacct コマンドを使用して、完了したジョブを含むジョブ履歴をクエリします。

    sacct

Singularity ジョブをスケジュールする

  1. Docker2Singularity。

    Singularity イメージ管理を簡素化するために、クラウド内の Docker イメージリポジトリを再利用できます。次の手順に従って、イメージ形式を変換します。

    # 方法 1: ローカル docker パッケージを介して sif イメージに変換する
    [root@compute006 opt]# docker images
    REPOSITORY                                               TAG               IMAGE ID       CREATED       SIZE
    ac2-registry.cn-hangzhou.cr.aliyuncs.com/ac2/pytorch   2.4.0-cuda12.1.1-py310-alinux3.2104   19301a07d7fd   4 months ago   6.33GB
    [root@compute006 opt]# docker save -o docker.tar 19301a07d7fd 
    [root@compute006 opt]# ll docker.tar 
    -rw------- 1 root root 3021202432 Feb  12 15:03 docker.tar
    [root@compute006 opt]# singularity build pytorch.sif docker-archive:///opt/docker.tar
    
    
    # 方法 2: docker コンテナリポジトリを介して sif イメージをビルドする
    singularity build xx.sif docker://ac2-registry.cn-hangzhou.cr.aliyuncs.com/ac2/pytorch:2.4.0-cuda12.1.1-py310-alinux3.2104

展開して Singularity スクリプトとその説明を表示する

#!/bin/bash
#SBATCH --job-name=my_job_name
#SBATCH --output=output.log
#SBATCH --nodes=1
#SBATCH --gres=gpu:1
#SBATCH --ntasks=2

# args
image=your_image.sif
run_cmd="your cmd"
share_dir="/ehpcdata/:/mnt"

singularity exec --nv --bind $share_dir $image $run_cmd
説明

スクリプトの説明:

  1. Singularity は、root ユーザーと非 root ユーザーの両方をサポートしています。コンテナの起動時にユーザーコンテキストは変更されません。ユーザー環境は、イメージの内外で同じです。これは Docker イメージとは異なり、Slurm 環境変数をイメージの内部にアクティブに渡す必要があります。

  2. GPU ID は物理的な分離をサポートしていません。アプリケーション自体は、スケジューラによって割り当てられた GPU ID(CUDA_VISIBLE_DEVICES)を介して GPU 選択を完了する必要があります。例: "CUDA_VISIBLE_DEVICES=0 ./deviceQuery"。

  3. ジョブスクリプトには、次の部分が含まれています。

    1. リソース要件、ジョブ名(必須)、入出力情報など、Slurm スケジューリングパラメータ。

    2. 実行コマンドやイメージアドレス(リポジトリまたはローカル)など、環境変数の設定。これらは Docker 起動パラメータとして使用されます。

    3. Singularity 起動コマンド。

  4. Singularity exec コマンド:

    1. --nv: NVIDIA GPU のサポートを有効にするために使用されます。

    2. --bind: コンテナ共有ディレクトリを指定します。コードやモデルを含む作業ディレクトリをコンテナにマッピングすることをお勧めします。注: コンテナは内部で root 権限を使用し、デフォルトで作成されるファイルは root に属します。

    3. イメージとコマンド: イメージ名と実行コマンドを設定します。

E-HPC ポータルからジョブを送信する

  1. E-HPC ポータルにログオンする

  2. NCCL ジョブを送信します。

    1. 上部のナビゲーションバーで、[タスク管理] を選択し、ページ上部の [送信者] をクリックし、[ジョブの作成] ページで、[ノード数]1 に、[タスク数 (ノードごと)]2 に、[GPU 数]1 に設定します。

    2. ジョブスクリプトの内容は次のとおりです。

      image=/opt/pytorch.sif
      run_cmd="python main.py"
      share_dir="/home/usertest/:/root"
      
      singularity exec --nv --bind $share_dir $image $run_cmd
  3. ジョブをクエリします。

    [タスク管理] ページに移動して、ジョブステータスとアクションを含むジョブリストを表示します。詳細については、「ジョブのクエリ」をご参照ください。

コマンドラインからジョブを送信する

  1. コマンドラインからジョブを送信します。詳細については、「SLURM」をご参照ください。

  2. ジョブスクリプトの内容は次のとおりです。

    #!/bin/bash
    #SBATCH --job-name=singularity_TF
    #SBATCH --output=output.log
    #SBATCH --nodes=1
    #SBATCH --gres=gpu:1
    #SBATCH --partition=container
    #SBATCH --ntasks=2
    
    
    image=/opt/pytorch.sif
    run_cmd="python main.py"
    share_dir="/home/usertest/:/root"
    
    singularity exec --nv --bind $share_dir $image $run_cmd
  3. Slurm を介してジョブをクエリします。

    squeue コマンドを使用して、現在実行中またはキューに入っているジョブリストをクエリします。

    squeue

    sacct コマンドを使用して、完了したジョブを含むジョブ履歴をクエリします。

    sacct