使用 Docker 部署 openstf 平台
一、背景
笔者所处业务需要搭建一个云测平台,用于集中管理一批安卓系统板卡,经过方案选择,决定使用 openstf
进行搭建,一来 openstf
开源,二来大部分服务采用nodejs进行编写,方便笔者进行二次开发。
二、主要环境
本地环境为 mac,线上环境为 k8s(docker),连接板卡环境为deepin(linux)。
生产环境为 docker 和 linux,为了测试,会首先在 mac 上搭建,所以以下搭建过程会兼顾 mac 环境。
三、openstf 架构
openstf 架构图.png官方文档上将一个个服务称为单元。在本次部署中,将 stf-provider
单元部署在直接连接设备的 linux 机器上,其他主单元部署在云端服务器上。具体单元分配如下:
- 云服务器:
- rethinkdb
- stf-storage-temp
- stf-storage-plugin-apk
- stf-storage-plugin-image
- stf-triproxy-app
- stf-triproxy-dev
- stf-processor
- stf-websocket
- stf-api
- stf-reaper
- stf-app
- stf-auth
- linux(deepin)机器:
- adbd
- stf-provider
mac 无法使用
docker host
模式,但是stf-provider
需要监听一定范围内的端口,所以无法在mac上使用stf provider
。
adbd
需要映射/dev/bus/usb
,mac 上没有该输入。所以这两个服务要部署在本地 linux 机器(windows 自查)。
下面介绍每个单元的作用及提供 docker 启动脚本。以下顺序也是服务启动顺序。
以下实例中,除了 adbd
和 stf-provider
单元,其他单元同一台机器进行部署。所以端口都设置成不冲突的端口,在容器需要访问其他服务 ip 时都使用了 host.docker.internal
或 127.0.0.1
,如果部署在不同机器上,需要将其替换成机器地址。
其中,
host.docker.internal
在容器内能映射成宿主机 ip,主要是为了兼容不支持host
的环境(如mac),在支持host
的环境中(如 linux),也可直接换成127.0.0.1
。
四、主服务部署(云服务器)
1、rethinkdb
rethinkdb 为 openstf 服务采用的数据库服务(据说当时作者在开发时选择它是因为作者想学习当时作为新技术的 rethinkdb )。有 RethinkDB 集群的话可以不启动此服务。
docker run -it --rm \
--name rethinkdb \
-v $HOME/demo/rethinkdb:/data \
-p 28015:28015 \
-p 8080:8080 \
-p 29015:29015 \
rethinkdb \
rethinkdb --bind all \
--canonical-address host.docker.internal \
--cache-size 8192 \
--no-update-check
参数注释如下:
-
-it
让容器保持在前台运行,此参数在生产环境部署可去除; -
--rm
在容器退出时自动清理容器内部的文件系统,方便调试,此参数在生产环境部署时可去除; -
host.docker.internal
在容器内会映射到宿主机 ip,如果 docker 使用host
模式可以替换成127.0.0.1
; -
-p
为宿主机端口到容器内端口的映射,以上暴露的三个端口,都是连接 rethinkdb 需要用到的。这么处理是为了兼容不支持 dockerhost
模式的环境(如mac),linux 机器可以直接使用--net=host
共享宿主机网络。之后没有特殊说明,都采用相同的处理。
2、* stf-migrate
此单元初始化数据库表,创建 stf
需要的各种表格。只需要运行一次。
docker run --rm \
--name migrate \
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015 \
-e "STF_ROOT_GROUP_NAME=STF" \
-e "STF_ADMIN_NAME=username" \
-e "STF_ADMIN_EMAIL=username@email.com" \
openstf/stf:latest \
stf migrate
3、stf-storage-temp
提供数据持久化的服务(本教程中主要提供给 stf-storage-plugin-image
和 stf-storage-plugin-apk
)。
此单元的实例不能超过一个,因为同时使用了临时文件和内存映射。
docker run --rm \
--name storage-temp \
-v $HOME/demo/openstf/mnt/storage:/data \
-p 3500:3000 \
openstf/stf:latest \
stf storage-temp --port 3000 \
--save-dir /data
4、stf-storage-plugin-apk
该 APK 存储插件从主存储单元加载原始 Blob
,并允许对 APK 文件执行其他操作,例如检索 AndroidManifest.xml
。
docker run --rm -it \
--name storage-plugin-apk \
-p 3300:3000 \
openstf/stf:latest \
stf storage-plugin-apk --port 3000 \
--storage-url http://host.docker.internal:3500/
5、stf-storage-plugin-image
该 image 存储插件从主存储单元加载原始 Blob
,并允许使用参数调整图像大小。
docker run --rm -it \
--name storage-plugin-image \
-p 3400:3000 \
openstf/stf:latest \
stf storage-plugin-image --port 3000 \
--storage-url http://host.docker.internal:3500/
6、stf-triproxy-app
该单元发送和接收来自 stf-app
单元的请求,并将其分发给 stf-processor
处理。
docker run --rm \
--name triproxyapp \
-p 7150:7150 \
-p 7160:7160 \
-p 7170:7170 \
openstf/stf:latest \
stf triproxy app \
--bind-pub "tcp://*:7150" \
--bind-dealer "tcp://*:7160" \
--bind-pull "tcp://*:7170"
7、stf-triproxy-dev
该单元发送和接收来自 stf-provider
单元的请求,并将其分发给 stf-processor
处理。
docker run --rm \
--name triproxydev \
-p 7250:7250 \
-p 7260:7260 \
-p 7270:7270 \
openstf/stf:latest \
stf triproxy dev \
--bind-pub "tcp://*:7250" \
--bind-dealer "tcp://*:7260" \
--bind-pull "tcp://*:7270"
8、stf-processor
该单元为 stf 服务主力,它充当设备与 stf 程序之间的桥梁,几乎所有通信都通过它进行。
docker run --rm \
--name processor \
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015 \
openstf/stf:latest \
stf processor processor \
--connect-app-dealer tcp://host.docker.internal:7160 \
--connect-dev-dealer tcp://host.docker.internal:7260
-
RETHINKDB_PORT_28015_TCP
为rethinkdb
地址,由于实例部署在同一台机器上,所以使用了host.docker.internal
,若分布在不同机器,则需要改为对应机器ip地址。 -
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015
可用--link rethinkdb:rethinkdb
代替,效果相同。 -
connect-app-dealer
为stf-triproxy-app
地址,connect-dev-dealer
为stf-triproxy-dev
。
此处tcp地址不能写域名映射,如
tcp://triproxy-app.openstf.com
,会导致服务直接访问到tcp://triproxy-app.openstf.com:0
上
9、stf-websocket
该单元提供客户端js脚本和服务器端 ZeroMQ
+ Protobuf
之间的通信层。STF 中的几乎每个动作都通过此单元进行。
docker run --rm -it \
--name websocket \
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015 \
-e SECRET=YOUR_SESSION_SECRET_HERE \
-p 3600:3000 \
openstf/stf:latest \
stf websocket --port 3000 \
--storage-url http://host.docker.internal:3500/ \
--connect-sub tcp://host.docker.internal:7150 \
--connect-push tcp://host.docker.internal:7170
10、stf-api
该单元为 STF 提供了所有主要的 RESTful API。用户可以从 STF UI 生成其个人访问令牌,并且可以使用该令牌从任何界面访问这些 api。
docker run --rm \
--name api \
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015 \
-e "SECRET=YOUR_SESSION_SECRET_HERE" \
-p 3700:3000 \
openstf/stf:latest \
stf api --port 3000 \
--connect-sub tcp://host.docker.internal:7150 \
--connect-push tcp://host.docker.internal:7170 \
--connect-sub-dev tcp://host.docker.internal:7250 \
--connect-push-dev tcp://host.docker.internal:7270
11、stf-reaper
该单元监听 device workers 的心跳,并将丢失的设备标记为不存在,直到继续收到心跳信息。目的是确保数据库中存在/不存在标志的完整性,以防 stf-provider 意外关闭或发生另一个意外失败。它在启动时从数据库加载当前状态,并在事件路由到它时继续修补其内部视图。
docker run --rm \
--name reaper \
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015 \
openstf/stf:latest \
stf reaper dev \
--connect-push tcp://host.docker.internal:7270 \
--connect-sub tcp://host.docker.internal:7150 \
--heartbeat-timeout 30000
12、stf-app
提供用户可访问的网站。
docker run --rm -it \
--name stf-app \
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015 \
-e SECRET=YOUR_SESSION_SECRET_HERE \
-p 3000:3000 \
openstf/stf:latest \
stf app --port 3000 \
--auth-url http://openstf.test.com/auth/mock/ \
--websocket-url ws://host.docker.internal:3600/
由于
stf-app
会去stf-auth
进行用户验证之后返回,所以stf-app
必须和stf-auth
处于相同 host 之下,此处实例使用openstf.test.com
,需要配置 nginx 代理。配置见后文。
13、stf-auth
提供用户验证。以下选择 Mock auth,只要用户提供用户名及邮箱即可登录。其他设置可见
stf-auth@.service。
docker run --rm -it \
--name auth-mock \
-e RETHINKDB_PORT_28015_TCP=tcp://host.docker.internal:28015 \
-e SECRET=YOUR_SESSION_SECRET_HERE \
-p 3200:3200 \
openstf/stf:latest \
stf auth-mock --port 3200 \
--app-url http://openstf.test.com/
14、nginx
以上服务启动之后,还不能正常访问,需要添加 nginx 代理连接各个服务。以下为 nginx 配置,是官网nginx配置的自定义删减版。
daemon off;
worker_processes 1;
events {
worker_connections 1024;
}
#如果nginx与服务器不在一台上将127.0.0.1改为服务器IP
http {
upstream stf_app {
server 127.0.0.1:7100 max_fails=0;
}
upstream stf_auth {
server 127.0.0.1:7101 max_fails=0;
}
upstream stf_storage_apk {
server 127.0.0.1:7104 max_fails=0;
}
upstream stf_storage_image {
server 127.0.0.1:7105 max_fails=0;
}
upstream stf_storage {
server 127.0.0.1:7106 max_fails=0;
}
upstream stf_websocket {
server 127.0.0.1:7102 max_fails=0;
}
upstream stf_api {
server 127.0.0.1:7103 max_fails=0;
}
types {
application/javascript js;
image/gif gif;
image/jpeg jpg;
text/css css;
text/html html;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
# 配置的域名
server_name openstf.test.com;
keepalive_timeout 70;
location /auth/ {
proxy_pass http://stf_auth/auth/;
}
location /api/ {
proxy_pass http://stf_api/api/;
}
location /s/image/ {
proxy_pass http://stf_storage_image;
}
location /s/apk/ {
proxy_pass http://stf_storage_apk;
}
location /s/ {
client_max_body_size 1024m;
client_body_buffer_size 128k;
proxy_pass http://stf_storage;
}
location /socket.io/ {
proxy_pass http://stf_websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $http_x_real_ip;
}
location / {
proxy_pass http://stf_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $http_x_real_ip;
}
}
}
在本地启动 nginx 容器:
docker run -it --name nginx \
-p 80:80 \
-v $HOME/demo/openstf/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx nginx
注:如果 nginx 服务没生效,需要进入容器内手动启动 nginx :
/etc/init.d/nginx start
五、provider 服务部署(linux端)
在 linux 端,需要安装 adbd 用于连接设备,之后启动 stf-provider
服务。
stf-provider
单元主要用于连接 adb,为每个 adb 连接设备开一个 worker 进程。用于发送和接受 stf-processor
的指令。
需要注意的是,stf-provider
需要管理特定范围的端口,这些端口用于内部服务和屏幕截图 WebSocket。所以要求设置 --net host
的。这也是为什么 stf-provider
机器要使用 linux 的原因。
- 启动 adbd:
docker run -d --name adbd \
--privileged \
-v /dev/bus/usb:/dev/bus/usb \
--net host \
sorccu/adb:latest
- 启动
stf-provider
:
docker run -it --rm --name provider1 --net host openstf/stf \
stf provider --name provider1 \
--min-port=15000 \
--max-port=25000 \
--heartbeat-interval 20000 \
--allow-remote \
--no-cleanup \
--screen-jpeg-quality 80 \
--screen-reset false \
--connect-sub tcp://devside.stf.example.org:7250 \
--connect-push tcp://devside.stf.example.org:7270 \
--storage-url http://openstf.test.seewo.com
devside.stf.example.org
为 stf-triproxy-dev
地址,stf-provider
将自动与 stf-triproxy-dev
建立连接并交换数据。
至此,openstf 部署完成,通过 openstf.test.com
即可访问网站。
如果在本地部署,其他机器需要配置 hosts:部署机器ip openstf.test.com