k3s上でxpra + OpenboxのGUI Podを動かす設計

k3s上でxpra + OpenboxのGUI Podを動かす設計

k3s 上で一時的な GUI desktop を動かしたい場合、xpra start-desktop と Openbox の組み合わせは小さく始めやすい。

目的は「Kubernetes 上に恒久的な VDI を作る」ではなく、検証用の軽量 desktop を 1 Pod で起動し、browser から操作できるようにすることである。

最小構成

flowchart LR
  Browser["Browser"]
  Ingress["Ingress or port-forward"]
  Service["Service :10000"]
  Pod["Pod"]
  Xpra["xpra server + HTML5 client"]
  Openbox["Openbox session"]
  Tmp["emptyDir runtime dirs"]

  Browser --> Ingress --> Service --> Pod
  Pod --> Xpra --> Openbox
  Pod --> Tmp

最初の PoC は次の範囲に絞る。

  • 1 namespace
  • 1 Deployment
  • 1 ClusterIP Service
  • HTML5 client
  • Openbox session
  • /tmp/var/tmp/run/user/<uid>emptyDir
  • audio / GPU / IME は後回し

この範囲なら、resource と security の切り分けがしやすい。

xpraの起動例

distro や xpra version によって option は調整が必要だが、考え方は次のようになる。

xpra start-desktop :100 \
  --bind-tcp=0.0.0.0:10000 \
  --html=on \
  --mdns=no \
  --notifications=no \
  --webcam=no \
  --printing=no \
  --pulseaudio=no \
  --start=openbox \
  --no-daemon

不要な機能は最初から切る。特に audio、webcam、printing は container desktop の初期 PoC では複雑さの割に得るものが少ない。

image方針

最初は自前 image が扱いやすい。

理由:

  • xpra、Openbox、Xvfb/Xdummy、font、dbus の依存を把握できる
  • root 実行や password 埋め込みの既存 image を避けられる
  • arm64 など target architecture を明示できる
  • 不要 service を削れる

Debian / Ubuntu 系 image は GUI stack の package が揃いやすい。Gentoo image で揃えることもできるが、PoC では build 時間が長くなり判断が遅れる。

永続化の分け方

GUI Pod で最初に壊れやすいのは writable layer と cache である。

パス推奨
/tmpemptyDir
/var/tmpemptyDir
/run/user/<uid>emptyDir or memory-backed emptyDir
browser cacheemptyDir
user profile必要になってから PVC
downloads必要になってから PVC

NFS PVC に browser cache を置くと、体感が悪くなることがある。永続化すべきものは config、profile、downloaded data であり、cache ではない。

resource設計

軽量 Openbox desktop でも、GUI image は通常の CLI Pod より大きい。

最初は控えめに始め、実測で増やす。

resources:
  requests:
    cpu: 250m
    memory: 512Mi
  limits:
    memory: 2Gi

containerd の snapshot 展開先が小さい node では、image size と unpack 後サイズも見る。特に k3s の native snapshotter では、GUI 系 image の展開後サイズが効きやすい。

securityContext

初期値は non-root に寄せる。

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  runAsGroup: 1000
  allowPrivilegeEscalation: false

readOnlyRootFilesystem: true は理想だが、xpra / X11 runtime の書き込み先をすべて tmpfs/PVC に逃がすまでは無理に有効化しなくてよい。

公開経路

初期 PoC は kubectl port-forward が一番安全である。

kubectl -n desktop-lab port-forward svc/xpra-openbox 10000:10000

Ingress 化する場合は、少なくとも次を入れる。

  • TLS
  • Basic Auth または xpra password auth
  • source CIDR allowlist
  • WebSocket 用 timeout
  • NetworkPolicy

Ingress 側 Basic Auth は xpra image を変えずに前段で認証を置ける。家庭内検証では十分なことが多い。

複数 Web UI を同じ認証基盤で守りたい段階になったら、OIDC + oauth2-proxy などを検討する。

audioとGPUは後回し

audio forwarding は xpra で可能だが、PulseAudio / PipeWire / container device / permission の論点が増える。

GPU も同じで、DRM device、Mesa、権限、host library との整合が必要になる。

最初は software rendering で良い。Openbox と軽量 X11 app の PoC では、画面、keyboard、clipboard、reconnect を先に確認する。

よくある警告

xpra image を小さくすると、optional module の警告が出る。

警告意味初期 PoC での扱い
paramiko 不足SSH socket upgrade 用不要なら無視
uinput 不足pointer emulationXTest fallback でよいことが多い
python-xdg 不足desktop menu / application metadata必要になったら追加
dbus helper 不足xdg-open や tray 系GUI app を増やす段階で検討
/run/xpra 作成不可system-wide socket diruser socket に寄せる

警告を全部消すより、使う機能に必要なものだけ足す方が image を保ちやすい。

GUI Podを重くしないコツ

  • desktop environment ではなく window manager から始める
  • browser を入れる前に xterm などで xpra の安定性を見る
  • audio / webcam / printing / mDNS を切る
  • cache は永続 PVC に置かない
  • image は digest で検証し、tag cache による取り違えを避ける
  • nodeSelector / taint で重い GUI workload の配置先を限定する

GUI Pod は便利だが、cluster の基盤 workload と同じ node に無制限で置くと調査が難しくなる。検証用 namespace と配置制約を分けるのが重要である。