Docker on GPU インスタンス(EC2)で CUDA コンテナの実行環境を作って Hello World を実行してみた

2023.06.07

CUDA の動作確認環境として、CUDA のコンテナを利用した実行環境を用意する機会がありました。Hello World を実行するまでの過程を例に GPU インスタンス上で CUDA コンテナの実行環境の構築方法を紹介します。

検証環境

検証環境の構成です。

Inventory icons created by Freepik - Flaticon

Name Version
Instance Type g4dn.xlarge
AMI Name Deep Learning Base AMI (Amazon Linux 2) Version 58.2
AMI I ami-0921844ed281cc082
OS Amazon Linux 2
Docker 20.10.23
Docker Image nvidia/cuda:10.2-devel-centos7
CUDA 10.2.89

CUDA コンテナ実行環境の準備

NVIDIA ドライバーのセットアップに時間がかかるため、素の GPU インスタンスの AMI を使用するのではなく、Deep Learning AMI を使用することをオススメします。また、CUDA をインストール済みの AMI(Deep Learning GPU CUDA)も提供されていますが、本検証では、CUDA をコンテナイメージ側で管理するため、ホストにはインストールしないことにします。

  • AMI Name: Deep Learning Base AMI (Amazon Linux 2) Version 58.2
  • Instance Type: g4dn.xlarge

GPU インスタンスは比較的高額なため、スポットインスタンスを使用して検証を行います。検証には最新の g5.xlarge インスタンスタイプを使用したかったのですが、キャパシティ不足のため起動できませんでした。そのため、スポットインスタンスでも起動可能だった g4dn.xlarge を使用しています。

Docker の準備

Deep Learning AMI はすでに Docker がインストール済みでした。現時点での Docker Engine の最新バージョンは24.0.2でした。最新ではありませんがそのまま利用します。

Docker Engine 24.0 release notes | Docker Documentation

$ docker version
Client:
 Version:           20.10.23
 API version:       1.41
 Go version:        go1.18.9
 Git commit:        7155243
 Built:             Tue Apr 11 22:56:36 2023
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.23
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.18.9
  Git commit:       6051f14
  Built:            Tue Apr 11 22:57:17 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.19
  GitCommit:        1e1ea6e986c6c86565bc33d52e34b81b3e2bc71f
 runc:
  Version:          1.1.5
  GitCommit:        f19387a6bec4944c770f7668ab51c4348d9c2f38
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

CUDA コンテナイメージの準備

NVIDIA 謹製の CUDA イメージを利用します。CUDA のソースコードをコンパイルするためnvccコマンドが利用可能なdevelのイメージを使います。

今回は CUDA 10. 代 + CentOS 7 の環境をコンテナで動作するか検証が必要だったため、あえて古いバージョンのコンテナイメージを利用しています。

  • Docker Image: nvidia/cuda:10.2-devel-centos7

nvidia/cuda Tags | Docker Hub

CUDA コンテナの起動

コンテナ内でコンパイルしたバイナリファイルをローカル(GPU インスタンス)と共有できるように作業用のディレクトリをマウントします。

先にマウント用のディレクトリを作成しておきます。

mkdir $(pwd)/work

GPU を利用するコンテナ起動時のポイント--gpus=allフラグが必要です。

docker run \
  --gpus=all \
  -it \
  -v "$(pwd)"/work:/work \
  nvidia/cuda:10.2-devel-centos7 \
  bash

以下のメッセージが表示されていれば成功です。

==========
== CUDA ==
==========

CUDA Version 10.2

Container image Copyright (c) 2016-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

This container image and its contents are governed by the NVIDIA Deep Learning Container License.
By pulling and using the container, you accept the terms and conditions of this license:
https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license

A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience.

*************************
** DEPRECATION NOTICE! **
*************************
THIS IMAGE IS DEPRECATED and is scheduled for DELETION.
    https://gitlab.com/nvidia/container-images/cuda/blob/master/doc/support-policy.md

CUDA コンテナを使ってみる

CUDA コンテナにログインできました。コンテナ内であれば必要なコマンドが利用可能なはずです。Hello World コードをコンパイルして実行まで試してみます。

nvccコマンドが利用可能かチェックします。develのイメージはコンパイル作業に利用できることが確認できました。

# nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2019 NVIDIA Corporation
Built on Wed_Oct_23_19:24:38_PDT_2019
Cuda compilation tools, release 10.2, V10.2.89

Hello World コードは下記リンクを参考にしました。

__global__の指定があると GPU 上で実行する関数となります。

/work/hello.nc

#include <stdio.h>
__global__ void helloFromGPU(void)
{
    printf("Hello World From GPU!\n");
}
int main(void)
{
    printf("Hello World from CPU!\n");
    helloFromGPU<<<1,10>>>();
    cudaDeviceReset();
    return 0;
}

コンパイルします。

nvcc hello.cu -o hello

バイナリが生成されました。CUDA コンテナを利用してコンパイルまでは正常に行えることを確認できました。

# ll
total 640
-rwxr-xr-x 1 root root 650000 Jun  7 05:59 hello
-rw-r--r-- 1 root root    222 Jun  6 08:04 hello.cu

バイナリを実行してみると GPU 上で実行された関数の結果(Hello World From GPU!のメッセージ)を確認できました。

# ./hello
Hello World from CPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!
Hello World From GPU!

GPU からのメッセージが返ってこないケース

コンテナ起動時に--gpus=allフラグを付け忘れたとしましょう。

docker run \
  -it \
  -v "$(pwd)"/work:/work \
  nvidia/cuda:10.2-devel-centos7 \
  bash

すると、コンテナから GPU を利用できずに CPU 上で実行した関数の結果しか返ってきません。

# ./hello
Hello World from CPU!

コンテナが NVIDIA GPU リソースへアクセスするためには--gpusフラグが必須ですので注意しましょう。

Docker-docs-ja 20.10 ドキュメント

おわりに

CUDA を扱ったことがなかったので単純なプログラムで動作環境の検証したかったので Hello World を試してみました。そんな実行環境の作り方紹介でした。はじめて GPU コンテナを使ってみたのですけどコンテナだと CUDA のバージョン指定が楽で使い勝手が良いですね。

参考