はじめに
最近 Docker 内で GUI を使おうと思い色々調べたので、それのメモです。
参考までに筆者の環境は以下のとおりになっています。
- OS
- Windows 11 Home 22H2
- WSL(一部割愛)
- WSL バージョン: 1.0.3.0
- カーネル バージョン: 5.15.79.1
- WSLg バージョン: 1.0.47
wsl -vで確認できます。ない人はwsl --updateをしてみてください
- Docker
- Docker Desktop 4.16.3
- Docker version 20.10.22
- Docker Compose version v2.15.1
今回はWSLgを使う例、およびVNCを使う例を紹介します。
WSLg を使う方法
WSLg is 何?
WSL その 222 - Linux GUI アプリを動かす WSLg のアーキテクチャーと仕組みを見るとなんとなく御理解いただけるのではないかなと思います。

上記ブログでも紹介されているMicrosoft のブログから図を拝借してきました。普段使用しているディストリビューションの一部が書き換えられて、裏で動作している GUI 用のサーバーに接続しているようです。そしてこのサーバーからリモートデスクトップを使ってホスト(つまり Windows)と通信しているのだと思います。
図を見て「X とは?」となった方はgihyo 様の記事を確認すると何となく理解できると思います
試しにDISPLAYなどの環境変数を見てみると
$ printenv DISPLAY WAYLAND_DISPLAY XDG_RUNTIME_DIR PULSE_SERVER:0wayland-0/mnt/wslg/runtime-dir/mnt/wslg/PulseServer確かに GUI 用のサーバーに接続するように設定されているようです。
Docker の設定
以下のようなDockerfileとcompose.yamlを準備してください、「Docker Compose なぞ使わねえ!」という方は適時docker -vなどに読み替えてください。たぶん動くと思います。
Dockerfile
# devcontainerを使いたかったのでこのイメージを使ってますが、# 通常の用途では`ubuntu:20.04`とかを使うといいと思いますFROM mcr.microsoft.com/vscode/devcontainers/base:debian
# 動作確認用です。不要なら削除してくださいRUN apt-get update -y && \ apt install -y \ x11-appscompose.yaml
services: app: build: context: . dockerfile: Dockerfile command: sleep infinity environment: - DISPLAY=$DISPLAY - WAYLAND_DISPLAY=$WAYLAND_DISPLAY - XDG_RUNTIME_DIR=/tmp - PULSE_SERVER=$PULSE_SERVER volumes: - type: bind source: /tmp/.X11-unix target: /tmp/.X11-unix - type: bind source: "${XDG_RUNTIME_DIR}/wayland-0" target: /tmp/wayland-0docker compose upしてコンテナを立ち上げ、docker compose exec app bashでコンテナ内に入ります。
一応ここでも環境変数がどうなっているか軽く確認します。
$ printenv DISPLAY WAYLAND_DISPLAY XDG_RUNTIME_DIR PULSE_SERVER:0wayland-0/tmp/mnt/wslg/PulseServerちゃんと設定されている風ですね。試しにxeyesを使ってみます。
$ xeyes
ちゃんと画面が出てきました!!。軽くしか試していませんが2つ以上ウィンドウを立ち上げても問題ないようです。

docker 内で日本語化の設定などをすれば、おそらく日本語入力などもできると思います(未検証)。
VNC を使う方法
そもそも大抵の要望は 👆 のWSLgで問題ないと思います。なのでほとんどの方はここを読む必要がありません。ですが特定のユースケース、例えばスクリーンショットを取る等の必要があればこちらを選ぶと良いと思います。
VNC is 何?
調べる中でVNCを使ってもできるという情報があったのですが、私はそもそも初めて聞く言葉だったのでちょっと調べます。
辞書によると、RFB と呼ばれるプロトコルを用いてデスクトップ画面や、キーボードなどの入出力をネットワーク越しに通信するソフトウェアの総称だそうです。音声通信も調べた限りできるっぽいです。
Docker の設定
これに関してはいい感じのイメージがあったのでこれを使えば以下の面倒な設定は不要です。
今回は自分で組む経験を積みたいので Dockerfile から書いていきます。
(再掲)以下のようなDockerfileとcompose.yamlを準備してください、「Docker Compose なぞ使わねえ!」という方は適時docker -vなどに読み替えてください。たぶん動くと思います。
Dockerfile
# devcontainerを使いたかったのでこのイメージを使ってますが、# 通常の用途では`ubuntu:20.04`とかを使うといいと思いますFROM mcr.microsoft.com/vscode/devcontainers/base:debian
ENV DISPLAY=:1ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && \ apt install -y --no-install-recommends \ x11-apps \ # 好みで適当なGUIに変えてください lxde \ # 好みでここも好きなVNCに変えられます tigervnc-standalone-server \ tigervnc-common \ # NoVNC novnc \ websockifycompose.yaml
services: app: build: context: . dockerfile: Dockerfile command: sleep infinity ports: - 7001:80先ほどど同様の手順でコンテナ内に入り、TigerVNC or お好きな VNC を起動します。
$ USER=root vncserver :1 -geometry 800x600 -depth 24
rootでユーザを作成し解像度 800x600 の VNC デスクトップを立ち上げています。depthの意味はよくわかっていません。パスワード入力してくださいと言われたら、適当なパスワードを設定します。
vncserver -listで現在の VNC デスクトップを確認できます。また、vncserver -kill :xで対象の VNC デスクトップを削除できます。

5901 番のポートにサーバーが立っているので、localhost:5901 を指定します。
$ websockify -D --web=/usr/share/novnc/ 80 localhost:5901
ここまで実施したら、ホスト PC からlocalhost:7001/vnc.htmlにアクセスすると…

NoVNCの画面が表示されるので、Connect を押して先程設定したパスワードを入力します。すると、コンテナ内の GUI が表示されます。

👆 はxcalcを実行してみた例です。コンテナ内のターミナルから実行しても問題なく表示されています。

ブラウザだけで GUI が使えるのは便利です。
音声について
この IssueよるとNoVNC は音声もサポートしている or その実装があるっぽいですが、そもそも VNC 側で対応しているサーバが少ないようです。
音声だけは上のWSLgでやったようにPULSE_SERVERで送るのがいいかもしれませんが、今回は検証していないので実際にできるかどうかわかりません。
終わりに
WSLgを使った例は Mac を使っている方にはできないので、そういう意味ではVNCを使ったほうが良いのかもしれませんが、
パフォーマンス的なあれそれがちょっと気になります。そもそも、Docker と GUI の相性があまり良くないのかもしれません。
参考
これは以下の素晴らしい記事を参考に作成されました。