VBoxes 本地部署
VBoxes 本地部署
优先基础需求使用起来,然后再根据使用时的反馈,发现问题再解决问题。依此不停地迭代,直到达成自己期望的样子。
如果搭建好完整的 CI/CD 系统,迭代流程会相当丝滑,只要把重心放在设计和编码上就好。如果再配合上 AI 工作流,那效果或许会非常棒。
这里的本地部署,指的是部署到家庭的 Nas 主机上,操作系统使用的是 Rock Linux 8.9 (最新 10.0 了,后面找机会迁移到 9.6 )。
VBoxes 服务部署
VBoxes 服务由三部分组成,vboxes 接口服务、网页和 Nginx 代理。最初希望只有一个 vboxes 程序搞定一切,现在则是由 Nginx 处理网页静态资源请求。因此想要自动化部署就比较麻烦了,不然这篇就叫《VBoxes 服务的自动部署》。
vboxes 程序目前仅作为接口服务,即使具备静态服务器的功能(尚未将网页资源打包进可执行文件当中)。当前的 VBoxes 服务架构如图:

创建 vboxes 服务
这里使用 systemd 来管理 vboxes 服务。
创建并编辑配置文件 /etc/systemd/system/vboxes.service ,内容如下:
[Unit]
Description=VBoxes (Vagrant box share server)
After=syslog.target
After=network.target
[Service]
RestartSec=2s
Type=simple
User=happilys
Group=happilys
WorkingDirectory=/srv/nas/VagrantBoxes
ExecStart=/usr/local/bin/vboxes --address 0.0.0.0 --port 9321 --host http://192.168.5.114 --dir ./Boxes
Restart=always
[Install]
WantedBy=multi-user.target加载服务配置并启动
systemctl daemon-reload
# 启动
systemctl start vboxes
# 开机自启
#systemctl enable vboxes
打包并部署网页
配置好 Vue 的开发环境后,拉取代码,打包。把生成的网页文件放到指定目录,编辑 Nginx 配置
实际情况是在开发机打包好,上传到 Nas 主机上的。因为前端不像 C/C++ 程序,跨 CPU 或系统都需要重新编译。C++ 语言是跨平台的,但编译后的二进制不能跨平台。
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name vboxes.home.local;
# 网页的静态资源处理
location / {
root /srv/data/vboxes-ui;
}
# 其他配置
}
}修改网页文件的权限或用户/组
chown -R nginx:nginx /srv/data/vboxes-ui
chmod -R u=rwX,go=rX /srv/data/vboxes-ui家庭内网环境一般可以直接关停 SELinux 和防火墙 。
关闭防火墙
systemctl disable firewalld编辑 /etc/selinux/config 来停用 SELinux
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=disabled另外
# 获取 SELinux 状态
getenfoce
# 临时设置 SELinux
## 停止(Permissive: 仅发出警告,不作实际控制)
setenfoce 0
## 启动(Enforcing: 完全激活状态)
setenfoce 1Nginx 代理配置
这里客户端浏览器可以直接访问接口请求,不过因为浏览器同源策略问题,因此需要使用 Nginx 来代理接口请求(这里提到过)。
生产环境不会将公开接口暴露出来,这里 vboxes 监听所有接口(
vboxes -a 0.0.0.0),只是为了方便网页开发时的调试工作。
# 省略的配置
http {
# 省略的配置
server {
listen 80;
server_name vboxes.home.local;
# 网页静态资源
location / {
root /srv/data/vboxes-ui;
}
# 接口和 .box 文件代理
location ~* (^/api|\.box$) {
proxy_pass http://127.0.0.1:9321;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}重新加载 Nginx 配置后,即可访问 VBoxes 网页
systemctl reload nginx
一些问题
部署过程中,主要问题还是在 vboxes 程序上,部署反而并未发现多少问题。程序方面,存在内存占用较高、运行时异常重启和下载速度太慢等问题。
文件权限被篡改
部署前端文件时,将文件放指定目录后(该目录下的内容同时会被 SMB 共享出去),在经过一段时间之后目录下文件权限全变成问号了。
后面移到了未被 SMB 共享的目录(即前面配置中出现的位置)也便正常了。
文件下载速度
局域网带宽为 1 Gbps ,使用 Vagrant CLI 获取「盒子」时平均速率 10 M/s 。很明显不符合预期,在其他设备上测试也是如此。
使用 iperf3 进行网络测试,能够接近理论带宽。
将 vboxes 重新编译安装之后,然后启服务,似乎恢复正常了。
待续
运维不懂开发,开发不懂运维。原地死锁——自动运维也就很难发展了。
开发角度,可以通过一些技术将原来七个步骤缩短为三个步骤。相对于七步而言,三步更不易出错;而相对于机器而言,人在执行重复性任务时更容易出错(虽然可以通过后天训练提高)。
后续的改善步骤除了搭建好自动化流程,还需将重点放在开发上。
自动化部署
手动部署的方式还是麻烦,如果每次都需要这么做,很容易出错。仅就一台设备而言,好说;可设备多起来的话,岂不原地升天。
如果只有一个二进制程序,不管是本地部署,还是容器化部署都很轻松,虽然当下情况也能使用 docker,但后面还是希望改进为单一的可执行文件形式——把网页文件也编译进二进制。不管你外层是否添加代理还是其他的什么东西,vboxes 程序是完备的。不需要像现在这样,由 Nginx 处理静态文件请求。
另外,单一的二进制也方便嵌入式设备使用,也不用像 Qt 部署时,需要带上一堆动态库(系统自带的和开发时使用的版本往往是不一样的,甚至某些库会因为一些需求而魔改过)。
自动化方案有两种,第一,编写 Dockerfile ,自动构建 Docker 镜像,然后使用 Docker 自动化部署;第二种,完成「二进制资源访问器」的实现,自动化执行 CMake 的 Build 和 Install 步骤(然后重启服务)。将来可以打包为 rpm ,通过包管理器来安装部署。
容器化方案:

非容器化方案:

vboxes 程序的改进
程序崩溃跟踪和日志记录
可能存在的内存问题
多线程:解析数据时程序时没法正常退出的
.box 文件元数据解析:vboxes 模块在 box 文件元数据的读取方面太慢了,除了要计算 MD5 值,还需要解析 tar 包,效率打了大大的折扣。既然都用 C/C++ 了,为什么不定义一个数据结构写到文件头呢?
代码工程化:相对于整个 VBoxes 项目而言,包括前端和其他的衍生工具。
单元测试:因为修改代码引发 Bug ,谁也不愿因此背锅。如果开发时可以大胆地改进糟糕的实现,而不用担心因为代码的修改导致接口出错而引发的 Bug。单元测试可以很好减轻修改代码引发错误的可能,只需要跑一遍单元测试就可以知道接口有没有被改坏。
代码格式化:想要统一所有的 C/C++ 语法代码不可能,但是格式化语言子集还是能够做到的。
参考
- 鸟哥著.鸟哥的Linux私房菜.基础学习篇[M].北京:人民邮电出版社,2018.
- ChatGPT
