かみのメモ

コンピュータビジョン・プログラムな話題中心の勉強メモ

Docker上のGUIアプリをホスト/リモートで描画する

今回は、リモートUbuntuサーバーのDockerコンテナ上で起動しているGUIアプリの画面を、ホスト自身ローカルのMac/Windowsのディスプレイに表示させる方法についてまとめてみます。

筆者は環境構築を楽に済ませるためにDockerを利用することが多いのですが、デフォルトのDockerコンテナではGUIを表示させることができないので、画像の確認やGUIアプリが付属しているOSSの動作確認をするときに不便です。

そこでコンテナ内でX11対応のアプリを起動し、それをローカル側にforwardすることでGUIを表示させてみます。

※ 2020/06/02 : 申し訳ないことにX11の仕様を誤解していました。解説の中でクライアント/サーバーが逆になっていた部分を修正しました。

【2021/02/07 追記】
今回紹介するX11 Forwardingは、手軽に使える反面、複数のウィンドウが立ち上がるアプリを使うときに不便なのと、遅延が大きいのがネックになります。 「コンテナの容量と計算負荷は増えていいから、本格的なデスクトップ環境を使いたい」という方は、以下の記事で紹介するVNCによるリモートデスクトップの方がニーズに合っているかもしれません。

kamino.hatenablog.com

もくじ

スポンサーリンク

1. 環境

Ubuntuサーバーの上でDockerコンテナを立て、Mac/WindowsからSSHログインしてdocker exec -it xxxx bashしている状況を想定します。

Host : Ubuntu 18.04
  Docker Community : 19.03.2

Local Mac : macOS Mojave 10.14.6
  XQuartz 2.7.11

Local Windows : 
  Tera Term 4.104
  VcXsrv 1.20.5.1

2. X Window Systemとは

最初に、今回利用するX Window Systemについて軽くおさらいしておきます。

X Window SystemはOSがGUIを提供するために利用するウィンドウシステムの一種です。 1987年以降、バージョン11が安定版となっているのでX11と呼ばれることもあります。

描画の原理としては、

  1. GUIを表示するマシン上でX11サーバーを立ち上げる
  2. GUIを提供するアプリがX11クライアントを立ち上げ、X11サーバーと接続する
  3. サーバーとクライアントが通信しながら描画・キーボード/マウス入力を処理する

という流れになっています。 サーバーとクライアントはネットワーク越しに通信できるため、別のコンピュータで動作するGUIアプリを手元のコンピュータで描画することもできます。 このことをX11 Forwardingと言います。

X11Unix系OSにおける標準的なウィンドウシステムとして採用されています。 またWindowsMacOSは独自のウィンドウシステムを実装しているのですが、X11規格のGUIを描画するためのソフトウェアも提供されているため、環境を整えればX11GUIを扱うことができます。

3. Docker上のGUIアプリを描画する方法

3.1. Macから接続する場合

  1. XQuartzをインストールする(公式サイト)
    • XQuartzはMac上で動作するX11サーバーアプリ
  2. ターミナルからssh -Y xxxx@xxx.xxx.xxx.xxxUbuntuに接続する
    • -YX11 Forwardingを有効にするためのオプション
  3. -e DISPLAY=$DISPLAY --net host -v /tmp/.X11-unix:/tmp/.X11-unix -v $HOME/.Xauthority:/root/.Xauthorityのオプションを付けてDockerコンテナを作成する
    • -e DISPLAY=$DISPLAY : GUIを転送するX11サーバーのアドレスを設定
    • --net host : コンテナ上のX11クライアントがサーバーにアクセスできるよう、コンテナのネットワーク設定をホストと共有する
    • -v /tmp/.X11-unix:/tmp/.X11-unix : X11で利用するUNIXドメインソケットをホストと共有する

以上で設定完了です。

あとはコンテナ内でX11を利用するGUIアプリを起動させると勝手にXQuartzが起動し、手元でGUIが描画されます。

一番手軽に試せるのはapt install x11-appsでインストールできるテストアプリxeyes, xclock, xcalc, etcです。

f:id:kamino-dev:20191022125546p:plain:w400
xcalcを実行した様子

OpenCVをインストールしてimshowしてみるのもアリです。

f:id:kamino-dev:20191022124915j:plain:w700
MacでリモートのGUIを表示した様子

またDockerコンテナを立ち上げた後にSSH接続するとXサーバーとクライアントの通信に失敗します。 具体的にはX11のアプリを立ち上げようとした段階でX11 connection rejected because of wrong authentication.というエラーが出ます。 はっきりした原因はわかっていないのですが、おそらくクライアントがサーバーの認証情報をうまく拾えていないのだと思います。 新しくSSH接続した後でX11 Forwardingする場合は、対象のコンテナを一度docker restartさせる必要があります。

3.2. Windowsから接続する場合

  1. VcXsrvをインストールする(公式サイト)
    • VcXsrvはWindowsで動作するX11サーバーアプリ
    • インストールオプションはすべてデフォルトのままでOK
    • 他にもいくつかのサーバーアプリが存在するのでそちらを使っても良い
  2. Tera Termをインストールする(公式サイト)
    • 言わずと知れたWindows向けターミナルソフト、X11 Forwardingにも対応している
    • インストールオプションはすべてデフォルトのままでOK
  3. Tera Termを起動させ Setup > SSH Forwarding > Display remote X applications on local X server にチェックを入れた上で Setup > Save setup からTeraterm.iniに現在の設定を上書き保存する
  4. VcXsrvを起動させる
    • デスクトップにショートカットが作成されているはずなのでそれをダブルクリック、すべての設定をデフォルトのままNextをクリックしていけばOK
    • クライアントが起動すると画面右下にVcXsrvのアイコンが表示されるようになる
  5. Tera Termを再起動させUbuntuSSHログイン

あとはMacと同じ要領でDockerコンテナを作成し、コンテナ内でGUIアプリを起動するだけです。

3.3. ホストで表示する場合

Dockerを起動しているUbuntuのデスクトップに表示させるのは結構簡単です。

  1. ターミナルでxhost +local:dockerを実行する

これだけです。

あとはMacと同じ要領でDockerコンテナを作成し、コンテナ内でGUIアプリを起動すればOKです。

4. 後から表示先を変えたい場合

X11クライアントは環境変数DISPLAYに設定されたアドレスのサーバーに画面をForwardするので、この環境変数を変更すれば表示先を切り替えることができます

UbuntuサーバーにSSHログインした直後にecho $DISPLAYを実行すると:1, localhost:10.0などの文字列が表示されます。 これはディスプレイのアドレスを表しており<IP>:<ディスプレイ番号>.<スクリーン番号>を基本として逐次省略形が使われています。

ここで表示された文字列を覚えておき、docker exec -it xxxx bashした後でコンテナ内でDISPLAY=localhost:10.0のように変数を上書きすることで、Forwardするサーバーを変更できます。


以上、Dockerコンテナ内のGUIアプリを描画する方法をまとめました。

今回はLAN内のサーバーに接続することを前提にしていましたが、LAN外のコンピュータに接続する場合は適宜ファイアウォールを調整すべきですね。