Incusでリモートデスクトップ環境の構築: GPUとサウンド対応でChromeが動作するまで

## はじめに

これまでDockerを用いてChromeを動作させる方法について紹介してきた。次のステップとして高性能PCを複数のエンジニアが共有して利用する開発用のリモートデスクトップ環境を構築を考える。

しかし、Dockerには[「各コンテナはただ１つだけの用途を持つべき」(1コンテナ1アプリケーションプロセス)](https://docs.docker.jp/v19.03/develop/develop-images/dockerfile_best-practices.html#decouple-applications)という設計哲学がある。この哲学により、systemdのような複数プロセスの管理を行う仕組みをDockerコンテナ内で使用することが推奨されていない。つまり、Dockerは開発用のリモートデスクトップ環境の構築には向いていない。

一方で、VirtualBoxやVMwareといった従来の仮想化ソリューションは、リソース消費が大きい。1台のPC上で多数のエンジニア向けの開発環境を提供するには適していない。

そこで、[LXDからフォークされた軽量なシステムコンテナ仮想化ツールであるIncus](https://linuxcontainers.org/ja/incus/announcement/)を使って、リモートデスクトップ環境を構築する。

その後の記事では、Incusを用いて効率的かつスケーラブルなリモートデスクトップ環境を構築する方法を調査する。

### Linuxコンテナ(LXC/CLD or Incus) と Dockerのユースケースの違い
https://www.docker.com/ja-jp/blog/lxc-vs-docker/

### Linuxコンテナ と Docker と VM のアーキテクチャの違い
https://incus-ja.readthedocs.io/ja/latest/explanation/containers_and_vms/


## LXDとIncusの紹介

### LXDとは何か  

[LXD](https://lxd-ja.readthedocs.io/ja/latest/)はLinuxコンテナ技術をベースにした軽量な仮想化ツールであり、LXC（Linux Containers）の拡張版として開発された。LXDは仮想マシンのように扱えるコンテナを提供し、効率的なリソース管理を可能にしている。LXDを使うことで、従来の仮想マシンに比べてはるかに軽量で、迅速な仮想環境の構築が可能となる。

### LXDからフォークされたIncus

LXDは現時点ではUbuntuを開発しているCanonicalが開発しており、IncusはLXDプロジェクトからフォークされたコンテナ仮想化ツールである。[LXDはバージョン５.2からライセンスがApache2からAGPL3に変更された](https://discourse.ubuntu.com/t/lxd-5-20-has-been-released/40865)が、フォークしたIncusはApache2ラインセンスのままである。

フォークのアナウンスが2023年8月7日ということもあり、現時点ではLXDとIncusに大きな違いはUbuntu依存部分とのこと。コマンド名がldxなのかincusなのかの違いくらいしかないのであれば、現時点ではどちらを利用するかは、個人利用の範囲であればあまり重要ではなさそうだ。

https://gihyo.jp/admin/serial/01/ubuntu-recipe/0796



## ネットワーク環境

```plantuml
@startuml
nwdiag {
  internet [ shape = cloud];
  internet -- router;
  network Home_Network {
      router
      address = "192.168.1.0/24"

      クライアントPC [address = "192.168.1.110"]
      ホストPC  [address = "192.168.1.113"]
  }
  network Bridged_LAN {

      address = "10.107.73.0/24"

      ホストPC  [address = "10.107.73.1"]
      コンテナ [address = "10.107.73.63"]
  }
  group db {
      ホストPC;
      コンテナ;

  }
}
@enduml
```


| 項目          | IPアドレス        | OS                  | 詳細 |
|---------------|-------------------|---------------------|-------|
| **クライアントPC**  | 192.168.1.100     | MacOS| ユーザーが手元で操作する端末|
| **ホストPC**      | 192.168.1.113     | Ubuntu 24.04| Incusコンテナが動作するPC。クライアントPCからコンテナに接続する場合の踏み台サーバ(Bastion Server)になる|
| **Incusコンテナ**  |  10.107.73.63 | Ubuntu 24.04 | 開発用のリモートデスクトップを動作させる。クライアントPCからホストPCを経由してIncusコンテナに接続する |



## Incusのインストールと設定

Ubuntu24.04の場合は標準でincusのパッケージが用意されている。

```bash
sudo update
sudo apt install -U -y incus
```

その他の環境向けの公式ドキュメント

https://incus-ja.readthedocs.io/ja/latest/installing/


### 初期設定

#### `sudo`なしで`incus`コマンドを実行できるようにする

ユーザーにIncusを制御する許可を与える。ユーザーグループは２つ存在する。
- **incus**: 基本的なユーザーアクセスを許可
- **incus-admin**: `Incus`の完全なコントロールを許可

すべてのコマンドを`root`で実行することなくIncusを制御するには、自分のユーザ名を`incus-admin`グループに追加する。

```bash
sudo adduser YOUR-USERNAME incus-admin
newgrp incus-admin
```

`newgrp incus-admin`を行わない場合には端末の再起動（ロウアウトしてログイン）が必要となる

`incus-admin`グループにユーザ名が登録されたことを確認する

 ```bash
 $ sudo cat /etc/group | grep incus
incus:x:984:
incus-admin:x:983:raiko
```

#### Incusの初期化

`Incus`はネットワークとストレージでいくつかの初期設定が必要だ。自動設定で良いなら下記のコマンドを実行する。

```bash:自動設定
incus admin init --minimal
```

初期化オプションをチューニングしたい場合

```bash: 対話式で設定
incus admin init
```

初期化オプションの詳細の公式ドキュメント

https://incus-ja.readthedocs.io/ja/latest/howto/initialize/#initialize


### コンテナ起動と基本設定

#### コンテナの公式イメージ

コンテナの公式のイメージサーバーで利用可能な一覧は下記のコマンドで取得可能だ。

```bash
incus image list images:
```


#### コンテナを起動する
Ubuntu24.04のイメージを使って`noble`という名前のコンテナを起動する。

```bash
incus launch images:ubuntu/24.04 noble
```

#### コンテナ一覧を取得する

起動したコンテナを確認する。デフォルト設定の場合には起動したコンテナはNATでホストPCを経由して外部ネットワークに接続する。作成したコンテナのIPアドレスは`10.07.73.63`である。

```bash
incus list
```

```console: incus listの表示
+-------+---------+----------------------+---------------------------------------------+-----------+-----------+
| NAME  |  STATE  |         IPV4         |                    IPV6                     |   TYPE    | SNAPSHOTS |
+-------+---------+----------------------+---------------------------------------------+-----------+-----------+
| noble | RUNNING | 10.107.73.63 (eth0) | fd42:8159:b94:458:216:3eff:feb8:9cc2 (eth0) | CONTAINER | 0         |
+-------+---------+----------------------+---------------------------------------------+-----------+-----------+
```

#### コンテナ内のユーザーの設定を行う

ここではデフォルトユーザの`ubuntu`を使う。後ほどリモートデスクトップにログインで必要となるパスワードを設定しておく。

```bash
incus exec noble -- passwd ubuntu
```

#### コンテナからインターネットへのパケット到達を確認する

```bash
incus exec noble -- ping -c 4 8.8.8.8
```

```bash incus exec noble -- ping -c 4 8.8.8.8 の出力
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=4.20 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=57 time=4.47 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=57 time=4.25 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=57 time=4.24 ms

--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 4.198/4.289/4.466/0.104 ms
```

:::note warn
筆者の環境ではコンテナからインターネットへのパケット到達の確認に失敗した。コンテナからのNAT接続に必要な設定を手動で行う必要があったのでメモを残しておく。筆者の場合には「NATルールの追加」と「FORWARDチェーンのデフォルトポリシーを変更」の二つでコンテナからインターネット接続が可能となった。
:::

#### コンテナからインターネットに接続できない場合のNAT設定の例

##### NATルールの追加
コンテナから外部へのパケットは`eth0`のIPアドレスに変換して通信する

```bash
sudo iptables -t nat -A POSTROUTING -s 10.107.73.0/24 ! -d 10.107.73.0/24 -o eth0 -j MASQUERADE
```

##### FORWARDチェーンのデフォルトポリシーを変更

FORWARDチェーンのポリシーをACCEPTに変更する。

```bash
sudo iptables -P FORWARD ACCEPT
```

詳細は下記を参照のこと

https://qiita.com/rairaii/items/d44cf249e3810b394572


## コンテナにデスクトップ環境の構築する

デスクトップ環境のパッケージをコンテナにインストールする

```bash:ホストPCで実行
incus exec noble -- apt-get update
incus exec noble -- apt-get upgrade -y
incus exec noble -- sudo apt-get install -y lubuntu-desktop
```


## SSHでコンテナに接続する

SSHでコンテナに接続する環境を構築する

```bash:ホストPCで実行
incus exec noble -- apt-get install -y openssh-server

# sshが起動しているか確認する
incus exec noble -- systemctl status ssh

# sshが起動していない場合は起動する
incus exec noble -- sudo systemctl start ssh

# 再起動時に自動でsshが起動するように設定する
incus exec noble -- sudo systemctl enable ssh
```

### ポートフォワーディングで接続する場合

クライアントPCからホストPCを経由してコンテナにSSH接続するための設定を行う。
(1)incusのポートフォワーディングを利用する場合と、(2)SSHのトンネリングを利用する場合に２点について確認する。

#### ポートフォワーディング設定

ホストPCの10022ポートで接続された場合にコンテナ`noble`の22ポートに転送したい。
Incusでポートフォワーディング行う場合には、(1)ネットワークフォワードを作成し、(2)フォワードするポートを設定する。

```bash
# ネットワークフォワードを作成する 
#  - incusbr0: Incusの初期化時に作られたネットワーク名
#  - 192.168.1.133 はホストPCのIPアドレス
incus network forward create incusbr0 192.168.1.113

# フォワードするポートを設定する
#  - incusbr0: Incusの初期化時に作られたネットワーク名
#  - 192.168.1.133: ホストPCのIPアドレス
#  - tcp: 転送するプロトコル
#  - 13389: ホストPCで受け付けるポート番号
#  - 10.107.73.63: 転送先のコンテナのIPアドレス
#  - 22: 転送先のコンテナのポート番号　（ssh)
incus network forward port add incusbr0 192.168.1.113 tcp 10022 10.107.73.63 22
```

:::note info
フォアワーディング設定を削除する場合
```bash
incus network forward port remove incusbr0 192.168.1.113 tcp 10022
incus network forward delete incusbr0 192.168.1.113
```
:::

#### フォワーディングのポートを指定して接続する
```bash:クライアントPCで実行
ssh ubuntu@192.168.1.113 -p 10022
```

### 踏み台PC経由で接続する場合
クライアントPCからホストPC(192.168.1.113)を踏み台にコンテナ(10.107.73.63)のSSHサーバーに接続する。ホストPCでSSHサーバーが動作している必要があるが、ポートフォワーディングの設定が必要はない。

```bash:クライアントPCで実行
ssh -J raiko@192.168.1.113 ubuntu@10.107.73.63
```

### トラブルシューティング

`ssh`で接続できない場合には`ssh`のステータスやログを確認する

```bash: ホストPCで実行
# sshのステータスを確認する
incus exec noble -- systemctl status ssh

# sshのログを確認する
incus exec noble -- journalctl -u ssh
```

## VScodeでコンテナのファイルをリモート編集する

クライアントPCのVScode（Visual Studio Code)でコンテナのファイルをリモート編集する。

### 拡張機能をインストール
クライアントPCのVScodeに`Visual Studio Code Remote - SSH`拡張をインストールする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/07c9eb79-7efa-1f6d-fb25-c60737485a43.png)


### SSH経由でコンテナに接続

公式リファレンス

https://code.visualstudio.com/docs/remote/ssh

VSCodeのリモートエクスプローラーを開いてのSSHの右にある`➕`ボタンを押して、SSH経由でホストPCを踏み台にしてコンテナに接続する


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/fbddc8d6-26fa-82e8-5d32-7c445a241f83.png)


sshでコンテナにログインする場合と同じコマンドを指定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/a68ac643-7c40-476d-5d2f-c8524da7fbe8.png)


コンテナに接続すると`VS Code Server`がコンテナに自動インストールされる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/e45588ee-c2af-0ae4-4e22-fba7341bbbe1.png)


`VS Code Server`がインストールされるとクライアントPCで動作している`VScode`からコンテナのファイルにリモート編集できるようになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/b82970d5-dfc7-e0a5-336d-0cd0bc15a829.png)



## XRDPでコンテナに接続する

XRDPでコンテナに接続する環境を構築する。

### XRDPサーバ(xrdp)をコンテナにインストールする　

```bash:ホストPCで実行する
incus exec noble -- apt-get install -y xrdp

# xrdpの起動を確認する
incus exec noble -- systemctl status xrdp

# xrdpが起動していない場合
incus exec noble -- systemctl restart xrdp
```

### ポートフォワーディングで`xrdp`に接続する場合

ホストPCの13380ポートにアクセスされた場合はコンテナ`noble`の3398ポートに転送する。

```bash
# ネットワークフォワードを作成する 
#  - incusbr0: Incusの初期化時に作られたネットワーク名
#  - 192.168.1.133 はホストPCのIPアドレス
# 未作成の場合は実行する
# incus network forward create incusbr0 192.168.1.113

# フォワードするポートを設定する
#  - incusbr0: Incusの初期化時に作られたネットワーク名
#  - 192.168.1.133: ホストPCのIPアドレス
#  - tcp: 転送するプロトコル
#  - 13389: ホストPCで受け付けるポート番号
#  - 10.107.73.63: 転送先のコンテナのIPアドレス
#  - 3389: 転送先のコンテナのポート番号　（RDP)
incus network forward port add incusbr0 192.168.1.113 tcp 13389 10.107.73.63 3389
```

:::note info
フォアワーディング設定を削除する場合
```bash
incus network forward port remove incusbr0 192.168.1.113 tcp 13389
incus network forward delete incusbr0 192.168.1.113
```
:::

クライアントPCからIncusコンテナに接続する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/db19a209-ea01-d9c2-4de9-34ca1b06ceb5.png)

Incusコンテナで動作するディスクトップを表示できた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/87582fb3-383b-e370-deb6-466096b1e072.png)

### 踏み台PCを使ってクライアントPCからIncusコンテナにXRDP接続する

クライアントPCからホストPC(踏み台PC)を経由してIncusコンテナまでのSSHトンネルを作成する。つまり、下記のコマンドでクライアントPCの`3389`ポートを、ホストPCを経由して、Incusコンテナの`3389`ポートにフォワーディングする。

```bash
# ssh -L 3389:<コンテナのIP>:3389 -J username@<ホストPCのIP> username@<コンテナのIP>
ssh -L 3389:10.107.73.63:3389 -J raiko@192.168.1.113 ubuntu@10.107.73.63
```

クライアントPC上で動作しているMicrosoft Remote Desktopアプリで`localhost`の`3389`ポートに接続する。この接続はホストPCを経由してIncusコンテナのxrdpに接続する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/f325f4c2-16a2-19e5-0878-5bbe9cf51e36.png)

Incusコンテナのディスクトップを表示できた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/94c4e40f-bed4-4898-ae26-deb11ee97482.png)



### トラブルシューティング

`xrdp`に接続できない場合には`xrdp`のステータスやログを確認する

```bash: ホストPCで実行
# xrdpのステータスを確認する
incus exec noble -- systemctl status xrdp

# xrdpのログを確認する
incus exec noble -- journalctl -u xrdp
```

## コンテナでChromeブラウザを動作させる

### Chromeインストール手順  

リモートデスクトップ環境が構築されたコンテナ内にChromeブラウザを追加でインストールする

```bash:ホストPCで実行
incus exec noble -- wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
incus exec noble -- apt-get install -y ./google-chrome-stable_current_amd64.deb
```

### Chromeの動作確認 

インストールが完了したらChromeを起動し正常に動作するか確認する。

```bash: XRDPで表示したリモートデスクトップのターミナルで実行
google-chrome
```

YouTubeで音声再生をサポートした状態で動画が再生されることを確認した。ただしRDP経由では音声の再生が安定せず、動画もカクカクと表示されることが多い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/cae64f9d-4d41-23cd-59c4-6225249bf6ed.png)

`chrome://gpu`にアクセスするとGPUを利用せずソフトウェアレンダリングしていることがわかる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/09937549-3337-c169-a110-1bdfb3a762ce.png)


### IncusコンテナでGPUをサポートする

#### GPUデバイスの追加
`intelgpu`という名前をつけて`gpu`デバイスをコンテナ`noble`に追加する。

```bash:ホストPCで実行する
incus config device add noble intelgpu gpu
```
```bash: incus config device add noble intelgpu gpuの出力
Device intelgpu added to noble
```

コンテナに追加したデバイスを確認する。

```bash:ホストPCで実行する
incus config device list noble
```

```bash:incus config device list nobleの出力
intelgpu
```

#### GPUデバイスのアクセス権限の確認

GPUにアクセスするための`/dev/dri`を確認すると、`card1`と`renderD128`のユーザIDとグループIDがどちらも`root`となっているため、ユーザ`ubuntu`からアクセスできないことが確認できる。

```bash:ホストPCで実行する
incus exec noble -- ls -la /dev/dri
```
```bash:incus exec noble -- ls -la /dev/dri/の出力
total 0
drwxr-xr-x 2 root root       80 Aug 27 01:56 .
drwxr-xr-x 9 root root      520 Aug 27 01:56 ..
crw-rw---- 1 root root 226,   1 Aug 27 01:56 card1
crw-rw---- 1 root root 226, 128 Aug 27 01:56 renderD128
```

#### GPUへのアクセス権限を解決しつつデバイスを追加

```bash:ホストPCで実行する
# 先ほど作成したGPUデバイスは一度削除する
incus config device remove noble intelgpu

# グループIDにvideoを指定してGPUデバイスを作成しなおす
incus config device add noble intelgpu gpu gid=`getent group video | cut -d: -f3`

# 作成したGPUデバイスへのアクセス権限を確認する
incus exec noble -- ls -l /dev/dri
```

`video`グループに所属させれば`/dev/dri`以下へアクセス可能になる。

```bash: incus exec noble -- ls -l /dev/dri の出力
total 0
crw-rw---- 1 root video 226,   1 Aug 27 02:07 card1
crw-rw---- 1 root video 226, 128 Aug 27 02:07 renderD128
```

#### GPUへのアクセス権限を付与する

```bash:ホストPCで実行する
# ユーザ(ubuntu)をvideoグループに追加する
incus exec noble -- adduser ubuntu video

# videoグループに追加されたことを確認する
incus exec noble -- cat /etc/group | grep video

# コンテナをリスタートする
incus restart noble
```

#### GPUへのアクセスを確認する

`glxinfo`コマンドでIntelのGPUが利用可能であると確認できる。

```bash: コンテナで実行する
glxinfo | grep OpenGL
```
```bash:glxinfo | grep OpenGL　の出力
OpenGL vendor string: Intel
OpenGL renderer string: Mesa Intel(R) Graphics (RPL-S)
OpenGL core profile version string: 4.6 (Core Profile) Mesa 24.0.9-0ubuntu0.1
OpenGL core profile shading language version string: 4.60
OpenGL core profile context flags: (none)
OpenGL core profile profile mask: core profile
OpenGL core profile extensions:
OpenGL version string: 4.6 (Compatibility Profile) Mesa 24.0.9-0ubuntu0.1
OpenGL shading language version string: 4.60
OpenGL context flags: (none)
OpenGL profile mask: compatibility profile
OpenGL extensions:
OpenGL ES profile version string: OpenGL ES 3.2 Mesa 24.0.9-0ubuntu0.1
OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.20
OpenGL ES profile extensions:
```

`vainfo`コマンドでIntelのGPUのハードウェアデコーダが利用可能だと確認できる

```bash:コンテナで実行する
sudo apt-get update
sudo apt-get install vainfo
vainfo
```
```bash: vainfoの出力
libva info: VA-API version 1.20.0
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so
libva info: Found init function __vaDriverInit_1_20
libva info: va_openDriver() returns 0
vainfo: VA-API version: 1.20 (libva 2.12.0)
vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics - 24.1.0 ()
vainfo: Supported profile and entrypoints
      VAProfileNone                   : VAEntrypointVideoProc
      VAProfileNone                   : VAEntrypointStats
      VAProfileMPEG2Simple            : VAEntrypointVLD
      VAProfileMPEG2Main              : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointEncSliceLP
      VAProfileH264High               : VAEntrypointVLD
      VAProfileH264High               : VAEntrypointEncSliceLP
      VAProfileJPEGBaseline           : VAEntrypointVLD
      VAProfileJPEGBaseline           : VAEntrypointEncPicture
      VAProfileH264ConstrainedBaseline: VAEntrypointVLD
      VAProfileH264ConstrainedBaseline: VAEntrypointEncSliceLP
      VAProfileHEVCMain               : VAEntrypointVLD
      VAProfileHEVCMain               : VAEntrypointEncSliceLP
      VAProfileHEVCMain10             : VAEntrypointVLD
      VAProfileHEVCMain10             : VAEntrypointEncSliceLP
      VAProfileVP9Profile0            : VAEntrypointVLD
      VAProfileVP9Profile0            : VAEntrypointEncSliceLP
      VAProfileVP9Profile1            : VAEntrypointVLD
      VAProfileVP9Profile1            : VAEntrypointEncSliceLP
      VAProfileVP9Profile2            : VAEntrypointVLD
      VAProfileVP9Profile2            : VAEntrypointEncSliceLP
      VAProfileVP9Profile3            : VAEntrypointVLD
      VAProfileVP9Profile3            : VAEntrypointEncSliceLP
      VAProfileHEVCMain12             : VAEntrypointVLD
      VAProfileHEVCMain422_10         : VAEntrypointVLD
      VAProfileHEVCMain422_12         : VAEntrypointVLD
      VAProfileHEVCMain444            : VAEntrypointVLD
      VAProfileHEVCMain444            : VAEntrypointEncSliceLP
      VAProfileHEVCMain444_10         : VAEntrypointVLD
      VAProfileHEVCMain444_10         : VAEntrypointEncSliceLP
      VAProfileHEVCMain444_12         : VAEntrypointVLD
      VAProfileHEVCSccMain            : VAEntrypointVLD
      VAProfileHEVCSccMain            : VAEntrypointEncSliceLP
      VAProfileHEVCSccMain10          : VAEntrypointVLD
      VAProfileHEVCSccMain10          : VAEntrypointEncSliceLP
      VAProfileHEVCSccMain444         : VAEntrypointVLD
      VAProfileHEVCSccMain444         : VAEntrypointEncSliceLP
      VAProfileAV1Profile0            : VAEntrypointVLD
      VAProfileHEVCSccMain444_10      : VAEntrypointVLD
      VAProfileHEVCSccMain444_10      : VAEntrypointEncSliceLP
```

Chromeブラウザで`chrome://gpu`を開くことでGPUでのハードウェアアクセラレーションが有効になっていることが確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/a03be82a-b1ec-881c-fe44-74b4c8a46ae6.png)



XRDPでリモート表示させる場合は転送速度が間に合わないためカクカク表示されるが、3Dベンチマークのレンダリングは50fp以上で行われていることが確認できる。
![incus_gpu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1017489/27ca1aa3-4f6f-68e8-da2b-8c1b17ee5a1d.gif)



## まとめ

ここまでの作業情報をまとめておく。

### コンテナの起動

```bash: ホストPCで実行する
# コンテナの起動
incus launch images:ubuntu/24.04 noble
```

### コンテナがネットワーク接続できない場合
https://qiita.com/rairaii/items/d44cf249e3810b394572

### デスクトップ環境の構築
```bash: ホストPCで実行する
# パッケージのインストール
incus exec noble -- apt-get update
incus exec noble -- apt-get upgrade -y
incus exec noble -- sudo apt-get install -y lubuntu-desktop openssh-server xrdp 

# ユーザとパスワードの設定
incus exec noble -- passwd ubuntu

# Chromeのインストール
incus exec noble -- wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
incus exec noble -- apt-get install -y ./google-chrome-stable_current_amd64.deb

# コンテナをリスタートする
incus restart noble
```


### ポートフォワーディングの設定

ホストPCとコンテナのIPアドレスを確認する
```bash: ホストPCで実行する
# ホストPCのIPアドレス
ip a

# コンテナのIPアドレス
incus list
```

```bash: ホストPCで実行する
# IPアドレスを設定する
HOST_ADDRESS=192.168.1.113
CONTAINER_ADDRESS=10.107.73.126

# ネットワークフォワードを作成する 
incus network forward create incusbr0 ${HOST_ADDRESS}

# 転送先のコンテナのポート番号　（ssh)
incus network forward port add incusbr0 ${HOST_ADDRESS} tcp 10022 ${CONTAINER_ADDRESS} 22

# 転送先のコンテナのポート番号　（RDP)
incus network forward port add incusbr0 ${HOST_ADDRESS} tcp 13389 ${CONTAINER_ADDRESS} 3389
```



### GPUを有効にする
```bash: ホストPCで実行する
# グループIDにvideoを指定してGPUデバイスを作成する
incus config device add noble intelgpu gpu gid=`getent group video | cut -d: -f3`

# ユーザ(ubuntu)をvideoグループに追加する
incus exec noble -- adduser ubuntu video

# コンテナをリスタートする
incus restart noble

# GPUでのハードウェアデコーダが有効であることを確認する
incus exec noble -- apt-get install -y vainfo
incus exec noble -- vainfo
```

### GPUが有効であることを確認する

```bash: XRDPでログインしたコンテナのターミナルで実行する
# GPUが認識されていることを確認する
glxinfo | grep OpenGL
```

### ダウンロードしたキャッシュを削除する

```bash: コンテナのターミナルで実行する
apt clean
rm -rf /var/lib/apt/lists/*
```

### 踏み台PC経由でSSHに接続する
クライアントPCからホストPC(192.168.1.113)を踏み台にコンテナ(10.107.73.63)のSSHサーバーに接続する。ホストPCでSSHサーバーが動作している必要があるが、ポートフォワーディングの設定が必要はない。

```bash:クライアントPCで実行
ssh -J raiko@192.168.1.113 ubuntu@10.107.73.63
```