Docker運行GUI軟件的方法


轉自 https://www.csdn.net/article/2015-07-30/2825340

簡介:
Docker通過namespace將容器與主機上的網絡和運行環境進行了隔離,默認情況下,在容器中運行帶界面的軟件在外部是看不到的。在這個分享中,將介紹通過共享X11套接字讓外部主機顯示容器中運行的程序界面的方法。並討論在『運行本地的GUI程序』和『運行遠程服務器上的GUI程序』兩種場景的下的實現原理。

下文是本次的分享整理:
Docker比較常用的場景是『運行無界面的后台服務』或者『運行基於的Web服務』。不過有時出於個人的喜好或特定的需求,我們會希望在Docker中運行帶圖形界面的應用程序。

比如,在今年的『Docker全球開發者大會』上,Docker自家的美女程序員『傑西·弗萊澤爾(Jessie Frazelle)』展示了一系列黑魔法一樣的鏡像。這些鏡像中的大多數都使用了圖形界面。

DaoCloud的孫宏亮在現場通過博客直播了她的演講。看到這張照片很多人應該已經認出她了。

Jessie在自己的博客里介紹這些鏡像時說,她十分欣賞蘋果的Mac電腦中每個應用程序使用獨立沙盒中運行的做法,這樣避免了應用程序將配置文件和運行過程中生成的臨時文件散亂的丟在系統各種目錄中。Jessie現在的工作環境主要是Debian系統,出於這種喜好,她將自己常用的各種軟件統統使用Docker容器化了。

將容器中的圖形界面展示到外部的一般性思路。
目前Unix/Linux比較主流的圖形界面服務是X11,而X11服務的圖形顯示方式實際上是一種Client/Server模式,在服務端和客戶端之間,X11通過『DISPLAY』環境變量來指定將圖形顯示到何處。如下面的流程所示,請注意服務端與客戶端的位置,服務端是用於提供顯示信息的。

[應用程序]->[X11客戶端]->[X11服務端]->[顯示屏幕]

DISPLAY的格式是『unix:端口』或『主機名:端口』,前一種格式表示使用本地的unix套接字,后一種表示使用tcp套接字。

默認情況下,X11的服務端會監聽本地的『unit:0』端口,而DISPLAY的默認值為『:0』,這實際上是『unit:0』的簡寫。因此如果在Linux的控制台啟動一個圖形程序,它就會出現在當前主機的顯示屏幕中。

基於這個原理,將Docker中的GUI程序顯示到外面,就是通過某種方式把X11的客戶端的內容從容器里面傳遞出來。基本的思路無非有兩種:

通過SSH連接或遠程控制軟件,最終通過tcp套接字將數據發送出來
讓容器和主機共享X11的unix套接字,直接將數據發送出來
從應用場景上划分,又可以分成兩類情況:『運行本地的GUI程序』和『運行遠程服務器上的GUI程序』。這兩類情況在操作上很相似,但前者可以使用unix套接字,而后者必然要使用tcp套接字轉發,原理上有很大差別。先說本地運行GUI程序的情況。

以Jessie在Docker開發者大會上做的第一個演示『LibreOffice』為例。這個鏡像的Dockerfile代碼和使用方法都已經開源在Github上了。

不知道有多少人實際測試過Jessie在博客或者Docker開發者大會上用過的例子,我相信其中應該有些人會發現,直接運行這些例子是行不通的。下面是我的運行環境:

$ cat lsb-release

DISTRIB_ID=Ubuntu

DISTRIB_RELEASE=14.04

DISTRIB_CODENAME=trusty

DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"

$ docker --version

Docker version 1.7.1, build 786b29d

這是一個全新的Ubuntu系統,僅僅添加了Docker等基本的軟件。

在LibreOffice的Dockerfile注釋里提供了運行方法:

$ docker run -d \

-v /etc/localtime:/etc/localtime:ro \

-v /tmp/.X11-unix:/tmp/.X11-unix \

-e DISPLAY=unix$DISPLAY \

-v $HOME/slides:/root/slides \

-e GDK_SCALE \

-e GDK_DPI_SCALE \

--name libreoffice \

jess/libreoffice
其中的『-v /tmp/.X11-unix:/tmp/.X11-unix』參數就是將主機上X11的unix套接字共享到了容器里面。因為每個unix套接字實際上就是系統/tmp/.X11-unix目錄下面依據套接字編號命名的一個特殊文件。

命令執行完,LibreOffice並沒有啟動。

好在剛剛已經說過這茬,所以還不算太意外。看一下日志:

$ docker logs libreoffice

No protocol specified

Failed to open display

javaldx: Could not find a Java Runtime Environment!

Warning: failed to read path from javaldx

No protocol specified

No protocol specified

No protocol specified

No protocol specified
這是由於X11服務默認只允許『來自本地的用戶』啟動的圖形程序將圖形顯示在當前屏幕上。對於Jessie的運行環境,她應該的已經修改了這個設置,但並沒有在博客中提及。對於大多數的Linux用戶來說,直接運行博客中的命令,都應該會遇到這個問題。

解決的辦法很簡單,允許所有用戶訪問X11服務即可。這個事情可以用xhost命令完成。

$ sudo apt-get install x11-xserver-utils

$ xhost +
參數『+』表示允許任意來源的用戶。

現在再次運行前面的docker run命令,就會看到LibreOffice啟動起來了,速度相當快。由於是直接共享了X11的unix套接字,在效率上與運行安裝在主機上的程序基本沒有差異。

在遠程服務器上用Docker運行GUI程序的情況。
這種情況多出現在將Docker作為產品測試環境使用的場景。利用Docker用后既消除的特點,能夠快速的為每次測試提供干凈的上下文環境。有時為了在非Linux系統中使用Linux的圖形化軟件,也可以通過遠程Docker運行的方法實現。

此時,整個數據連接實際就變成了這樣的:

[應用程序]->[X11客戶端]->[SSH服務端]->[SSH客戶端]->[X11服務端]->[顯示屏幕]

這種情況實際上已經演化成為了通過tcp套接字轉發的X11連接,只不過用戶並沒有直接使用SSH連接到容器里面的tcp端口上,而是連接到了遠程主機。相應的X11數據先從容器傳遞到了主機,再通過SSH通過傳遞到了用戶的電腦上。

這就必須有要求用於展示的用后電腦安裝有X11服務,大多數的Linux系統默認就具備了,Mac系統可以安裝XQuartz軟件,Windows則可以使用Xming等第三方X11服務端實現。首先將本地的X11服務運行起來。

其次,當用戶使用SSH連接運行程序的服務器時,應該開啟SSH的『X11-Forwarding』功能。具體來說,有兩個注意點。

1)檢測服務器上的/etc/ssh/sshd_config文件,是否已經有『X11Forwarding yes』這樣的配置,如果沒有則需要加上。

2)當連接到服務器時,應該在ssh命令后面加上-X參數,表示使用X11-Forwarding特性。

$ ssh -X @
登陸上去后運行剛才的docker run命令,並不能看到LibreOffice運行起來的跡象。通過log發現錯誤還是『Failed to open display』。在前面已經說過,對於遠程連接運行GUI的情況,必然要換成tcp套接字的X11轉發方式。而命令中的『-v /tmp/.X11-unix:/tmp/.X11-unix』參數僅僅是共享了unix套接字。那么怎樣才能換成tcp套接字呢?需要修改兩個地方:

1)首先為容器里面設置的環境變量『DISPLAY』,不能是『unix$DISPLAY』了

2)其次共享unix套接字可以不必了,而是要用『--net=host』讓容器內的網絡環境與主機共享

DISPLAY改成什么呢?首先要看SSH登陸后得到的系統DISPLAY變量值,我這里看到的是『localhost:10.0』,主機的localhost:10.0到了容器里面就要變成0.0.0.0:10.0。原因不解釋了,這個是Docker默認添加的映射。

因此DISPLAY的值應該賦予『0.0.0.0:10.0』。可以簡寫為『:10.0』,X11會先去找unix:10.0,發現沒有那個套接字文件,然后就會去試0.0.0.0:10.0。結果是一樣的。修改過后的啟動命令變成了:

$ docker run -d \

-v /etc/localtime:/etc/localtime:ro \

--net=host \

-e DISPLAY=:10.0 \

-v $HOME/slides:/root/slides \

-e GDK_SCALE \

-e GDK_DPI_SCALE \

--name libreoffice \

jess/libreoffice
再次運行這個鏡像,然而,依舊沒有看到LibreOffice。查看Docker logs,這次的錯誤信息是:

『X11 connection rejected because of wrong authentication』。

這是因為在使用SSH通道轉發X11時,會生成一個隨機的授權cookie,存放在服務器的Xauthority文件中。這個cookie會在每次X11客戶端發送數據時被用到。我們使用了『--net=host』參數后,容器中的X11客戶端將直接通過tcp套接字與外部通信,然而容器里面並沒有這個授權文件。因此我需要加上參數『-v $HOME/.Xauthority:/root/.Xauthority』把授權文件也放到容器里面。此外,啟動命令中的兩個GDK開頭的環境變量在服務器端的Ubuntu上是不存在的,因此也可以刪掉。

現在我們得到了最終的啟動命令:

$ docker run -d \

-v /etc/localtime:/etc/localtime:ro \

--net=host \

-e DISPLAY=:10.0 \

-v $HOME/slides:/root/slides \

-v $HOME/.Xauthority:/root/.Xauthority \

--name libreoffice \

jess/libreoffice
執行這個命令后,就看到LibreOffice已經在本地電腦的顯示器上運行起來啦!

這個在Mac上看到的LibreOffice,程序本身運行在遠程服務器的Docker里面。同樣的方法也可以適應於Jessie的其他鏡像。

Q&A:
問題1. X11是什么,與KDE有什么區別?

林帆:X11是Linux下面的界面顯示服務。KDE是一種窗口管理軟件,是具體的界面,X11是在更下面一層的協議層。

問題2. 在服務端運行GUI鏡像時會收到網絡的影響畫面卡頓嗎?

林帆:這個和網速關系比較大。

問題3. 通過這種gui方式,是不是可以做docker桌面雲了?

林帆:不算是,因為這種做法需要SSH登錄,其實有點不安全.

問題4. 可以多用戶連接同一docker image不?就像remote desktop service一樣?

林帆:用這種方式不能,SSH的X-Forwarding是單點的。

問題5. 可以考慮用xvfb嗎?

林帆:原理上是可以的,不過這樣就看不到運行的界面了。

問題6. 理論上講,只要配置合理正確並且GUI支持X11,這種方式可以運行大部分linux下的gui應用?

林帆:是的,對於應用程序本身感覺不到圖像是被現實到了本地還是遠程的屏幕上

問題7. 請問在docker上運行的gui應用,應用間的互操作性如何保障?x11協議應該只能轉發顯示數據,無法轉發實際數據(如電子表格中的數據,用以粘貼到其他打開的文檔文件中),是否有其他協議可以保證互操作性?

林帆:目前看來互操作的話只能用其他協議代替X11了,比如VNC或者FreeNX。X11協議中,剪貼板的數據都是保存在X的客戶端,兩個遠程窗口之間不能共享。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM