netron基本服务部署
Linux 服务器上部署 netron 服务很简单。我用的 python 方式:
# 安装netron
pip3 install netron
# 启动netron,端口默认是8080,可以通过 -p 参数修改
netron
之后就能通过 ip:8080 访问了。
使用的时候,从本地选择模型文件导入就行了。
如果是 docker 方式部署,启动的时候需要增加 --host 参数,才能被容器外部访问:
netron --host 0.0.0.0 -p 8080 -b
场景
由于我做的平台中,模型文件不是在用户本地,而是在公司的云存储上的。用户只能在平台中使用各种模型文件。那么这种情况下,上面的部署方式就没法满足我的要求了,因为没法导入模型文件!
但是也有办法解决,那就是在启动 netron 时指定模型文件路径(netron 的启动命令中可以指定模型文件路径)。
也就是说,每当用户想通过 netron 查看模型时,我都通过平台创建一个 docker 容器,并指定模型的路径!由于项目中已经有别的功能使用了 kubernetes 了,所以这里也直接使用 kubernetes 来创建容器,最后通过 ingress 对外暴露服务!
做一些定制
由于默认的 netron 实现,在打开模型后,在左上角,是提供模型导出功能的,而在我这个场景中,这个操作显然是不被允许的,所以需要做一些定制。
方法就是在 github上下载源码,修改代码,比如删除导出的功能,然后重新打包,再按跟上面一样的部署方式。
通过k8s创建容器并在启动时指定模型文件
为了防止容器创建得太多,我设定每个用户只能创建一个 netron 容器,也就是说只能同时打开一个模型文件。
提前准备好镜像,里面安装好 python 环境,以及 netron。
启动脚本
由于我暂时没找到在 kubernetes 创建容器时自动执行 netron 启动命令的方法,试了好几种方式都不行,所以将启动命令写到一个 shell 脚本中,每次创建容器前,在后台代码中先写入启动命令(主要是替换模型文件名称),然后指定 kubernetes 启动容器时执行该脚本。
netron --host 0.0.0.0 -p 8080 -b /netron/test.caffemodel
deployment
最终实现的时候 pod 名需要根据模型而修改。
这里简单起见,贴出的是 yml,实际后台实现时,用的是 fabric8,以编程方式创建 deployment,service,以及 ingress。所以 deploymentName,serviceName,ingressName 是在后台创建资源时,根据模型的不同而指定的。
模型文件和脚本都放在宿主机 /data/netron 大目录,在创建容器时挂载为容器中的 /netron 目录。
指定容器启动时执行的脚本 start.sh。
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: netron-dep
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: netron-dep
template:
metadata:
labels:
app: netron-dep
spec:
containers:
- image: netron_python:v1.0
imagePullPolicy: IfNotPresent
name: netron-container
command:
- "/bin/bash"
args:
- "/netron/script/user1/start.sh"
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /netron
name: model-path
volumes:
- name: model-path
hostPath:
path: /data/netron
service
用于在集群内访问 pod。
apiVersion: v1
kind: Service
metadata:
name: netron-svc
namespace: default
spec:
ports:
- port: 8070
targetPort: 8080
selector:
app: netron-dep
多个 service 可以都指定同样的端口
service 创建完毕后,netron 服务就可以在集群内部访问了,但是还不能在集群外部访问,比如浏览器。需要创建 ingress 来将服务暴露到外部。
当然,使用 service 的 NodePort 模式也可以对外暴露服务,但是得额外指定一个 30000 以上的端口,而且这个端口还不能是重复的。也就是说每个容器都要指定一个以为的宿主机端口以对外暴露服务,这个显然很麻烦,所以决定采用 ingress 方式。
ingress
对外暴露 netron 服务,跟某个 service 绑定。
我想要的效果是,不同模型的 netron 服务,通过访问路径来做区别:
- 模型A,通过 xx.xx.xx.xx/path1/ 来访问
- 模型B,通过 xx.xx.xx.xx/path2/ 来访问
- ......
最终的 yml 内容如下,里面最重要的就是 path 和 annotations 的配置。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: netron-igr
namespace: default
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: 'rewrite ^(/test)$ $1/ permanent;'
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /test/
backend:
serviceName: netron-svc
servicePort: 8070
根据上面的 yml,当浏览器中访问 xx.xx.xx.xx/test/ 时,就能访问到 service 对应的服务了。
path & rewrite-target
ingress 里配置的 - path: /test/ ,不代表 netron 容器中必须要提供 /test 接口,在 annotations 中增加了 rewrite-target 参数,就是用来重写请求路径的。这里配置的 /test/ 只是用来区分不同的服务的。所以说,创建容器的时候,path、ingressName、serviceName、deploymentName 要对应,这样才能通过 path 来找到相应的服务。
这里我把 path 重写为了 "/",因为在容器里面, netron 服务开放的请求路径就是 "/",在容器中访问就是通过 localhost:8080 来访问的。
注意:访问的时候最后的 "/" 是必须的,以上面的请求为例,必须是 xx.xx.xx.xx/test/,而不能是 xx.xx.xx.xx/test。当然,应该可以通过正则表达式解决,但我懒得深究了。
configuration-snippet
用于重写静态文件的路径。
上面的 path 和 rewrite-target,只是解决了请求路径的重写问题。而 netron 是有界面的,所以涉及很多静态文件,比如 js,图片之类的。这个 configuration-snippet 可以用来重写静态文件的路径。注意好每个服务的路径也是不同的就行。
至于这个 rewrite 语法,我也没仔细研究,不过上面提供的是可用的。
ssl-redirect
设置为 false,防止重定向。默认是 true 的,所以需要重新设置下。我不需要用 ssl,访问的时候也是直接用的 ip,所以这里设置为 false,否则会报错:
ingress 部署完成后,就能在浏览器上根据不同的请求路径访问对应的 netron 服务了。
xx.xx.xx.xx/test/
xx.xx.xx.xx/test2/