一、背景
筆者所處業(yè)務(wù)需要搭建一個云測平臺,用于集中管理一批安卓系統(tǒng)板卡,經(jīng)過方案選擇,決定使用 openstf 進(jìn)行搭建,一來 openstf 開源,二來大部分服務(wù)采用nodejs進(jìn)行編寫,方便筆者進(jìn)行二次開發(fā)。
二、主要環(huán)境
本地環(huán)境為 mac,線上環(huán)境為 k8s(docker),連接板卡環(huán)境為deepin(linux)。
生產(chǎn)環(huán)境為 docker 和 linux,為了測試,會首先在 mac 上搭建,所以以下搭建過程會兼顧 mac 環(huán)境。
三、openstf 架構(gòu)

官方文檔上將一個個服務(wù)稱為單元。在本次部署中,將 stf-provider 單元部署在直接連接設(shè)備的 linux 機(jī)器上,其他主單元部署在云端服務(wù)器上。具體單元分配如下:
- 云服務(wù)器:
- 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)機(jī)器:
- adbd
- stf-provider
mac 無法使用
docker host模式,但是stf-provider需要監(jiān)聽一定范圍內(nèi)的端口,所以無法在mac上使用stf provider。
adbd需要映射/dev/bus/usb,mac 上沒有該輸入。所以這兩個服務(wù)要部署在本地 linux 機(jī)器(windows 自查)。
下面介紹每個單元的作用及提供 docker 啟動腳本。以下順序也是服務(wù)啟動順序。
以下實(shí)例中,除了 adbd 和 stf-provider 單元,其他單元同一臺機(jī)器進(jìn)行部署。所以端口都設(shè)置成不沖突的端口,在容器需要訪問其他服務(wù) ip 時都使用了 host.docker.internal 或 127.0.0.1,如果部署在不同機(jī)器上,需要將其替換成機(jī)器地址。
其中,
host.docker.internal在容器內(nèi)能映射成宿主機(jī) ip,主要是為了兼容不支持host的環(huán)境(如mac),在支持host的環(huán)境中(如 linux),也可直接換成127.0.0.1。
四、主服務(wù)部署(云服務(wù)器)
1、rethinkdb
rethinkdb 為 openstf 服務(wù)采用的數(shù)據(jù)庫服務(wù)(據(jù)說當(dāng)時作者在開發(fā)時選擇它是因?yàn)樽髡呦雽W(xué)習(xí)當(dāng)時作為新技術(shù)的 rethinkdb )。有 RethinkDB 集群的話可以不啟動此服務(wù)。
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
參數(shù)注釋如下:
-
-it讓容器保持在前臺運(yùn)行,此參數(shù)在生產(chǎn)環(huán)境部署可去除; -
--rm在容器退出時自動清理容器內(nèi)部的文件系統(tǒng),方便調(diào)試,此參數(shù)在生產(chǎn)環(huán)境部署時可去除; -
host.docker.internal在容器內(nèi)會映射到宿主機(jī) ip,如果 docker 使用host模式可以替換成127.0.0.1; -
-p為宿主機(jī)端口到容器內(nèi)端口的映射,以上暴露的三個端口,都是連接 rethinkdb 需要用到的。這么處理是為了兼容不支持 dockerhost模式的環(huán)境(如mac),linux 機(jī)器可以直接使用--net=host共享宿主機(jī)網(wǎng)絡(luò)。之后沒有特殊說明,都采用相同的處理。
2、* stf-migrate
此單元初始化數(shù)據(jù)庫表,創(chuàng)建 stf 需要的各種表格。只需要運(yùn)行一次。
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
提供數(shù)據(jù)持久化的服務(wù)(本教程中主要提供給 stf-storage-plugin-image 和 stf-storage-plugin-apk)。
此單元的實(shí)例不能超過一個,因?yàn)橥瑫r使用了臨時文件和內(nèi)存映射。
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 文件執(zhí)行其他操作,例如檢索 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,并允許使用參數(shù)調(diào)整圖像大小。
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
該單元發(fā)送和接收來自 stf-app 單元的請求,并將其分發(fā)給 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
該單元發(fā)送和接收來自 stf-provider 單元的請求,并將其分發(fā)給 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 服務(wù)主力,它充當(dāng)設(shè)備與 stf 程序之間的橋梁,幾乎所有通信都通過它進(jìn)行。
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地址,由于實(shí)例部署在同一臺機(jī)器上,所以使用了host.docker.internal,若分布在不同機(jī)器,則需要改為對應(yīng)機(jī)器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,會導(dǎo)致服務(wù)直接訪問到tcp://triproxy-app.openstf.com:0上
9、stf-websocket
該單元提供客戶端js腳本和服務(wù)器端 ZeroMQ + Protobuf 之間的通信層。STF 中的幾乎每個動作都通過此單元進(jìn)行。
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
該單元監(jiān)聽 device workers 的心跳,并將丟失的設(shè)備標(biāo)記為不存在,直到繼續(xù)收到心跳信息。目的是確保數(shù)據(jù)庫中存在/不存在標(biāo)志的完整性,以防 stf-provider 意外關(guān)閉或發(fā)生另一個意外失敗。它在啟動時從數(shù)據(jù)庫加載當(dāng)前狀態(tài),并在事件路由到它時繼續(xù)修補(bǔ)其內(nèi)部視圖。
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
提供用戶可訪問的網(wǎng)站。
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進(jìn)行用戶驗(yàn)證之后返回,所以stf-app必須和stf-auth處于相同 host 之下,此處實(shí)例使用openstf.test.com,需要配置 nginx 代理。配置見后文。
13、stf-auth
提供用戶驗(yàn)證。以下選擇 Mock auth,只要用戶提供用戶名及郵箱即可登錄。其他設(shè)置可見
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
以上服務(wù)啟動之后,還不能正常訪問,需要添加 nginx 代理連接各個服務(wù)。以下為 nginx 配置,是官網(wǎng)nginx配置的自定義刪減版。
daemon off;
worker_processes 1;
events {
worker_connections 1024;
}
#如果nginx與服務(wù)器不在一臺上將127.0.0.1改為服務(wù)器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 服務(wù)沒生效,需要進(jìn)入容器內(nèi)手動啟動 nginx :
/etc/init.d/nginx start
五、provider 服務(wù)部署(linux端)
在 linux 端,需要安裝 adbd 用于連接設(shè)備,之后啟動 stf-provider 服務(wù)。
stf-provider 單元主要用于連接 adb,為每個 adb 連接設(shè)備開一個 worker 進(jìn)程。用于發(fā)送和接受 stf-processor 的指令。
需要注意的是,stf-provider 需要管理特定范圍的端口,這些端口用于內(nèi)部服務(wù)和屏幕截圖 WebSocket。所以要求設(shè)置 --net host 的。這也是為什么 stf-provider 機(jī)器要使用 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 建立連接并交換數(shù)據(jù)。
至此,openstf 部署完成,通過 openstf.test.com 即可訪問網(wǎng)站。
如果在本地部署,其他機(jī)器需要配置 hosts:部署機(jī)器ip openstf.test.com