在Mac上尝试用Docker运行一个需要网络的Gui程序


关于Docker使用X11来运行Gui程序,网上的东西只能说是千篇一律的复制粘贴,以至于产生了一种错觉-> 使用Dockerfile就可以达到目的

实际上单纯使用Dockerfile无法达到运行Gui程序的目的,应该说远远不够,因为Dockerfile本身是用于自定义镜像的,也就是说,是用来自定义一个集成了你需要的软件包的原始镜像

但是本着尝试的态度我也去学习了下Dockerfile,并且尝试写了一个,那就记录下来算了

编写Dockerfile

在一个可写入的位置新建一个文件 名为Dockerfile 不需要加后缀,也不要改名,内容如下

#!/bin/bash
#这里以ubuntu:16.04为基础镜像
FROM ubuntu:16.04
# 用你自己的 uid /gid 替换下面的0 查看命令 id $USER
# RUN export uid=501 gid=20
#添加自己需要的内容
ADD ./Acc /home/app
# 添加具有免密码sudo权限的普通用用户 用户目录为/home/acc 名为lckiss
#PS:以下所有命令都不要包含sudo 如果需要,请取消下面这行的注释 去掉#即可
# RUN apt-get update && apt-get install -y sudo && rm -rf /var/lib/apt/lists/*

RUN mkdir -p /home/acc
RUN echo "lckiss:x:${uid}:${gid}:lckiss,,,:/home/acc:/bin/bash" >> /etc/passwd
RUN echo "lckiss:x:${uid}:" >> /etc/group
RUN echo "lckiss ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
RUN chmod 0440 /etc/sudoers
RUN chown ${uid}:${gid} -R /home/acc

#解决无法找到add-apt-repository问题
RUN apt-get update && apt-get install -y python-software-properties && apt-get install -y software-properties-common

#添加mono的源
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
RUN echo "deb http://download.mono-project.com/repo/ubuntu xenial main" | tee /etc/apt/sources.list.d/mono-official.list

#安装mono环境
RUN apt update
RUN apt-get install -y mono-devel

#指定用户
USER lckiss
#设置环境变量 HOME为/home/acc
ENV HOME /home/acc
#启动容器时的运行命令
CMD /usr/bin/mono /home/acc/Acc.exe

构建原始镜像

打开终端,切换到Dockerfile所在文件夹运行下面的命令来构建你的基础镜像(我上面的是一些基础的用法,比如添加了mono(.net环境)支持,并且添加了一些东西期望其能运行Gui)

docker build --rm -t my-mono .

这里的my-mono是取的镜像名称,构建成功后会生成一个名为my-mono的原始镜像,很多人都会问为什么要加--rm,都知道--rm是容器运行后就销毁的意思,起始Dockerfile就是这样运作的,先按你选择的最基础的镜像,比如我上面写的是ubuntu 16.04,生成一个容器,在容器中执行你的那些命令,完成后保存为镜像。那么这里就有个问题,无论是失败成功都会有一个容器,并且运行过后再无用处,所以加--rm,不信你可以用这个命令查看你生成了多少个容器

docker container ls -a

然后你可以按你的需求去运行这个镜像的实例了,比如:

docker run -it --privileged --mount type=bind,source=/Users/lckiss/Downloads/,target=/my-data --name baidu -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix my-mono

起始这个启动命令的有些参数是可以写到Dockerfile中的,但是我就懒得去改了,本来想着这样我就可以使用mono运行Gui的程序,后来发现不行,因为我的是Mac而不是linux。

具体linux运行Gui的方法可以看这篇文章,因为看上去更符合环境:http://blog.csdn.net/ericcchen/article/details/79253416

过渡段

mono这个工程实际上mac也有,但是很抱歉,这玩意儿在mac上不是那么的尽如人意,linux是友好的,软件本身是一方面,环境也是一方面

如何在mac上跑起来

起初我在看这篇文章的时候就觉得,这不是一般的麻烦:http://www.infoq.com/cn/articles/talk-about-docker-running-the-chinese-gui-software

应该说这是2018年,不是2015年,这中间的3年足矣让我怀疑这篇文章的做法

但是网上的文章,少之又少,并且由于平台的差异性,难以找到一定吻合的解决方案,这里就需要自己慢慢Google加上慢慢尝试

网上有一些解决办法,其中最常见的就是:https://coderwall.com/p/vwbpcg/run-gui-apps-in-containier-in-osx-docker-for-mac

中文版:http://shaoguangleo.github.io/2018/01/21/docker-run-gui-on-macosx/

不要问我为什么不尝试,实际上这种是有局限性的,而我要运行的是一个下载器,运行时错误为:

[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeInitializationException: The type initializer for 'System.Windows.Forms.XplatUI' threw an exception. ---> System.ArgumentNullException: Could not open display (X-Server required. Check your DISPLAY environment variable)
Parameter name: Display
  at System.Windows.Forms.XplatUIX11.SetDisplay (System.IntPtr display_handle) [0x00408] in <8254e6010dae4d09ac0767af6dcc75af>:0 
  at System.Windows.Forms.XplatUIX11..ctor () [0x00077] in <8254e6010dae4d09ac0767af6dcc75af>:0 
  at System.Windows.Forms.XplatUIX11.GetInstance () [0x00019] in <8254e6010dae4d09ac0767af6dcc75af>:0 
  at System.Windows.Forms.XplatUI..cctor () [0x00066] in <8254e6010dae4d09ac0767af6dcc75af>:0 

这种错误网上也有,但是因为不通用,而且比较新,谷歌上的结果基本上在2018年1月,所以无法解决,这种问题要么找开发者,要么尝试给docker装个桌面,然后通过远程连接的方式,这实在有点曲线救国,并且感觉不到这和虚拟机有什么区别。

更新

经过再一次的尝试,证明上面教程确实可行,原因是看到了这个文章:https://stackoverflow.com/questions/48335431/osx-x11-error-cannot-open-display

执行xhost +来确保是可以从网络连接的而不是"access control disabled, clients can connect from any host",解决办法是执行:export DISPLAY=192.168.1.X:0 (X改成你的主机Ip)

并且执行该命令(socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$SPLAY\")的终端不要中断,请后台运行,具体如下:

DockerFile

#!/bin/bash
#docker run 
# --rm -it 
# --privileged 
# --mount type=bind,source=/Users/lckiss/Downloads/,target=/my-data 
# --name baidu 
# -e DISPLAY=192.168.1.104:0 
# -v /tmp/.X11-unix:/tmp/.X11-unix 
# my-mono
#这里以mono:latest为基础镜像
FROM mono:latest
#添加自己需要的内容
RUN mkdir /home/app
ADD ./Acc /home/app
RUN cp /home/app/Config.json /
#解决中文乱码问题
RUN apt-get update && apt-get install -y xfonts-wqy ttf-wqy-zenhei

#缓存等清理
RUN apt-get clean && apt-get autoclean && apt-get autoremove && rm -rf /var/lib/apt/lists/*

#指定用户
USER root
#设置环境变量 HOME为/root
ENV HOME /root
ENV LANG zh-CN.UTF-8
ENV LANGUAGE zh-CN.UTF-8
ENV LC_ALL zh-CN.UTF-8
#启动容器时的运行命令
CMD /usr/bin/mono /home/app/Acc.exe

监听命令 (安装:brew install socat)

socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"

X11:

brew install Caskroom/cask/xquartz

打开Xquartz软件,偏好设置->安全性->勾选允许从网络客户端连接

编译dockerfile命令

docker build --rm -t my-mono .

启动命令 其中IP地址为本机的IP地址,注意修改

docker run --rm -it \
--privileged \
--mount type=bind,source=/Users/lckiss/Downloads/,target=/my-data \
--name baidu -e DISPLAY=192.168.43.196:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
my-mono

更新优化好的Dockerfile

用于打包rom的,将工具包放在同一目录下

#!/bin/bash
#docker run 
# -it 
# --privileged 
# --name rom 
# --mount type=bind,source=/Users/lckiss/Downloads/,target=/my-data 
# ubuntu:16.04 bash
#这里以java:8为基础镜像
FROM java:8

#添加自己需要的内容
RUN mkdir /mnt/rom
ADD ./util /home/util

#链接权限
RUN chmod +x -R /home/util/ && ln -s /home/util/* /bin

RUN apt-get update && apt-get install -y file lib32c-dev lib32stdc++6

#缓存等清理
RUN apt-get clean && apt-get autoclean && apt-get autoremove && rm -rf /var/lib/apt/lists/*

#设置环境变量 HOME为/root
ENV HOME /root
#启动容器时的运行命令
CMD /bin/bash

编译dockerfile命令

docker build --rm -t my-rom .

启动命令

docker run -it --privileged --name linux --mount type=bind,source=/Users/lckiss/Downloads/,target=/my-data my-rom

总结:

踩完坑了确实要比虚拟机强悍很多啊,不用等开机,告别了臃肿,只用来执行单一的任务,一条命令即可搞定。Nice!

Dockerfile的方式可以让你抛弃容器(用完就删除),个人觉得是Docker的一种高级用法,但是真正明白了,也是很简单的。

至于其他的实现,比如说ngnix等等环境,都有直接配置好的镜像,不满意可以在这个基础上进行修改即可。

至于直接安装一个桌面就不尝试了,完全没必要,但是对于美观的elementary,有必要留下一个脚印:Ubuntu1604安装Pantheon桌面环境

PS:你也可以不学习Dockerfile一样可以达到运行的效果,使用Dockerfile只是为了开箱即用

声明:TIL|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA[ZH]协议进行授权

转载:转载请注明原文链接 - 在Mac上尝试用Docker运行一个需要网络的Gui程序


Life is very interesting. In the end, some of your greatest pains become your greatest strengths.