第641回では「LXDとmicrok8sでシングルサーバーをKubernetesクラスターにする」と題して、より高機能になったmicrok8sについて紹介しました。microk8sで構築したKubernetesがあれば、気軽にたくさんのCPUコアを使ってさまざまなワークロードを動かし、部屋を暖められます。でも、待ってください。そのサーバーにはもっと便利な熱源がありませんか? そうGPUですね。本格的に寒くなる前に、Kubernetes環境からもGPUを使えるようにしましょう。
ホスト側でGPUパススルーの準備
第641回ではLXDの仮想マシンインスタンスの上にmicrok8sをインストールしました。つまりホストマシンのGPUを使うには、LXDの仮想マシンの中からGPUにアクセスできなくてはなりません[1]。コンテナの場合、第532回の「LXDのコンテナからGPUを利用する」などで手順を紹介していますが、実はLXD 4.3から仮想マシンインスタンスでも同じ手順でGPUパススルーをサポートするようになったのです。
この方法の注意点はふたつあります。まず、LXD 4.3はフィーチャーリリースであること。LXDにはLTSリリースとフィーチャーリリースの2種類のリリースブランチが存在します。LTSリリースはUbuntuのLTSと同じタイミング(つまり2年周期)でリリースされるリリースブランチで、メンテナンスもそれに合わせて行われます。Ubuntuサーバーをインストールしたときに一緒についてくるのは、LTSリリースのLXDです。現在はLTS 2.0、3.0、4.0のLTSリリースが存在します。
それに対してフィーチャーリリースは、ほぼ1ヶ月周期でリリースされる機能追加ブランチです。次のLTSリリースに向けてさまざまな新機能を追加しているため、どんどんと仕組みが更新されています。このようにフィーチャーリリースは安定性を求める環境には向かない点に注意が必要です。
もうひとつの注意点はGPUが仮想マシンに占有されてしまうことです。仮想マシンの場合、IOMMU(IntelのVT-dやAMDのAMD-Vi)を用いてPCIパススルーで仮想マシン上からGPUをアクセスできるようにするため、そのGPUを使えるのは設定した仮想マシンインスタンスのみとなります。コンテナインスタンスの場合は、GPUを複数のインスタンスで共有できますが、仮想マシンの場合はインスタンスの数だけGPUを用意してください。
よってまずはLXDをフィーチャーリリースに変更します。新規にインストールする際は「snap install
」のオプションに「--channel=latest/stable
」とつけるだけです。すでにインストール済みの環境であれば、次のようにrefreshしてください。
これでLXD側の準備は整いました。次にカーネル側の準備を行います。Intelの場合、IOMMU機能は有効化されていません。よってIntel CPUを使っている場合、カーネルのコマンドラインからIOMMU機能を有効化します。ちなみにAMDの場合は最初から有効化されているようです。
「/etc/default/grub
」のGRUB_CMDLINE_LINUX
を次のように変更してください。
あとはGRUBの変更を反映します。
またNVIDIAのGPUを利用している場合、Nouveauドライバーをロードしないようにしておいてください。
ここまで実行したら、一度ホストシステムを再起動しておきましょう。起動後に次のように「IOMMU enabled
」が表示されればOKです。
もし表示されない場合、チップセットが対応していない可能性もあります。まずはBIOSメニューでIOMMUが有効化されているか確認してみましょう。
仮想マシンインスタンスからGPUを見えるようにする
GPUを使うための仮想マシンインスタンスを用意します。このあたりは第641回とほぼ同じです。
インスタンス名が「kubeflow」ですが残念ながら今回はKubeflowは関係ありません[2]。タイムゾーンの設定やインスタンスのスペックを上げているのも、今回の話とは関係ありません。
次に一度インスタンスを停止して、GPUをインスタンスに追加します。仮想マシンインスタンスの場合このあたりの設定変更の際は、毎回インスタンスを停止しなくてはならないので少し面倒です。
ストレージを増やしていますが、これもスペック等と同じで関係ありません。重要なのはその次の行からです。
まず「security.secureboot=false
」でセキュアブートを無効化しています。LXDの仮想マシンインスタンスは、初期状態だとセキュアブートが有効化されています。普段使う分には問題ありませんが、あとで説明するようにサードパーティのドライバーをインストールしたい場合(つまりはDKMSを使いたい場合)には若干手間がかかります。具体的にはDKMSインストール時にパスワードを設定し、再起動後にコンソールからそのパスワードを入力する必要があるのです。LXDの場合、「lxc console
」で仮想マシンインスタンスのコンソールにアクセスできるものの、手間とメリットを考えるとセキュアブートを切ってしまうのもひとつの手でしょう。
次に「lxc config device add
」でベンダーIDが「0x10de」なPCIデバイスを「nv
」という名前でインスタンスに紐づけています。ここで言う「0x10de」はNVIDIAのベンダーIDです。具体的には次のコマンドで確認できます。
他にも「productid
」のようにデバイスIDやPCIのアドレスなど、より細かい粒度でも指定可能です。複数のGPUが繋がったサーバーだとこれらの指定が必要になるでしょう。詳しいことはLXDのドキュメントのGPUデバイスに関する項目を参照してください。
最後は仮想マシンの中でNVIDIAのGPUを使うためのオプションとなります。「raw.qemu
」オプションに「-cpu host,kvm=off
」を設定しています。LXDではraw.qemu
に任意の文字列を設定することで、仮想マシンを起動する際のQEMUコマンドのパラメーターを変更可能です。「kvm=off
」は仮想マシンインスタンスが「仮想マシンであること」を隠すためのオプションです。
どんな仮想マシンシステムもゲストの中から「CPUID命令」を発行することで、「自分自身がどんなハイパーバイザー上で動いているか」がわかる仕組みが存在します。Ubuntuだとcpuidパッケージで提供されているcpuidコマンドを使うと、たとえば「hypervisor guest status = true
」や「hypervisor_id = "KVMKVMKVM "
」とセットされることが確認できます。どうやらNVIDIAのツールは「KVMインスタンスであること」を検知すると「Unknown error」を発生させるようです。そこでQEMUにはこのKVMシグネチャを表示しないオプションが追加されました。これがCPUオプションの「kvm=off
」です。
設定後にインスタンスを起動したら、インスタンスの中からもGPUデバイスが見えていることを確認しましょう。
サーバー向けNVIDAドライバーのインストール
インスタンスの中からGPUデバイスが見えるようになったので適切なデバイスドライバーをインストールします。AMD系であれば標準のドライバーで問題ないはずですし、第471回や第524回でも紹介しているように、プロプライエタリなドライバーも用意されています。それに対してNVIDIAはプロプライエタリーなドライバー一択です。
具体的なインストール方法は第454回の「Ubuntu 16.04 LTSにNVIDIA製ドライバーをインストールする3つの方法」で紹介しているものの、当時と状況が若干異なっています。まずパッケージ名が「nvidia-XXX」から「nvida-driver-XXX」に変更されました。さらに「nvidia-driver-XXX-server」なるパッケージも追加されています。この「nvidia-driver-XXX-server」は中身が少し異なる(Powerサーバー向け対応が入っている)ものの、中身そのものはほとんど「nvidia-driver-XXX」と同じです。
では、何が違うのかと言うと新バージョンが出た時の挙動です。たとえば2020年11月時点での最新は「nvidia-driver-450」でその一つ前は「nvidia-driver-440」となっています。これらは現時点で次のような形になっています。
- nvidia-driver-440:nvidia-driver-450に依存するだけの移行用ダミーパッケージ
- nvidia-driver-440-server:NVIDA Driver 440.95.01をインストールする
- nvidia-driver-450:NVIDA Driver 450.80.02をインストールする
- nvidia-driver-450-server:NVIDA Driver 450.80.02をインストールする
つまり「nvidia-driver-XXX」をインストールした場合、新しいドライバーがリリースされたら自動的にアップグレードされます。それに対して「nvidia-driver-XXX-server」の場合はアップグレードされません。サーバーの用にドライバーが更新されることを防ぎたい場合に「nvidia-driver-XXX-server」は便利なパッケージなのです。
さらにサーバー向けとして「nvidia-headless-XXX」と「nvidia-headless-XXX-server」が追加されました。これらはX Window System/Wayland関連のパッケージに依存しないため、サーバーのようなヘッドレスマシンにおいてGPGPU目的でNVIDIAのドライバーをインストールしたい際に便利です。
ここからはNVIDIAのGPU前提で話を進めます。まずはドライバーのインストールです。次のコマンドでインストールできるバージョンを確認して、必要なものをインストールしてまいましょう。
ちなみに実機にNVIDIAドライバーをインストールしたときと比べるとDRMやKMS関連のドライバーがロードされていないことに気がつくかもしれません。これはLXDの仮想マシンインスタンスで使用しているカーネル(linux-image-kvm)は通常のカーネル(linux-image-generic)と異なり、CONFIG_DRM
などサーバーで使うことが少ないコンフィグを軒並み無効化しているためです。もし、何らかの理由で通常のカーネルが必要になった場合は次のようにカーネルを差し替えてください。
恒例のnvidia-smi
コマンドで動作確認しましょう。
もし「Unable to determine the device handle for GPU 0000:06:00.0: Unknown Error
」のようなエラーメッセージが表示される場合、何か設定に問題があります。一番可能性が高いのが、KVM上と判定されたケースなので、前項で説明したようにLXDのraw.qemu
設定に「-cpu host,kvm=off
」を追加できているか確認してください。一番手っ取り早いのはホスト上で「ps -fe | grep kvm=off
」することです。
microk8sでGPUを有効化する
前回と同じようにmicrok8sをインストールしましょう。
GPUの有効化もコマンド一発です。
いろいろ準備するためにそれなりに時間がかかります。気長に待ちましょう。無事にセットアップが完了したら、kube-system名前空間において、NVIDIAデバイスプラグインのPodが動いていることが確認できます。
試しにmicrok8sのドキュメントにもあるcuda-vector-addを使ってみましょう。これは単にCUDAのAPIを使ってSIMD演算するだけのPodです。
あとはPodを作成して、状態を確認してみます。
cube-vector-addはいわゆるデーモンなどではなくCUDAの演算をして終了するだけのコマンドです。kubectl create
したあとはイメージのダウンロードなどでしばらく時間がかかりますが、最終的に「Completed」で完了します。完了したら削除しておきましょう。
たとえば意図的にGPUを削除した上で実行すると、「Pendig」で停止します。
「kubectl decribe
」で確認すると、GPUを確保しようとして待っていることがわかります。
YAMLファイルの中で「nvidia.com/gpu: 1
」と書いているため、当然と言えば当然です。さらにこの部分をコメントアウトしてGPUがなくても強制的に実行しようとすると、次のようにエラー終了します。
kubectl describe
を実行すれば、CUDA関連のエラーが残っているはずです。
以上のように正しく設定してあるときちんとGPUがコンテナから見えて、CUDAも使えることがわかりました。
ここから先はGPUが使えるKubernetes環境なので、好きなコンテナイメージで、インスタンス代を気にすることなく好きなだけたくさんGPUを動かして、寒い冬を乗り切りましょう![3]