Help us understand the problem. What is going on with this article?

systemd-nspawnで外部からssh可能でnvidia GPUを使えるコンテナを作る

前書き

CUDAのバージョンが違う環境を複数用意して使い分けたい場合などにコンテナは便利である。DockerやLXCなど有名なものが幾つかあるが、最も軽量だと思われる systemd-nspawn を用いて、ホストOSとゲストOSが両方共Ubuntuである場合(両方DebianでもたぶんOK)にコンテナを作りゲストOSの使用メモリ量を制限する手順を解説する。デフォルトでホストとゲストがネットワークインターフェースを共有することに気づいていなかったり、無線LANインターフェースでMACVLANを使えないことを知らなかったため丸一日調べるのに時間がかかった。MACVLANなどでネットワークインターフェースをホストOSとゲストOSで分ける手順は最後の節で述べる。以下の作業はすべてroot権限で実行してください。コンテナ名はcontainer1にしてあるので適当に置き換えてください。

ホストとゲスト

コンテナ内で動作しているほうがゲストで、ハードウェアの上で直接動作しているほうがホストです。

コンテナ環境の設定

  1. (もしbtrfsを使っているなら btrfs subvolume create /var/lib/machines/container1)
  2. apt-get install debootstrap
  3. debootstrap --arch amd64 --variant=minbase --include=ubuntu-minimal,openssh-server,language-pack-en-base,language-pack-ja-base bionic /var/lib/machines/container1 http://jp.archive.ubuntu.com/ubuntu この作業で /var/lib/machines/container1 に最小限のUbuntu 18.04とopensshサーバーsshdがインストールされる。bionicの部分はホストOSと揃えたほうが無難でしょう。
  4. cp /etc/apt/sources.list /var/lib/machines/container1/etc/apt
  5. vi /var/lib/machines/container1/etc/ssh/sshd_configPort 26000, PermitRootLogin yes にする。ネットワークインターフェースがホストとゲストで共有されているから、ホストで動作するsshdとゲストで動作するsshdのポートを変えないと同時に実行できない。
  6. systemd-nspawn -M container1 をホストで実行するとゲストOSのシェルプロンプトが出るから、passwd コマンドで適当なルートパスワードを設定する。
  7. systemd-nspawn -b -M container1 --bind=/dev/nvidia0 --bind=/dev/nvidiactl --bind=/dev/nvidia-uvm --bind=/dev/nvidia-uvm-tools --bind=/dev/nvidia-modeset でゲストOSを起動する。--bindは指定したホストOSのファイルをゲストにもアクセスさせるオプションである。また/etc/systemd/system/systemd-nspawn@.serviceの中のDeviceAllowの行に上記のデバイスを追加しておく。
  8. ゲストOSの中でapt-get --no-install-recommends install software-properties-common; add-apt-repository ppa:graphics-drivers/ppa; apt-get update; apt-get dist-upgrade; apt-get --no-install-recommends install nvidia-utils-???を実行する。???の部分にはホストOSに入っているNVIDIAドライバのバージョン(例えば410)を入れる。これでゲストOSの中でnvidia-smiを使用できる。
  9. 他の計算機からゲストOSにsshでアクセスするにはssh -l root -p 26000 ホストOSのホスト名でできる。ゲストOSの終了はログインしてpoweroffまたはshutdown -h nowでできる。
  10. ゲストOSの/etc/hostname の設定をやっておいたほうが便利である
  11. /homeにコンテナ内からアクセスさせたい場合は --bind=/home をつければよい

ゲストOSのメモリ制限と自動起動

ホストOSの起動時にゲストOSも起動し、ゲストOSの占有実メモリ量を制限するためには以下のようにする。
1. cp /lib/systemd/system/systemd-nspawn@.service /etc/systemd/system/systemd-nspawn@container1.service コピー先ファイル名の@の後にコンテナ名を入れてください
2. コピー先ファイルを編集してExecStartのところから--network-veth -Uを削除し、--settings=falseに変更し、--bind=/dev/nvidia0 --bind=/dev/nvidiactl --bind=/dev/nvidia-uvm --bind=/dev/nvidia-uvm-toolsを追加する。
3. '[Service]'欄のどこかに以下を追加
DeviceAllow=/dev/nvidia0 rwm
DeviceAllow=/dev/nvidiactl rwm
DeviceAllow=/dev/nvidia-uvm rwm
DeviceAllow=/dev/nvidia-uvm-tools rwm
DeviceAllow=/dev/nvidia-modeset rwm

4. '[Service]'欄に Delegate=memory pids cpu ioを追加し、MemoryHigh=3Gを追加。これでホストOSが使用する実メモリはおおよそ3GBに制限される。MemoryHighの他にもいろいろな制限をホストOSに課すことができるMemoryHighの制限機能を有効にするにはcgroup v1をsystemdにマウントさせない下準備を予め実施してホストOSを起動しなおしておく必要がある。
5. ここまで行ったあとにsystemctl start systemd-nspawn@container1でメモリ占有を制限したゲストOSを開始することが可能で、ホストOS起動時にゲストOSも自動で起動するにはsystemctl enable systemd-nspawn@container1とする。
6. ゲストOSのナイス値を低いままにするにはsystemd-nspawnの起動オプションに--drop-capability=CAP_SYS_NICEを追加した上で、[Service]欄に
Nice=19
RestrictRealtime=true

を追加する。
7. --drop-capability の引数にはCAP_MKNOD,CAP_SYS_TIME,CAP_SYS_BOOTもあってもよいだろう

独立したIPアドレスをゲストOSに与える方法

systemd-nspawn --network-macvlan=インターフェース名 を使う方法

インターフェース名にはenp0s25などの有線イーサネットを指定する。無線を指定すると動かない。ゲストOSの中に以下のファイルを作ればDHCPで自動的にIPアドレスをゲストOSが取得する。ゲストOSとホストOSの通信はなぜかできない。これは設定ミスではなくてそういうものらしいです

/etc/netplan/01-macvlan.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    mac-vlan1:
      match:
        name: mv-*
      dhcp4: true
      dhcp6: true

このときホストとゲストの間の通信ができない問題は、ホスト側もMACVLAN経由でインターネット接続すればよい。この設定はUbuntu標準のnetplanではできないようだが、systemd-networkd を用いた設定は「仮想環境のネットワーク接続をMACVLANとMACVTAPに切り替えてみた」に説明がある

systemd-nspawn -n を使う方法

  • UbuntuではホストOS側でsystemd-networkdが標準では起動されていないから、ゲストOSの起動前にsystemctl start systemd-networkdなどで起動しておく必要がある
  • 10.0.0.6 のようなIPアドレスが割り振られるから、組織内の別の計算機からアクセスできない場合も多そう

systemd-nspawn --network-ipvlan=インターフェース名 を使う方法

  • インターフェースは無線でも構わない。DHCPクライアントをゲストOS内で起動してIPアドレスを取得する方法がわからないので、例えば以下のファイルをゲストOSに置いて固定IPアドレスを割り当てる。DHCPでIPアドレスを貰えない一因としてMACアドレスをホストOSとゲストOSが共有していることがあるだろう。
/etc/netplan/01-ipvlan.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ip-vlan1:
      match:
        name: iv-*
      dhcp4: false
      dhcp6: false
      addresses: [192.168.1.150/24]
      gateway4: 192.168.1.1
      nameservers:
        addresses: [192.168.1.1]
  • 上記の設定でゲストOSから例えばapt-get updateなどをできるが、ホストOSからゲストOSのIPアドレスにsshしてもつながらない
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした