Posted at

Docker コンテナで NVIDIA GPU によるハードウェアエンコードを動かす


目的

NVIDIA GPU による高速なハードウェアエンコードを Docker コンテナで動かしたい。


前提条件


  • Docker コンテナで NVIDIA GPU を使用するには、ホストマシンに NVIDIA GPU が搭載されている必要がある。

  • 今回はホストマシンとして NVIDIA Tesla M60 を搭載した Amazon EC2 G3 インスタンス を使用した。

  • ホスト OS は Amazon Linux 2 を選択、NVIDIA GPU のドライバは執筆当時の最新版を使用した。


ホストマシンの準備

まずは Docker コンテナを動作させるホストマシンとなる Amazon EC2 インスタンスを用意する。


  1. Amazon EC2 G3 インスタンスを起動


    • AMI (Amazon Machine Image) は Amazon Linux 2 AMI ami-0a2de1c3b415889d2 を選択

    • インスタンスタイプは G3 インスタンスの中では比較的安価な g3.4xlargeg3s.xlarge を推奨

    • EBS ルートボリュームは 8 GB だと空き容量が不足する可能性があるため注意



  2. 起動した EC2 インスタンスに SSH 接続


  3. 最初にタイムゾーンを設定

    sudo timedatectl set-timezone Asia/Tokyo
    



  4. Docker のインストール

    Amazon Linux 2 では Extras Library を使用することで Docker を簡単にインストールすることができる。

    sudo amazon-linux-extras install docker
    



  5. NVIDIA Graphics Driver のインストール

    最新のドライバは https://www.nvidia.com/Download/index.aspx で Tesla M60 を検索することで取得可能。

    なお、NVIDIA Graphics Driver の使用には NVIDIA Software License Agreement への同意が必要である。

    sudo yum install gcc kernel-devel-$(uname -r)
    
    wget http://us.download.nvidia.com/tesla/410.72/NVIDIA-Linux-x86_64-410.72.run
    sudo bash NVIDIA-Linux-x86_64-410.72.run --ui=none --no-questions --accept-license



  6. nvidia-docker2 のインストール

    nvidia-docker2 は Docker コンテナで NVIDIA GPU を使用するために必要なランタイムである。

    distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
    
    curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo
    sudo yum install nvidia-docker2



  7. Docker コンテナの起動確認

    先にインストールしたランタイムを指定して nvidia/cuda コンテナを起動する。

    コンテナ内部で実行する nvidia-smi は NVIDIA GPU の情報を取得するためのコマンドである。

    sudo systemctl start docker
    
    sudo docker run --runtime=nvidia nvidia/cuda nvidia-smi

    以下のように NVIDIA GPU の情報が表示されれば成功。

    +-----------------------------------------------------------------------------+
    
    | NVIDIA-SMI 410.72 Driver Version: 410.72 CUDA Version: 10.0 |
    |-------------------------------+----------------------+----------------------+
    | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
    | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
    |===============================+======================+======================|
    | 0 Tesla M60 Off | 00000000:00:1E.0 Off | N/A |
    | N/A 29C P0 40W / 150W | 0MiB / 7618MiB | 97% Default |
    +-------------------------------+----------------------+----------------------+

    +-----------------------------------------------------------------------------+
    | Processes: GPU Memory |
    | GPU PID Type Process name Usage |
    |=============================================================================|
    | No running processes found |
    +-----------------------------------------------------------------------------+




FFmpeg のビルド

次に Docker コンテナで動かす FFmpeg のビルドを行う。ビルドも Docker コンテナ内部で行う。



  1. 作業用コンテナの起動

    nvidia/cuda:10.0-devel-centos7 コンテナを起動しシェルに接続する。

    sudo docker run --runtime=nvidia -it nvidia/cuda:10.0-devel-centos7 /bin/bash
    



  2. 必要なパッケージのインストール

    yum install autoconf bzip2 git make
    



  3. nasm のインストール

    curl -fsSLO https://www.nasm.us/pub/nasm/releasebuilds/2.14/nasm-2.14.tar.bz2
    
    tar xjvf nasm-2.14.tar.bz2
    cd nasm-2.14
    ./autogen.sh
    ./configure
    make -j$(nproc)
    make install



  4. ffnvcodec のインストール

    FFmpeg 4.0 以降 NVIDIA Video Codec SDK のヘッダファイルが FFmpeg 本体に付属されなくなった。

    git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers
    
    cd nv-codec-headers
    make install



  5. FFmpeg 本体のビルド

    export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
    
    curl -fsSLO https://ffmpeg.org/releases/ffmpeg-4.1.tar.bz2
    tar xjvf ffmpeg-4.1.tar.bz2
    cd ffmpeg-4.1
    ./configure --enable-nvenc
    make -j$(nproc)
    make install



ハードウェアエンコードの実行

ビルドした FFmpeg を使ってハードウェアエンコードを行う。エンコードはより軽量な新しいコンテナで行う。



  1. エンコード実行用コンテナの起動

    環境変数 NVIDIA_DRIVER_CAPABILITIES=video を設定することで、ホストマシンにインストールされた NVIDIA GPU ドライバを構成するライブラリのうち NVENC に関連するものをコンテナ内部から使用できるようになる。

    nvidia/cuda:10.0-runtime イメージは nvidia/cuda:10.0-devel に比べて含まれるライブラリの種類が少ない代わりにイメージサイズが小さい。ビルド済みの CUDA アプリケーションを実行する場合はこちらを使用する。

    sudo docker run --runtime=nvidia -it -e 'NVIDIA_DRIVER_CAPABILITIES=video' nvidia/cuda:10.0-runtime-centos7 /bin/bash
    



  2. 前節でビルドした FFmpeg 本体 /usr/local/bin/ffmpeg を新しいコンテナにもコピーする。

    いったんコンテナから exit してから docker cp コマンドによりコンテナ内部のファイルを操作できる。

    sudo docker cp <source-container-id>:/usr/local/bin/ffmpeg <target-container-id>:/usr/local/bin/ffmpeg
    



  3. FFmpeg コマンドの実行

    再び新しいコンテナに docker attach してから、適当な動画ファイル input.mp4 を用意する。

    これに対し NVENC によるハードウェアエンコードを行うには、以下の ffmpeg コマンドを実行する。

    /usr/local/bin/ffmpeg -i input.mp4 -vcodec h264_nvenc -acodec aac output.mp4
    



Docker コンテナイメージの作成

NVENC に対応した FFmpeg が最初から使用可能な新しい Docker コンテナイメージ example/nvenc を作成する。完成したコンテナイメージを Docker Hub に公開すれば、誰でも docker pull できるようになる。



  1. Dockerfile の作成

    Dockerfile はコンテナイメージのレシピを記述したものである。

    以下の内容を Dockerfile という名前で作成する。Dockerfile の記法についてはここでは割愛する。

    FROM nvidia/cuda:10.0-devel-centos7 as builder

    ENV NASM_VERSION 2.14
    ENV NVCODEC_VERSION 8.2.15.6
    ENV FFMPEG_VERSION 4.1

    RUN yum install -y autoconf bzip2 git make

    RUN curl -fsSLO https://www.nasm.us/pub/nasm/releasebuilds/$NASM_VERSION/nasm-$NASM_VERSION.tar.bz2 \
    && tar -xjf nasm-$NASM_VERSION.tar.bz2 \
    && cd nasm-$NASM_VERSION \
    && ./autogen.sh \
    && ./configure \
    && make -j$(nproc) \
    && make install

    RUN git clone -b n$NVCODEC_VERSION --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers \
    && cd nv-codec-headers \
    && make install

    ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
    RUN curl -fsSLO https://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2 \
    && tar -xjf ffmpeg-$FFMPEG_VERSION.tar.bz2 \
    && cd ffmpeg-$FFMPEG_VERSION \
    && ./configure --enable-nvenc \
    && make -j$(nproc) \
    && make install

    FROM nvidia/cuda:10.0-runtime-centos7

    ENV NVIDIA_VISIBLE_DEVICES all
    ENV NVIDIA_DRIVER_CAPABILITIES compute,video,utility

    COPY --from=builder /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
    COPY --from=builder /usr/local/bin/ffprobe /usr/local/bin/ffprobe




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

    Dockerfile と同じディレクトリで以下のコマンドを実行するとコンテナイメージのビルドが開始される。

    sudo docker build -t example/nvenc .
    



  3. Docker コンテナの起動確認

    作成した example/nvenc コンテナを起動し、ffmpeg コマンドが使用可能であることを確認する。

    sudo docker run --runtime=nvidia -it example/nvenc /bin/bash
    
    /usr/local/bin/ffmpeg



  4. コンテナイメージを Docker Hub に公開

    あらかじめ Docker Hub のアカウントを作成し Create Repository からリポジトリを作成しておく必要がある。

    sudo docker login
    
    sudo docker push example/nvenc


なお、今回作成した Docker コンテナイメージは https://hub.docker.com/r/clerk67/nvenc/ にて公開している。