林帆:Docker運行GUI軟件的方法

繼上週的「Kubernetes v1.0特性解析」分享以後,本週咱們邀請到ThoughtWorks諮詢師林帆爲你們帶來主題爲「Docker運行GUI軟件的方法」的分享。


嘉賓簡介:林帆,ThoughtWorks諮詢師,主要關注Docker與容器相關方向。java


簡介:

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

下文是本次的分享整理:

 

Docker比較經常使用的場景是『運行無界面的後臺服務』或者『運行基於的Web服務』。不過有時出於我的的喜愛或特定的需求,咱們會但願在Docker中運行帶圖形界面的應用程序。git

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

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

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

將容器中的圖形界面展現到外部的通常性思路。

目前Unix/Linux比較主流的圖形界面服務是X11,而X11服務的圖形顯示方式其實是一種Client/Server模式,在服務端和客戶端之間,X11經過『DISPLAY』環境變量來指定將圖形顯示到何處。以下面的流程所示,請注意服務端與客戶端的位置,服務端是用於提供顯示信息的。安全

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

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

默認狀況下,X11的服務端會監聽本地的『unit:0』端口,而DISPLAY的默認值爲『:0』,這其實是『unit:0』的簡寫。所以若是在Linux的控制檯啓動一個圖形程序,它就會出如今當前主機的顯示屏幕中。網絡

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

 

  1. 經過SSH鏈接或遠程控制軟件,最終經過tcp套接字將數據發送出來
  2. 讓容器和主機共享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 <user>@<ip-addr>

 

登錄上去後運行剛纔的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的客戶端,兩個遠程窗口之間不能共享。

相關文章
相關標籤/搜索