2018年7月23日 星期一

在 docker 裡面跑 GUI 程式

一開始玩 docker 主要都是為了伺服器的應用 (例如 ethercalc ldap), 所以只需要文字介面。 最近玩 ML/DL/AI 程式, 經常需要顯示圖片。 每次都要在 docker 裡面用 AI 程式在分享的目錄裡產生圖片, 再從外面 (host) 用看圖軟體看, 有點囉嗦。 如果是影片, 就更麻煩了。 搜尋到這篇 Running GUI apps with Docker, 略微修改他的做法, 得到以下簡單步驟讓你可以在 docker 裡面執行圖形介面程式並直接顯示在 host 實體機的 X Window 環境。

我們拿 ubuntu 18.04 的官方 docker 及輕巧的 feh xli 看圖程式來做實驗。

  1. 查看你自己的 user name 跟 UID: echo $UID $USER 例如我看到 1000 ckhung。
  2. 啟動 docker: docker run -it --name xwin -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v /home/ckhung/data:/tmp/exdata ubuntu:18.04 bash 這裡有兩個重點: (1) 把 host 裡的 DISPLAY 環境變數傳進 docker 裡面。 (2) 讓 docker 直接存取 host 的 /tmp/.X11-unix (裡面有一個 X11 在用的 socket)。 至於其他選項, 因為我們要進 bash, 所以要用 -it; 第二組 -v 則是為了分享一般的檔案。
  3. 進入 docker 之後, 安裝一個測試用的 GUI 軟體, 例如 apt update ; apt install xli
  4. 照外面的 $USER 跟 $UID, 在 docker 裡面也建立一個同名同代號的 user: useradd -m -s /bin/bash -u 1000 ckhung
  5. 變身成新的 user: su ckhung
  6. 如果你在 host 的 /home/ckhung/data 有放一些圖片, 那麼從 docker 裡下 xli /tmp/exdata 就可以看到這些圖片了! 在 xli 裡面, 按 q 離開、 按空格換下一張圖片。 其他按鍵請見 man xli。

實際上執行時, 我看到 X Error of failed request: BadValue (integer parameter out of range for operation) Major opcode of failed request: 130 (MIT-SHM) Minor opcode of failed request: 3 (X_ShmPutImage) 之類的錯誤。 根據 這個 issue, 砍掉 docker, 重新啟動時加上 --ipc=host 選項, 才成功。 不過這是比較危險的方法, 只適用於你信任那個 docker 映像檔的情況, 所以我沒直接寫進上面 docker run ... 那一句。

安裝 vlc 播放影片也 ok, 只是沒有聲音。 又, 後來發現我要用的那個 docker 的 opencv 並沒有把 GUI 編譯進去, 所以還是沒辦法看影片。 沒關係, 已前進一小步, 今天先這樣就滿足了。

* * * [2019/9/9 補充] * * *

今天在另一部電腦上用同樣的步驟, 卻出現 No protocol specified 的錯誤訊息。 直到讀到 這一頁 ksylvan 的 "SUPER useful" 回應, 才想起來自己以前曾寫過的這篇: 解釋 xauth 指令跟 XAUTHORITY 環境變數: 原來是 (原始登入用戶內的) X Window 的保護機制 -- 禁止別人讀寫我的桌面。 總之呢, 如果這部電腦的 IP 不是公開的, 而且只有你自己一個人在用, 那麼可以不理會安全問題, 直接 (在剛登入的那個帳號) 下 xhost + 允許任何人讀寫你的桌面視窗環境, 然後才變身成 root、 執行 docker, 那就不會有上述錯誤訊息。 比較正確且安全的做法是:

  1. 維持 xhost - 禁止閒雜人等讀寫我的桌面。
  2. [好像不需要這一步? 允許 root 讀我的 .Xauthority : setfacl -m user:0:r ${HOME}/.Xauthority]
  3. 啟動 docker 的命令列上, 補上 --net=host -e XAUTHORITY=/tmp/xauth -v ~/.Xauthority:/tmp/xauth 叫你的 container 去讀原始登入帳號的 .Xauthority 裡面的通關密語 (magic cookie)。

完整的說明請見超讚文章 Running X11 applications like xeyes in Docker

我在 lubuntu 18.04 上面安裝太多套件, 不知道哪個東西跟 inkscape 衝突, 過去一年多來 inkscape 很容易當掉, 每次都要用 kvm 開舊版的 lubuntu 來用。 這兩天才突然想到何不用 docker 專門跑一個舊版的 ubuntu 就好? 裡面裝了 inkscape 與 sozi , 把視窗丟到 host 來操作。 無法輸入中文, 但沒關係, 可以從 host 別的視窗複製中文貼進 inkscape, 或是畫圖時先寫英文, 畫完之後再用 vim (或 geany 或 nano) 去改文字就好。 於是用 ubuntu 18.04 + inkscape + sozi 做了一個 docker image ckhung/inkscape。 可以這樣用: docker run -it --name inkscape -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v /home/ckhung/public_html/mm:/home/ckhung/public_html/mm --ipc=host ckhung/inkscape:19a bash 進入後 useradd -m -s /bin/bash -u 1000 ckhung 並且 chown ckhung /home/ckhung /home/ckhung/public_html 然後就可以 su ckhung 用 docker 裡面的 ckhung 叫出 inkscape 。 以上, ckhung、 1000、 各路徑當然都要改成你自己的值。 離開 docker 之後要再重新進入, 只需要 docker start -ai inkscape

[2020/6/1] 逛到一個很熱門的 stack overflow 問答: Can you run GUI applications in a Docker container?

1 則留言:

  1. 補充 "No protocol specified" 的處理方式 (xauth 相關)、
    上傳一個 inkscape + sozi 的映像檔到 docker hub;
    舊文部份改用更輕巧的 xli 取代 feh。

    回覆刪除

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。