STF开发环境搭建与制作docker镜像过程

本帖已被设为精华帖!,

一、前言

看到很多同学在研究STF,大部分人只是讲了怎么安装,然后运行一个stf local命令可以开始使用了,但是在实际使用中,比如大公司中肯定不能使用stf local方式给大家演示一下吧,哈哈~~~

另外也不能使用mock方式让任何人都能随意的登录。如果你想自己定制一些东西,需要自己对源码进行一些修改,说实话,想随意修改stf源码还真不是一件特别简单的事。下面根据我自己的一些经验介绍一个生产环境中stf平台的搭建及一些开发注意事项,希望想了解的同学不要踩这些坑。
参考前辈的一些文章,特别是monkey的“STF 改造之 first blood”,给了我很大帮助,多谢他们!

(1)STF 改造之 first blood

(2)MAC 下 STF 的环境搭建和运行

(3)WEB 端批量移动设备管理控制工具 STF 的环境搭建和运行

二、生产环境中STF平台的搭建

这里是官方的搭建文档:
Deployment

官方推荐使用docker容器来搭建,根据我的使用经验,docker容器确实更方便一些,关于docker的知识就不作介绍了,详情可以自行搜索,或者参考docker官方网站。

使用docker搭建STF平台需要pull下来五个镜像,这里假设你已经安装过了docker容器,为了加快pull速度,可以参考下面的文章,换成国内的docker镜像

Docker学习笔记 — 配置国内免费registry mirror

确定安装好docker以后,pull下面几个镜像:

docker pull openstf/stf:latest
docker pull sorccu/adb:latest
docker pull rethinkdb:latest
docker pull openstf/ambassador:latest
docker pull nginx:latest

拉下来这几个镜像以后,stf就可以直接在容器中运行了,也就是说你宿主机上完全不用再装stf的相关工具,甚至adb也不用。

很多文章介绍的装stf要先装一堆工具,然后使用npm install -g stf安装到系统中,大部分人会发现,由于网络或者其他原因,npm install -g stf方式也不太容易成功,其实在docker里也可以直接运行stf local,并且更加简单,使用以下三条命令即可:

#先启动一个数据库
docker run -d --name rethinkdb -v /srv/rethinkdb:/data --net host rethinkdb rethinkdb --bind all --cache-size 8192 --http-port 8090
#再启动adb service
docker run -d --name adbd --privileged -v /dev/bus/usb:/dev/bus/usb --net host sorccu/adb:latest
#再启动stf
docker run -d --name stf --net host openstf/stf stf local --public-ip your-ip

这样就可以直接从7100端口也访问了,下面介绍正宗的安装方式,先介绍下几个组件:

  • openstf/stf就是stf主镜像了。

  • sorccu/adb是adb工具,adb工具是和provider模块在一台机器上的,这个后面会再说。

  • rethinkdb就是数据库了,不知道开发者为什么想用这个数据库,而不用常用的mysql之类的,反正感觉这个数据库挺另类的,和mysql的查询语法完全不一样。 这里注意,官方文档上要pull rethinkdb:2.1.1,但是现在版本的stf好像不支持rethinkdb:2.1.1,经测试,最新版本的rethinkdb也是没问题的,所以就用最新的了。

  • openstf/ambassador这个网络代理工具,是连接docker内部网络的一个工具,估计作者是基于ambassador官方镜像进行修改的,ambassador可以让不同dokcer之前以私有网络的方式进行连接,可以理解为虚拟网络,如果你把所有的端口暴露在外面,这个东西也可以不要。

  • nginx,这是一个web服务器反向代理工具,详细的就不介绍了,反正stf依赖它将不同的url转发到不同模块上,反正没有nginx,生产环境中的stf是肯定不能正常工作的。

好了,docker镜像讲完了,下面就是搭建过程了,为了简单,所有的模块搭建在一台机器上,如果要搭建在不同机器,修改nginx里的反向代理server就行了。其实搭建就是启动几个docker容器,设置一些端口的事情。stf作者希望用systemd来启动这些容器,在官方部署文档上可以看到类似下面的代码

[Unit]
Description=ADB daemon
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=/usr/bin/docker pull sorccu/adb:latest
ExecStartPre=-/usr/bin/docker kill %p
ExecStartPre=-/usr/bin/docker rm %p
ExecStart=/usr/bin/docker run --rm \
--name %p \
--privileged \
-v /dev/bus/usb:/dev/bus/usb \
--net host \
sorccu/adb:latest
ExecStop=-/usr/bin/docker stop -t 2 %p

刚开始我纠结了几天,不知道这是个什么东西,其实它就是一个启动工具,跟手动执行shell命令是一样样的,只不过可以开机启动,或者在失败以后可以自动重启。在使用sysyted启动的系统中,systemd所有可用的单元文件存放在 /usr/lib/systemd/system/ 和 /etc/systemd/system/ 目录(后者优先级更高),可以去这两个目录看看已经有脚本。

ubuntu 15.04和centos7以后的版本都是使用systemd进行启动的,如果你用的这些系统,可以考虑使用systemd来启动stf容器,确实更方便一些,如果你不要使用systemd,使用shell脚本也是可以的,为了更好理解,下面介绍时会shell脚本来启动。

为了搭建更加顺利,先介绍几个docker命令:

#运行docker镜像,不再多说
docker run 一大堆参数
#查看所有的docker镜像
docker images
#查看所有的docker运行实例
docker ps -a
#停止/杀死/删除/重启 docker实例
docker stop/kill/rm/restart
#查看dockerlog
docker logs 实例名或ID

其中docker logs比较重要,docker实例出了什么问题可以查看log。

启动过程:

1、启动nginx

一般来说,nginx肯定要第一个启动的,因为它要转发不同url到不同的模块,启动nginx需要一个nginx.conf 配置文件,官方给的配置文件比较复杂,而且启用了ssl,我想大部分公司都不会在内网用ssl吧,所以就修改了一个简单的版本如下,这里的配置需要你根据情况来修改:

daemon off;
worker_processes 4;

events {
worker_connections 1024;
}

http {
upstream stf_app {
server 192.168.3.16:3100 max_fails=0;
}

upstream stf_auth {
server 192.168.3.16:3200 max_fails=0;
}

upstream stf_storage_apk {
server 192.168.3.16:3300 max_fails=0;
}

upstream stf_storage_image {
server 192.168.3.16:3400 max_fails=0;
}

upstream stf_storage {
server 192.168.3.16:3500 max_fails=0;
}

upstream stf_websocket {
server 192.168.3.16:3600 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 stf.test;
keepalive_timeout 70;
root /dev/null;

resolver 127.0.0.1 valid=300s;
resolver_timeout 10s;

# Handle stf-provider@floor4.service
location ~ "^/d/floor4/([^/]+)/(?<port>[0-9]{5})/$" {
proxy_pass http://192.168.3.16:$port/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
}

location /auth/ {
proxy_pass http://stf_auth/auth/;
}

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 -d -v /home/blueshark/stf/nginx/nginx.conf:/etc/nginx/nginx.conf:ro --name nginx --net host nginx nginx

启动nginx以后就可以访问你的网站,看看有没起来,正常起来的现象是报一个nginx的错误,没错,是报一个错误,因为其他服务还没起。

好了,nginx起来了,下面就是db了。

2、启动rethinkdb

数据库当然要比其他模块要早一点儿启动,否则影响连接嘛,rethinkdb启动命令

docker run -d --name rethinkdb -v /srv/rethinkdb:/data --net host rethinkdb rethinkdb --bind all --cache-size 8192 --http-port 8090

启动完去用浏览器访问一下8090端口看看,如果能看到rethinkdb的管理界面就是OK的。

3、给数据库建表

rethinkdb启动以后,要给stf建立相关的表,openstf/stf镜像提供了这个功能,执行下面这条命令即可

docker run -d --name stf-migrate --net host  openstf/stf stf migrate

可以使用docker logs来查看命令的执行状态

docker logs stf-migrate

4、下面要启动一堆storage等,这些模块在stf截图和安装app时会用到


docker run -d --name storage-plugin-apk-3300 -p 3300:3000 --dns 192.168.1.5 openstf/stf stf storage-plugin-apk --port 3000 --storage-url http://stf.test/
docker run -d --name storage-plugin-image-3400 -p 3400:3000 --dns 192.168.1.5 openstf/stf stf storage-plugin-image --port 3000 --storage-url http://stf.test/
docker run -d --name storage-temp-3500 -v /mnt/storage:/data -p 3500:3000 --dns 192.168.1.5 openstf/stf stf storage-temp --port 3000 --save-dir /data

这里要注意几点,stf官方docker镜像使用的google dns,但是大部分公司都有自己的内网域名,因此必须换成自己的dns,否则内网域名没法访问。如果你有需要持久保存的数据,最好用-v对docker中的目录进行映射。这里使用-p参数把docker内的端口绑定到主机端口,否则都是3000会冲突。

5、三方代理

说实话,这个东西我也是很懂,感觉是跟总线一样的东西,这个模块的启动顺序不一定放到这里,反正我是按照这个顺序来的,能工作

docker run -d  --name triproxy-app  --net host  openstf/stf  stf triproxy app   --bind-pub "tcp://*:7150"   --bind-dealer "tcp://*:7160"  --bind-pull "tcp://*:7170"
docker run -d --name triproxy-dev --net host openstf/stf stf triproxy dev --bind-pub "tcp://*:7250" --bind-dealer "tcp://*:7260" --bind-pull "tcp://*:7270"

6、登录授权

登录授权的重要性不言自明,没人希望自己的手机被别人随意使用,最后也不知道谁用了。

docker run -d --name stf-auth3200 -e "SECRET=YOUR_SESSION_SECRET_HERE" -p 3200:3000 --dns 192.168.1.5 openstf/stf stf auth-mock --port 3000  --app-url http://stf.test/

默认情况下的mock登录随便输入name和email登录,当然可以自己接入自己的授权系统。

7、stf-app

这个就是主web界面了。

docker run -d --name stf-app3100 --net host -e "SECRET=YOUR_SESSION_SECRET_HERE" -p 3100:3000 openstf/stf stf app --port 3100 --auth-url http://stf.test/auth/mock/ --websocket-url http://stf.test/

启动到这里,访问stf.test网站,应该有点儿模样了。

8、stf-processor、websocket、reaper

processor和websocket不知道具体是干什么的,这和消息队列好像有点儿关系,但是reaper是用来不断监控手机的在线状态的。

#stf-processor
docker run -d --name stf-processor --net host openstf/stf stf processor stf-processor.service --connect-app-dealer tcp://192.168.3.16:7160 --connect-dev-dealer tcp://192.168.3.16:7260

#websocket 3600
docker run -d --name websocket -e "SECRET=YOUR_SESSION_SECRET_HERE" --net host openstf/stf stf websocket --port 3600 --storage-url http://stf.test/ --connect-sub tcp://192.168.3.16:7150 --connect-push tcp://192.168.3.16:7170

#reaper
docker run -d --name reaper --net host openstf/stf stf reaper dev --connect-push tcp://192.168.3.16:7270 --connect-sub tcp://192.168.3.16:7150 --heartbeat-timeout 30000

至此为至,stf主框架已经搭起来了,就等provider给提供手机了。

9、provider

provider的作为就是给主框架提供手机,注意,provider可以运行在同一台机器,也可以运行在其他机器,但是,这台机器一定要可直接访问的,因为手机屏幕其实就是从这里出来的。

在provider机器上一定要先运行adb的docker,毕竟安卓手机要依赖adb调试

#adb services
docker run -d --name adbd --privileged -v /dev/bus/usb:/dev/bus/usb --net host sorccu/adb:latest

下面就可以运行provider了

sudo docker run -d --name provider1 --net host openstf/stf stf provider --name "provider-test" --connect-sub tcp://192.168.3.16:7250 --connect-push tcp://192.168.3.16:7270 --storage-url http://192.168.3.16 --public-ip 192.168.3.16 --min-port=15000 --max-port=25000 --heartbeat-interval 20000 --screen-ws-url-pattern "ws://stf.test/d/floor4/<%= serial %>/<%= publicPort %>/"

到这里,整个基于docker的stf生产环境就搭建好了,只要加上域名和授权控制,就可以邀请别人使用了!

三、STF开发

如果你和我一样,觉得限制使用别人做好的东西很不爽,你肯定想要自己对STF框架进行开发,加入一些特色功能。但是,要想通过源码来把STF跑通,还真没有那么容易,其中大大小小的坑令人防不胜防。

1、 安装nodejs。

stf是用nodejs来编写的,当然要nodejs来运行,如果你用的是ubuntu系统执行下面两条命令就可以安装好了

curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
sudo apt-get install -y nodejs

也可以安装6.x版本的

curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install -y nodejs

最好再安装一些编译的工具

sudo apt-get install -y build-essential

如果你用的是其他系统就恕不奉告了,上面都是从nodejs官网上抄的,可以自己去上面瞅瞅。

2、既然从源码来搞它,先拉源码吧,地址 https://github.com/openstf/stf.git
养成好习惯,拉下代码先看看分支,很多项目的master分支不能正常运行。stf仓库有多个分支,其中最重的就是master和2.0.0分支,看了下github,似乎2.0.0已经发布了,1.1.1才发布,不知道怎么回事,不过看到master分支经常更新,就用master分支来搞吧。

3、根据monkey前两天的介绍,先安装所需要的module吧,在stf项目根目录执行

npm install

等待完成,很慢吧?可以换成淘宝的npm镜像,地址 http://npm.taobao.org/ ,首先执行

npm install -g cnpm --registry=https://registry.npm.taobao.org

以后就可以用cnpm代替npm命令了,cnpm用的是淘宝镜像。注意cnpm要求nodejs版本在4.x以上。
下面执行

cnpm install

速度很快,然后就遇到了下面的问题

[stf@1.1.1] scripts.prepublish: "bower install && not-in-install && gulp build || in-install"
bower ESUDO Cannot be run with sudo

Additional error details:
Since bower is a user command, there is no need to execute it with superuser permissions.
If you're having permission errors when using bower without sudo, please spend a few minutes learning more about how your system should work and make any necessary repairs.

http://www.joyent.com/blog/installing-node-and-npm
https://gist.github.com/isaacs/579814

You can however run a command with sudo using --allow-root option
Error: Run "sh -c bower install && not-in-install && gulp build || in-install" error, exit code 1
Error: Run "sh -c bower install && not-in-install && gulp build || in-install" error, exit code 1
at ChildProcess.proc.on.code (/usr/lib/node_modules/cnpm/node_modules/runscript/index.js:67:21)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at maybeClose (internal/child_process.js:852:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5)

这里提示bower不能用root安装,其实我也不知道bower到底是什么鬼,我喜欢用root账户来搞,这也不是一个好习惯,主要是非root账户问题提醒没有写入权限,百度一下,发现只要加一个 –allow-root就行了,打开项目根目录下 package.json 文件,在bower那一行加上–allow-root就行了,然后再次运行cnpm install
然后bower balabala安装一大堆东西,duang!又报了一个错!

bower ng-context-menu#~1.0.5    ECMDERR Failed to execute "git ls-remote --tags --heads git://github.com/AdiDahan/ng-context-menu.git", exit code of #128 fatal: unable to connect to github.com: github.com[0: 192.30.252.131]: errno=Connection timed out

Additional error details:
fatal: unable to connect to github.com:
github.com[0: 192.30.252.131]: errno=Connection timed out
Error: Run "sh -c bower install --allow-root && not-in-install && gulp build || in-install" error, exit code 1
Error: Run "sh -c bower install --allow-root && not-in-install && gulp build || in-install" error, exit code 1
at ChildProcess.proc.on.code (/usr/lib/node_modules/cnpm/node_modules/runscript/index.js:67:21)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at maybeClose (internal/child_process.js:852:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5)

遇到这种time out问题我一般是多试几次就行了,可是这次怎么试都不奏效了,哎,还是多分析一下错误的原因吧,提示这个命令fail

git ls-remote --tags --heads git://github.com/AdiDahan/ng-context-menu.git

手动执行一下看看,果然是fail,突然灵光一闪,我们都是用https://github.com/https试试?,改成

git ls-remote --tags --heads https://github.com/AdiDahan/ng-context-menu.git

果然可以了,那么在哪里改呢,找了半天没找到,google一下,有人提到git自带把git://替换成https://的功能,简直是雪中送炭啊!

执行一下下面的命令就行了

git config --global url."https://".insteadOf git://

如果你不放心,可以去git的config文件中看一下有没配置成功。再执行一下前面git ls-remote命令,这下子不报错了。

下面继续进行cnpm install命令,又遇到下面的问题

[stf@1.1.1] scripts.prepublish: "bower install --allow-root && not-in-install && gulp build || in-install"
(node:1305) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
module.js:442
throw err;
^

Error: Cannot find module 'strip-json-comments'
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/root/stf-master/node_modules/.npminstall/eslint/2.13.0/eslint/lib/config/config-file.js:23:21)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
Error: Run "sh -c bower install --allow-root && not-in-install && gulp build || in-install" error, exit code 1
Error: Run "sh -c bower install --allow-root && not-in-install && gulp build || in-install" error, exit code 1
at ChildProcess.proc.on.code (/usr/lib/node_modules/cnpm/node_modules/runscript/index.js:67:21)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at maybeClose (internal/child_process.js:852:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5)

还是先升级graceful-fs吧,然后再安装strip-json-comments,执行

cnpm install graceful-fs
cnpm install strip-json-comments

继续cnpm install,终于执行完了,见到了结尾

chunk    {6} entry/commons.entry.js (entry/commons.entry.js) 0 bytes [rendered]

ERROR in ENOENT: no such file or directory, scandir '/root/stf-master/node_modules/.npminstall/node-sass/3.8.0/node-sass/vendor'
@ ./res/web_modules/nine-bootstrap/nine-bootstrap.scss 4:14-197
[13:35:01] Finished 'webpack:build' after 16 s
INF/webpack:config 1645 [*] Build progress 100% (complete)
All packages installed (use 25s, speed 0B/s, json 0(0B), tarball 0B)

等等,还是有点错误,删除node_modules/.npminstall/node-sasss目录,重新执行cnpm install

这样就好了,如重复执行cnpm install命令,就不会有错误了。

这时执行stf命令是找不到的,最后来一个cnpm link就行了,然后就可以stf local了

stf local的时候还是有错误:

INF/util:procutil 15 [*] Forking "/app/lib/cli.js poorxy --port 7100 --app-url http://localhost:7105/ --auth-url http://localhost:7120/ --api-url http://localhost:7106/ --websocket-url http://localhost:7110/ --storage-url http://localhost:7102/ --storage-plugin-image-url http://localhost:7103/ --storage-plugin-apk-url http://localhost:7104/"
/app/node_modules/zmq/node_modules/bindings/bindings.js:83
throw e
^

Error: libzmq.so.3: cannot open shared object file: No such file or directory
at Error (native)
at Object.Module._extensions..node (module.js:568:18)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at bindings (/app/node_modules/zmq/node_modules/bindings/bindings.js:76:44)
at Object.<anonymous> (/app/node_modules/zmq/lib/index.js:6:30)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
FTL/cli:local 15 [*] Child process had an error ExitError: Exit code "1"
at ChildProcess.<anonymous> (/app/lib/util/procutil.js:49:23)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at Process.ChildProcess._handle.onexit (internal/child_process.js:204:12)
INF/cli:local 15 [*] Shutting down all child processes
/app/node_modules/zmq/node_modules/bindings/bindings.js:83
throw e
^

Error: libzmq.so.3: cannot open shared object file: No such file or directory
at Error (native)
at Object.Module._extensions..node (module.js:568:18)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at bindings (/app/node_modules/zmq/node_modules/bindings/bindings.js:76:44)
at Object.<anonymous> (/app/node_modules/zmq/lib/index.js:6:30)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)

这里提示缺少zmp的一个库文件,如果你有兴趣,可以把zmq装一遍,就没问题,但是我有更简单的方法,去openstf/stf的docker镜像中把libzmq.*这类文件拷到你的系统中就行了,库目录是/usr/lib/x86_64-linux-gnu,下面还有一个类似的错误,同样拷一下库文件就行了,什么?你不知道怎么从docker镜像中很外拷东西?我只能说一句,查查docker -v命令,其他真的不能多说了。

localhost:7106/ --websocket-url http://localhost:7110/ --storage-url http://localhost:7102/ --storage-plugin-image-url http://localhost:7103/ --storage-plugin-apk-url http://localhost:7104/"
/app/node_modules/zmq/node_modules/bindings/bindings.js:83
throw e
^

Error: libpgm-5.1.so.0: cannot open shared object file: No such file or directory
at Error (native)
at Object.Module._extensions..node (module.js:568:18)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at bindings (/app/node_modules/zmq/node_modules/bindings/bindings.js:76:44)
at Object.<anonymous> (/app/node_modules/zmq/lib/index.js:6:30)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
FTL/cli:local 112 [*] Child process had an error ExitError: Exit code "1"
at ChildProcess.<anonymous> (/app/lib/util/procutil.js:49:23)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at Process.ChildProcess._handle.onexit (internal/child_process.js:204:12)
INF/cli:local 112 [*] Shutting down all child processes

下面这个错误不一定每个人都会遇到,如果遇到了还真不好解决

INF/util:procutil 209 [*] Forking "/app/lib/cli.js poorxy --port 7100 --app-url http://localhost:7105/ --auth-url http://localhost:7120/ --api-url http://loc alhost:7106/ --websocket-url http://localhost:7110/ --storage-url http://localhost:7102/ --storage-plugin-image-url http://localhost:7103/ --storage-plugin-a pk-url http://localhost:7104/"
/app/node_modules/zmq/node_modules/bindings/bindings.js:83
throw e
^

Error: Module version mismatch. Expected 48, got 46.
at Error (native)
at Object.Module._extensions..node (module.js:568:18)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at bindings (/app/node_modules/zmq/node_modules/bindings/bindings.js:76:44)
at Object.<anonymous> (/app/node_modules/zmq/lib/index.js:6:30)
at Module._compile (module.js:541:32)
at Object.Module._extensions..js (module.js:550:10)
at Module.load (module.js:458:32)
at tryModuleLoad (module.js:417:12)
at Function.Module._load (module.js:409:3)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
FTL/cli:local 209 [*] Child process had an error ExitError: Exit code "1"
at ChildProcess.<anonymous> (/app/lib/util/procutil.js:49:23)
at emitTwo (events.js:106:13)
at ChildProcess.emit (events.js:191:7)
at Process.ChildProcess._handle.onexit (internal/child_process.js:204:12)
INF/cli:local 209 [*] Shutting down all child processes

这里提示zmq的version有问题,我尝试把zmq重装了几次,还是不行,最终google了一把,原来不是zmq的问题,是nodejs版本太高了,我是个喜新厌旧(除了人)的人,所有的东西都要用最新的,系统用ubuntu 16.04,nodejs要用6.x版本,这次就栽在这里了,其实只要把nodejs版本换成node-v4.4.5就ok了,具体怎么换就不说了,可以了解一下cnpm install -g n,这里吐槽一下nodejs的版本问题,感觉nodejs在0.12徘徊了几年,不知道什么时候突然就到4.x和6.x了,看来大跃进是国际惯例啊。

到这里stf已经完全可以正常运行了,终于见到了久违的登录界面!

cnpm link会把stf全集安装到系统中,cnpm link的时候还报了一个警告

root@ubuntu-mtc:~/git/stf-dev/stf-github# cnpm link
npm WARN skippingAction Module is inside a symlinked module: not running remove pinkie-promise@2.0.1 node_modules/node-sass/node_modules/meow/node_modules/read-pkg-up/node_modules/find-up/node_modules/path-exists/node_modules/pinkie-promise
npm WARN lifecycle stf@1.1.1~prepublish: cannot run in wd %s %s (wd=%s) stf@1.1.1 bower install --allow-root&& not-in-install && gulp build || in-install /root/git/stf-dev/stf-github
npm WARN optional Skipping failed optional dependency /karma/chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.12
npm WARN optional Skipping failed optional dependency /webpack/watchpack/chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.12
/usr/bin/stf -> /usr/lib/node_modules/stf/bin/stf
/usr/lib/node_modules/stf -> /root/git/stf-dev/stf-github

fsevents是mac系统上的东西,linux上不能安装,所以报warning,但是其他warning我也没搞懂是怎么回事,但是不影响运行。

以后每次修改stf源码,都要执行一下cnpm install,最好再cnpm link一下,这样修改的代码就生效了。

四、自己制作docker镜像

前面说过,生产环境中的stf是部署在docker当中的,我们修改完stf源码以后肯定要更新到镜像里才可以。制作docker镜像有两种方式:

  • 自己修改完代码以后,替换掉原openstf/stf镜像中的/app目录即可,这样是最简单的方法,原系统有很多链接库也不用管了,但是有个缺点,就是依赖于原docker镜像的操作系统,stf作者说是coreos系统,据说是专门为docker而生,但是这个系统我连自己安装软件都不会。

  • 完全自己从零开始制作镜像。这个工作的挑战性比较大,可以会遇到很多问题,特别是一些依赖的库,还有什么安装软件时unmet问题,太坑了!其实在stf项目中有一个作者的Dockerfile,但是我基于这个Dockerfile制作镜像始终都没有成功。

下面分别介绍两种制作镜像的方法:

1、替换原镜像/app文件夹。准备好你自己修改好的stf目录,假设为stf-github,用下面的Dockerfile生成新镜像即可

FROM openstf/stf

USER root
RUN rm -rf /app
ADD ./stf-github /app

USER root是因为原镜像不用root账户不能删除/app目录。

2、从零开始构建自己的镜像。

这里我使用的是ubuntu系统,所以首先要docker pull一个ubuntu系统镜像

docker pull ubuntu

准备好修改过的stf-github文件夹,和Dockerfile放在同一目录下,另外准备原openstf/stf镜像中的两种lib文件,在/usr/lib/x86_64-linux-gnu目录下,还有最好换成国内的ubuntu源,使用下面的Dockerfile创建镜像

FROM ubuntu

ADD ./stf-github /app

EXPOSE 3000

#replace source
ADD ./sources.list /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y vim curl git sudo

ADD /x86_64-linux-gnu/libzmq.* /usr/lib/x86_64-linux-gnu/
ADD /x86_64-linux-gnu/libpgm* /usr/lib/x86_64-linux-gnu/

#install npm
curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
sudo apt-get install -y nodejs

RUN npm install -g cnpm --registry=https://registry.npm.taobao.org

WORKDIR /app

RUN cnpm install -g graceful-fs
RUN cnpm install
RUN cnpm link

如果构建镜像过程中提示缺少某个模块,自己加上就行了,这个不是绝对的,上面的graceful-fs是因为有警告说graceful-fs的版本太低,就顺便更新一下了。其实也可以在宿主机上把stf全部弄好以后再整个放到docker中,都一样的。

至此,自己的镜像也构造完了,至于源码怎么修改,等我研究透了再说了

后续更新:

文章中说的复制libzmq的东西有点儿麻烦,事实上每个系统都有安装zmq的命令比,如说ubuntu上用apt-cache search zmq可以找到zmq的库(PS:说实话我没搞清楚到底是哪个,反正装了几个以后就可以了,试试libzmq-dev),mac上可以用brew install zmq来安装。

* 注:本文来自网络投稿,不代表本站立场,如若侵犯版权,请及时知会删除