打包Qt到Docker
Czz Hardcore

前言

Linux 下开发完 Qt 应用,需要借助于容器管理平台进行管理,因此产生了将 Qt 应用打包到 Docker 容器中的需求。

正文

在正常打包镜像的过程中,我觉得可以根据需求大致分为三类吧。

  • 整体环境打包(开发环境、编译环境、运行环境),可能用于多用户协同开发与测试。
  • 运行环境打包,仅仅将运行时需要用到的库复制到镜像中,常常用于该镜像需要供多主机使用,便于迁移。
  • 较为轻量的方式,挂载宿主机中的运行环境,便于缩小镜像体积,只是对应用进行隔离便于通过容器管理平台管理。

因为在此次需求中,该容器仅仅部署与该计算机中,因此我选择了第三种方法,能挂载的通通使用挂载的策略,不能挂载的库再进行 COPY 打包镜像操作。

X11 Server

x11 界面服务可以将 Docker 容器中需要显示的界面部分转换到宿主机中来呈现,因此需要先对 Linux 系统安装 x11 相关服务。

并在宿主机将 x11 Server 对外提供出去。

1
xhost +

如果需要开机就启动,则需要在~/.profile中添加该命令。

1
xhost + > /dev/null

并且在 docker 运行是添加挂载参数:

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

将容器中的应用映射宿主机上的 x11 套接字。

挂载相关库目录

与其说是相关挂载库目录,其实可以理解成将应用使用到的环境映射到容器中。因为 Qt 应用的库比较多且我的这个应用还涉及到了 OpenCV 的动态库。

通过 ldd 命令查看应用所依赖的库都有哪些,同时会打印这些库在宿主机中所处的绝对路径。

我的做法很简单,直接将这些路径原封不动的添加到镜像中的 LD_LIBRARY_PATH 环境变量中,这样在加载应用时就是直接检索的宿主机中的相关 .so 库。

Dockerfile 添加库路径环境变量。

1
2
# 设置 LD_LIBRARY_PATH
ENV LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:/usr/local/lib:/opt/Qt_xxx_xxx/xxx/bin:/lib

tini

tini 用于 docker 容器中的一号进程,以起到管理其他进程的作用,比如接受 kill 信号等等,由于对该容器的需求有尽可能快的”杀死”要求,那 docker 的强制回收的 -t 参数就显得有点鸡肋。

1
2
3
4
5
6
7
8
9
# Add Tini
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# Run your program under Tini
CMD ["/your/program", "-and", "-its", "arguments"]
# or docker run your-image /your/program ...

但是在实际使用过程中,由于网络问题无法在执行构建时获取 github 上的 tini,于是我选择了自行下载并进行 COPY 的操作。

在这种情况下,其实容器在运行时设置好的入口点便是 /tini 进程,他会进行用户自定义的进程启动以及信号处理等等操作。

启动

在启动时需要配置一些参数,以下是我使用的参数。

1
2
3
4
5
6
7
8
docker run -itd -name xxx 
--net host
-v /etc/localtime:/etc/localtime:ro \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY=unix$DISPLAY \ # 可通过 env | grep DISPLAY 进行查看
-v /lib:/lib
-v /opt:/opt
-v /usr:/usr

启动后就可以看到界面也随之启动,并且可以通过 docker stop 命令对界面进行 kill 操作。

参考