GitLabを使ってNVIDIA Dockerに自動デプロイ


機械学習とかGPUを使うプログラムを書いているとき,リモートのGPU搭載マシンで実行させたいときってないでしょうか...
ローカルPCにGPU積んでないとか,積んでるけどリモートのGPUが強力だからそっちで学習がっつり回したいとか,ファンうるさいからあっちでやってくれとか.

私は,GitレポジトリにあげてリモートでPullして実行をよくやります.が,CIツールを使って,もうちょっといい感じにできないかなと試してみましたので,そのときのメモ・手順をここに置きたいと思います.

構築イメージ

閉じた環境でやりたかったので,GitLabを利用しローカルにGitレポジトリマネージャー,CIサーバ,ランナーを構築します.そしてgit pushからのGPUマシンにデプロイをしていきます.

また,他の環境にも展開したいときサクッと行きたいので,Docker Composeを利用して構築します.
デプロイ先では,そこの環境を汚したくないので,ここもDockerを使います.Dockerコンテナ上でGPUを使うことになるので,NVIDIA Dockerで構築します.

こんなかんじ.

事前準備

  1. GitLab構築マシン(CIサーバー側)
  2. GPU搭載Linuxマシン(デプロイ先)

※ 両方にDocker Compose,2にはNVIDIA Dockerもインストールしてください.

1, 2は同一マシンでも問題ありません.
ただし,NVIDIA Dockerのサポート対象の関係上[参考],2がLinuxであることは必須です.

本稿ではDocker Compose,NVIDIA Dockerのインストール方法については割愛させていただきます.インストール方法については,公式ドキュメントなどをご参考ください.

GitLabを構築

まずGitLabをDocker Composeで構築します.
以下はYAMLの例です.initial_shared_runners_registration_tokenでランナー登録時に使用するトークンを与えています.(今回はテストのため非常に安直)

実行コマンド例)docker-compose -f gitlab.yml up -d

gitlab.yml

version: '2'

services:
  gitlab:
    image: 'gitlab/gitlab-ce:latest'
    container_name : gitlab
    restart: always
    hostname: 'mac-book.local'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://mac-book.local'
        gitlab_rails['time_zone'] = 'Asia/Tokyo'
        gitlab_rails['initial_shared_runners_registration_token'] = 'token-share'
    ports:
      - '80:80'
      # - '443:443'
      # - '22:22'
    volumes:
      - './etc/gitlab:/etc/gitlab'
      - './log:/var/log/gitlab'
      - './data:/var/opt/gitlab'

GitLab RunnerをNVIDIA Dockerに構築&ワーカー登録

GPU搭載マシン側にGitLab Runnerコンテナを構成します.YAMLの例は下記に示します.

ポイントは,/var/run/docker.sock:/var/run/docker.sockです.
ランナーをコンテナで動作させている場合,Docker in Docker (dind)を使うかと思います.
NVIDIA Dockerもdindいけるかもですが,NVIDIA Driverのマウントとか,私の実力では修羅の道になりそうなので,ここにあるようにDockerソケットのマウントでシンプルに行きます.
※ Dockerソケットのマウントはセキュリティ上好ましくない行為ですので,ご注意を.

gitlab-runner.yml
version: '2'

services:
  gitlab-runner:
    image: 'gitlab/gitlab-runner:latest'
    container_name : gitlab-runner
    volumes:
      # - './etc/gitlab-runner:/etc/gitlab-runner'
      - '/var/run/docker.sock:/var/run/docker.sock'
    dns: '192.168.X.X'

参考元:https://docs.gitlab.com/runner/install/docker.html

例ではURLにドメインを利用しているので,DNSを設定しています.
コメントアウト部分は設定ファイルのディレクトリです.有効にする場合,下記のような設定ファイルを事前に配置しておく必要があります.(無いとエラーで怒られます)

config.toml
concurrent = 1
check_interval = 0

[session_server]
  session_timeout = 1800

ランナーのコンテナを立てたら,ワーカーの登録を行います.登録のコマンド例は以下の通りです(例は全レポジトリ共有のワーカーの登録になります).runtimeオプションは必ずNVIDIAにしてください.
また,ワーカーを登録するごとに上記config.tomlに設定状況が記録されるので,永続化しておくのが良いと思います.

各オプションの意味は下記URLや,gitlab-runner registerのヘルプで確認できます.
参考:https://docs.gitlab.com/runner/configuration/advanced-configuration.html

登録コマンド
docker-compose -f gitlab-runner.yml exec gitlab-runner gitlab-runner register \
--non-interactive \
--request-concurrency 2 \
--url http://mac-book.local \
--registration-token "token-share" \
--executor docker \
--docker-image ubuntu:latest \
--docker-runtime nvidia \
--docker-dns "192.168.X.X" \
--docker-privileged=true \
--docker-pull-policy "never" \
--description "docker-runner" \
--tag-list "docker,oneshot" \
--run-untagged \
--locked="false"

登録が成功すると,以下のようにGitLabの管理者ページの登録ランナー一覧に表示されます.

今回ドメインを使ってやっているので,ランナーとワーカー登録時にDNSを教えています.
ちなみに,ランナーはジョブ取得時といったGitLabとコネクションするとき,ワーカーはGitレポジトリ引っ張ってくるときにDNS使ってます.

Let's Try!!

実際にデプロイがうまくいくか,レポジトリを適当に作ってテストしてみます.

CI用YAMLは以下の感じです.PyTorchでGPU調べてみるだけのシンプルなものです.nvidia-smiするだけでもよかったかも.

gitlab-ci.yml
image: 'pytorch/pytorch:latest'

deploy:
  stage: deploy
  script:
    - python3 test-gpu.py
test-gpu.py
import torch 
print('torch.cuda.is_available() ...', torch.cuda.is_available())
print('torch.cuda.get_device_name(0) ...', torch.cuda.get_device_name(0))

ジョブの実行結果はこちら(GitLabのレポジトリ→CI/CDから確認できます).どうやらうまくいったみたいです.

さいごに

CIツール使ってGPUコンテナに自動デプロイ的なことをやりました.

ただ,デプロイ先でのデータ永続化について扱っていないため,せっかく学習させたモデルが消えてしまったりします
このままでは正直使えないので,そこらへん別投稿とかで書けたらなぁと思ったり.
あと,最近購入したJetsonをデプロイ先にして遊びたいなぁ..

ありがとうございました.