部署

我们采用 docker 进行程序部署,至于 docker 如何使用,不在本文的讨论范围, 你可以在网上搜索丰富的教程和资料(主要是学习一些基础的概念和指令)。

我们在 core 目录下已经写好了 Dockerfile,你可以通过:

# 进入 core 文件夹
cd core 
# 构建镜像
docker build -t server .

这样会构建出一个名为 server 的 docker 镜像,这一过程大概需要 10 分钟,请耐心等待。

为了便于快速启动,在 core 目录底下有一个名为 run.sh 的脚本文件。你可以参考里面的写法, 本质上做了两件事:

  • 利用 ifconfig 指令设置 eth0 的网段,使其和机械臂处于同一网段下(192.168.1.xx)。
  • 启动对应的容器,并在该容器中执行 /bin/bash 指令,也就是开启一个会话终端,这样你就能用命令行和这个打开的容器进行交互了。

我知道第二点比较难看懂,我来一点点解释:

id=$(sudo docker run -itd --privileged -v /home/manager/broadim/:/broadim -v /dev:/dev -v /tmp/.X11-unix:/tmp/.X11-unix:rw -e DISPLAY=$DISPLAY --network host --runtime nvidia server:latest)

首先上面这一步,如果你了解 linux 命令行的话,应该知道美元符号 + 括号的作用,这会提取括号内指令执行结果,然后通过id=这样的赋值语句赋值给变量 id。

docker run 会启动一个容器,-itd 是很常用的 flag,网上搜一下就明白了。如果你对 docker 不是很了解,你大可以将其粗略理解为一个和外界隔绝的黑匣子, 我们的程序需要用到各类设备,主要是 USB 设备,在黑匣子里你是看不到外面的东西的,所以为了让黑匣子能看到、识别到 USB 设备(例如相机),就有了 -v /dev:/dev。 /dev 里面的诸多内容是 linux 非常常见的伪文件的形式,linux 通过文件的方式来提供设备文件信息读写的功能(关于这一点,如果你没有相关的操作系统知识,可以去了解,但是并不重要,也不影响后续的理解)。 将 /dev 挂载到容器内的 /dev,由于容器我们也用了 linux 的系统,那么自然就能顺利成章地读到宿主机(也就是小电脑,我们习惯把容器或者虚拟机运行在的平台成为宿主机) 上连接的 USB 设备了。由于涉及一些权限的问题,所以我们通过 --privileged 进行提权,让容器拥有宿主机的 root 权限,当然,这并不是一个很安全的行为, 或许会有更好的方式,欢迎你来进行修改~

-v /home/manager/broadim/:/broadim 将小电脑上的 broadim(也就是我们整个项目目录)挂载到容器里,这样方便我们使用项目的各个文件和工具,同时 我们只需要在宿主机上(在容器外部)使用 git pull 就能拉取最新的内容了(在小电脑上先用 git clone 拉整个项目),而不用通过 docker build 反复构建镜像。具体运行操作下文会阐述。

-v /tmp/.X11-unix:/tmp/.X11-unix:rw -e DISPLAY=$DISPLAY 这一部分是因为容器一般而言只能用命令行和它进行交互,而无法看到里面的可视化界面。 这一步是在处理 xserver 相关的内容,让外部能够看到容器内可视化的内容,例如在容器内使用了 cv2.imshow,外部就能看到。 如果想要做到这一点,外部记得使用

xhost + 

指令开启 xserver。

--network host 这一步是在指定容器网络模式为 host,也就是共享宿主机的网络命名空间(Network Namespace)。宿主机 ip 是多少,容器 ip 就是多少, 宿主机可以 ping 通 xxx.xxx.xxx.xxx,容器也可以。这又涉及到 linux 的命名空间的概念,如果你想了解可以去网上搜索,本质上它的作用就是在做隔离。 默认情况下,docker 启动的容器和宿主机的网络命名空间是独立的,两者通过网桥进行交互(不同的命名空间对应不同的 ip,虽然只有一张网卡,但是却可以有多个 ip?没错,这就是虚拟化的功劳,不多赘述) 我们希望使用宿主机的网络,不需要使用独立的网络命名空间,所以使用 host mode。

--runtime nvidia

用于在具有NVIDIA GPU支持的系统上运行容器时指定运行时为NVIDIA Container Runtime。 NVIDIA Container Runtime 是一种专门设计用于在GPU支持的主机上运行容器的运行时环境。它允许在容器中访问和利用主机上的NVIDIA GPU资源,从而使机器学习、深度学习等需要大量计算资源的应用能够在容器中得到加速。 当你在运行Docker容器时使用docker runtime=nvidia,Docker会使用NVIDIA Container Runtime来管理容器与主机上的GPU之间的交互,从而实现GPU加速的容器化应用。这对于训练神经网络、进行数据分析和其他需要大量并行计算的任务非常有用。

那么,说到这个份上,如何启动对应的服务器 server.py 呢?对了,先说说为什么有 server.py 这东西,因为我们需要有个后端 http server 和我们的 APP 进行交互, 所以就基于 Flask 开发了个后端服务。(同时我们还基于 Swagger 生成了 API 文档,你可以通过 <小电脑的ip>:5000 来访问,假如小电脑ip是 192.168.1.203,那么这个网址就是 http://192.168.1.203:5000)

言归正传,运行 run.sh 之后你就进入到容器内了,你会发现命令行前缀变了(从 manager 变成 root),然后你可以:

cd ..
cd /broadim/core
python3 server.py

请注意一定是 python3,因为 python 默认软链接(symlink)到了 python2.7,当然你可以修改一下这个软链接。。。

我们的小电脑已经开启了 ssh server,所以上述的操作你完全可以使用一些 ssh 工具远程完成,例如 xhsell,putty 等等。

疑难杂症

端口被占用?

tcp 端口只能被一个进程占用,你可以通过:

# 查看 5000 端口占用情况
netstat -anp | grep 5000

查看占用情况,5000 可以替换成别的端口。

如何关闭进程?

ps -ef | grep 进程名
kill -9 进程的 pid

如何查看 docker 容器有哪些?如何删除指定容器?

查看所有容器:

docker ps -a

删除容器

docker rm 容器ID/容器名

一行指令删除所有:

sudo docker rm -f $(sudo docker ps -a | awk '{print $1}')

各种权限问题

遇到权限不够的问题,往往会打印:Permission Denied,此时别担心,这也是 linux 安全的体现, 普通用户有些事情做不了很正常,用 sudo 暂时提权到 root 即可。例如:

sudo docker ps -a

如果是一些脚本执行不了,例如之前提及的 run.sh,你可以修改文件的模式,使其能够执行: (本质上是因为这个文件是 windows 下创建的,windows 和 linux 的文件权限管理不兼容)

chmod +x run.sh

chmod 当然可以指定更细粒度权限,请自行查资料。

其他解决不了的,请 google!