🍱

WSLgとdocker composeで全部やる

2022/01/24に公開

概要

メイン機の一台をLinuxからWindowに乗り換えたくなったので、簡単に移行できて、簡単に戻れるように、WSLgとdocker composeを使っていろいろなGUIアプリをコンテナ上で実行する環境構築をしました。docker-composeファイルだけでありとあらゆることをしたいのです。 そのうえで調べたことをまとめます。コンフィグ生成のSPAを作りましたので、GUIアプリコンテナの設定だけ知りたい方はDesktop Composeへ。

前提

  • Windows11
  • WSLg
  • Ubuntu

WSLgの下調べ

X Server

ys@laptop ~> echo $DISPLAY
:0
ys@laptop ~> ls -lh /tmp/.X11-unix/X0
srwxrwxrwx 1 ys users 0 Jan 23 15:00 /tmp/.X11-unix/X0=

X Serverのunix socketは普通に/tmp/.X11-unix/以下にあります。

ys@laptop ~> env | grep XAUTHORITY
ys@laptop ~ [0|1]> xauth list
xauth:  file /home/ys/.Xauthority does not exist

認証のためのXAUTHORITYはありません。

ys@laptop ~> xhost
access control disabled, clients can connect from any host
SI:localuser:wslg

デフォルトで無認証となっています。

Wayland

ys@laptop ~> env | egrep "(XDG_RUNTIME_DIR|WAYLAND_DISPLAY)"
WAYLAND_DISPLAY=wayland-0
XDG_RUNTIME_DIR=/mnt/wslg/runtime-dir
ys@laptop ~> ls -lh $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY
srwxrwxrwx 1 ys users 0 Jan 23 15:00 /mnt/wslg/runtime-dir/wayland-0=

Waylandのソケットは、/mnt/wslg/runtime-dir/wayland-0にあります。

PulseAudio

ys@laptop ~> env | egrep "(PULSE_SERVER|PULSE_COOKIE)"
PULSE_SERVER=/mnt/wslg/PulseServer

Pulse Audioのソケットは、/mnt/wslg/PulseServerにあります。
こちらもCOOKIEはなく、無認証です。

User session dbus

WSLgではsystemdが動いていないので、user session busは使えません。

各リソースへのアクセスは無認証なので、比較的シンプルにコンテナ上のGUIアプリからアクセスできます。

Docker Rootlessのインストール

Rootfull Dockerでも実現できますが、安心して使えるようにWSLgのDocker Rootlessをインストールし、docker composeを導入します。

sudo apt update
sudo apt upgrade -y
sudo apt install -y uidmap fuse-overlayfs
export SKIP_IPTABLES=1; curl -fsSL https://get.docker.com/rootless | sh

fuse-overlayfsをインストールしないと、docker createのたびにイメージをフルコピーするので、数十秒かかってしまいます。
自動起動するように~/.bash_profile~/.bashrc~/.bash_logoutに以下を追加します。

~/.bash_profile
# DOCKER ROOTLESS SETTING
export PATH=$HOME/bin:$PATH
export DOCKER_HOST=unix:///mnt/wslg/runtime-dir/docker.sock
~/.bashrc
# DOCKER ROOTLESS LAUNCH
dockerds=`ps -ef | grep "dockerd-rootless.sh"| grep -v grep | wc -l`
if [ $dockerds = 0 ]; then
  rm $XDG_RUNTIME_DIR/docker.sock $XDG_RUNTIME_DIR/docker.pid
  rm -rf $XDG_RUNTIME_DIR/docker
  dockerd-rootless.sh --iptables=false &
fi
~/.bash_logout
# TERMINATE ROOTLESS DOCKER
DOCKER_PID_FILE=${XDG_RUNTIME_DIR}/docker.pid
if [ -e $DOCKER_PID_FILE ]; then
  kill `cat $DOCKER_PID_FILE`
fi

これでUbuntuのterminalを起動すれば、Docker Rootlessが立ち上がります。

Docker Compose導入

mkdir -p ~/.docker/cli-plugins/
curl -SL https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
docker compose version

アプリ準備

ベースとなる下記を保存してください。

local-compose.yaml
version: "3.9"

services:

  ubuntubase:
    build: https://github.com/ysuito/desktop-compose.git#master:src/ubuntubase_ja
    image: ubuntubase
    container_name: ubuntubase

Desktop Composeでアプリのconfigを生成して、local-compose.yamlに追加してください。

local-compose.yaml
version: "3.9"

services:

  ubuntubase:
    build: https://github.com/ysuito/desktop-compose.git#master:src/ubuntubase_ja
    image: ubuntubase
    container_name: ubuntubase

  gedit:
    working_dir: /home/user
    environment:
      - UID=1000
      - GID=1000
      - DISPLAY=:101
      - WAYLAND_DISPLAY
      - XDG_RUNTIME_DIR=/tmp
    volumes:
      - type: bind
        source: /tmp/.X11-unix/X0
        target: /tmp/.X11-unix/X101
      - type: bind
        source: "${XDG_RUNTIME_DIR}/wayland-0"
        target: /tmp/wayland-0
    cap_add:
      - CHOWN
      - SETUID
      - SETGID
      - DAC_OVERRIDE
    cap_drop:
      - ALL
    command:
      - bash
      - -c
      - "groupadd -g $${GID} user && useradd -s /bin/bash -u $${UID} -g user user && chown -R user:user /home/user; su user -c \"gedit\""
    network_mode: host
    build: https://github.com/ysuito/desktop-compose.git#master:src/gedit
    image: gedit
    container_name: gedit
    depends_on:
      - ubuntubase

解説

イメージの依存関係

    depends_on:
      - ubuntubase

depends_onにビルド時の依存イメージを追加すると簡易的な依存関係を記述できます。

白いウィンドウ枠への対処


X Serverを差して起動するとウィンドウ枠が白くなります。ダークテーマを利用していると非常に気になるので、極力native waylandで起動するようにします。

    environment:
      - WAYLAND_DISPLAY
      - XDG_RUNTIME_DIR=/tmp
    volumes:
      - type: bind
        source: "${XDG_RUNTIME_DIR}/wayland-0"
        target: /tmp/wayland-0

Waylandソケットを共有して、2つの環境変数で接続先を伝えます。
さらにアプリ側にも、下記のように接続方法を指示します。

  • Firefoxの場合はMOZ_ENABLE_WAYLAND=1
  • QTの場合はQT_QPA_PLATFORM=wayland
  • GTKの場合はGTK_SESSION_TYPE=wayland
  • Chrome, Electron系はコマンド引数を指定--enable-features=UseOzonePlatform --ozone-platform=wayland

Build

docker build --network=host -t gedit https://github.com/ysuito/desktop-compose.git#master:src/gedit

Run

ステートレスに起動したい場合は

docker compose -f local-compose.yaml run --rm -d IMAGE

ステートフルに起動したい場合は

docker compose -f local-compose.yaml up -d IMAGE

Update

docker build --no-cache --network=host -t gedit https://github.com/ysuito/desktop-compose.git#master:src/gedit

Windows側からのショートカット

C:\Users\%username%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\desktop-composeに下記内容でショートカットを作成すれば簡単に起動できます。

C:\Windows\System32\wslg.exe ~ -d Ubuntu bash -c "docker compose -f local-compose.yml run --rm -d gedit"

Custom App

画面共有設定や音声共有設定を取り除いた、アプリ特有の設定の基本部分は下記のように簡潔です。

  gedit:
    build: https://github.com/ysuito/desktop-compose.git#master:src/gedit
    image: gedit
    container_name: gedit
    depends_on:
      - ubuntubase
    command: ["su user -c \"gedit\""]

build contextとして、ローカルディレクトリ、Githubリポジトリを指定でき、imageとしてDockerHubのイメージを指定できます。サンプル:geditのDockerfile

リモートリソースを使う

xpraを動かすコンテナ追加することでリモート上で実行するアプリの画面をローカルに転送することができます。詳細はこちらをご覧ください。

リモートホストとのファイル同期

複数の端末でファイル同期したい場合は、Syncthingコンテナを追加することで実現できます。

  syncthing:
    image: syncthing/syncthing
    container_name: syncthing
    hostname: HOSTNAME
    ports:
      - 127.0.0.1:8384:8384
      - target: 22000
        published: 22000
        protocol: tcp
        mode: host
      - target: 22000
        published: 22000
        protocol: udp
        mode: host
    volumes:
      - type: bind
        source: "${HOME}"
        target: /var/syncthing
    network_mode: host
    environment:
      - PUID=0
      - PGID=0

あとは、bashrcなどで起動設定等を追加してください。

~/.bashrc
# SYNCTHING LAUNCH
syncthings=`docker ps | grep "syncthing"| wc -l`
if [ $syncthings = 0 ]; then
  (sleep 3 && docker compose -f ~/local-compose.yml run --rm -d syncthing) &
fi

その他

sandbox化

怪しいサイトを見たりする専用ブラウザを作りたければ、UID/GIDを2000など他のアプリが利用していないものにすれば、仮にそのコンテナが破られても、他のコンテナのリソースやホストのリソースにはアクセスできないので、安心感が増します。ただ、Xサーバなどは共有しているので、画面のぞき見やクリップボード盗聴などの可能性は残ります。WSLg System Distroは各User Distro毎に立ち上がるようなので、専用Distroを用意すれば完璧な対策になるかもしれません。

制限

  • WSLg上のGUIコンテナアプリに日本語入力ができない(/tmp/fcitx-socket-:0とか共有したりいろいろやりましたがダメでした。WSLg側で対処されることを期待)。
  • リモートでの音声が利用できない。

WSLgのGPU利用について

今回検証に使ったPCがIntel第五世代CPUだったので、vGPU化のドライバが導入できず無理やり動かしているので、GUIアプリ表示時はCPU使用率がネイティブ起動より2~3倍高くなります。最新のPCならGPUアクセラレーションが効いてよりストレスなく動くと思いますが、この点は検証できていません。

まとめ

一部若干制限はありますが、WSLgとdocker composeで大方全部できそうです。WSLgの発展を切望しております。ホスト環境を汚したくない方や、WindowsとLinuxを横断的に利用したい方は参考にしてみてください。

参考

https://zenn.dev/sardonyx/articles/9726126c4a67ffb66509
https://k-hyoda.hatenablog.com/entry/2020/09/20/235346
https://ja.stackoverflow.com/questions/69486/ubuntu18-04-上のdockerコンテナに入れたqtcreatorでibusによる日本語入力ができない

Discussion