在之前的博客《部署Rasa框架到Kubernetes集群并分配GPU资源》中,我们成功在k8s集群中部署了rasa框架。这一节我们来看看如何结合statefulset对这个部署进行改进。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
之前我们通过Deployment的方式去部署的Rasa框架,但是这样有个很明显的问题,除非对Deployment更换image,不然很难将训练的模型更新到框架中,每次测试都要生成新的image,很不方便。于是准备
/app
目录进行一个volume映射,直接更新本地目录就达到了更新容器模型的目的,便于快速测试综合以上两点需求,发现Statefulset可以圆满完成任务。
以下的yaml文件都放在了我的Github仓库。
因为我这个集群只有一台GPU的node被设置来分配pod,本来想试试能不能直接在node上搞一个目录类似volume一样做为pv,后来发现这种操作已经被官方文档给否决
HostPath (Single node testing only – local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)
于是老老实实按照之前使用的NFS方法,单独搞了台机器弄了三个目录过来
root@control-plane-1:~# showmount -e 172.29.56.177
Export list for 172.29.56.177:
/share/nfs3 172.29.0.0/16
/share/nfs2 172.29.0.0/16
/share/nfs1 172.29.0.0/16
NFS操作可以参考《Centos7搭建NFS文件共享服务器以及客户端连接》
同时创建出3个pv来
root@control-plane-1:~# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
test-pv-nfs1 5Gi RWO Retain Available gold 6s
test-pv-nfs2 8Gi RWO Retain Available gold 6s
test-pv-nfs3 10Gi RWO Retain Available gold 6s
NFS创建PV操作可以参考《【Kubernetes 014】持久存储PersistentVolume原理以及配合StatefulSet实现有状态服务实际操作详解》
创建yaml文件rasa-statefulset-noinit.yaml
如下
apiVersion: v1
kind: Service
metadata:
name: rasa-headless-service
namespace: default
labels:
app: rasa-headless
spec:
type: ClusterIP
clusterIP: None #<-- Careful
selector:
app: rasa-reception
ports:
- name: http
port: 5005
targetPort: 5005
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rasa-reception
spec:
selector:
matchLabels:
app: rasa-reception
serviceName: rasa-headless-service
replicas: 1
template:
metadata:
labels:
app: rasa-reception
spec:
containers:
- name: mynginx
image: myrasa:v2
ports:
- containerPort: 5005
name: http
command: ["/bin/sh","-c","rasa run --enable-api"]
livenessProbe:
httpGet:
port: 5005
path: /
initialDelaySeconds: 1
periodSeconds: 3
resources:
limits:
aliyun.com/gpu-mem: 1
volumeMounts:
- name: rasa-app
mountPath: /app
- name: rasa-tmp
mountPath: /tmp
volumes:
- name: rasa-tmp
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: rasa-app
spec:
accessModes:
- ReadWriteOnce
storageClassName: gold
resources:
requests:
storage: 5Gi
根据无头服务创建statefulset以及Rasa的基本部署就不赘述了,这里主要注意两点
command
中的init命令,只保留了run命令。原因就是我们会直接采用我们训练好的模型,不再需要默认模型了/app
数据目录,而volume对应到临时/tmp
目录,可以看出两者的使用场景是完全不同的成功创建无头服务和statefulset
root@control-plane-1:~/rasa-test# kubectl apply -f rasa-statefulset-noinit.yaml
service/rasa-headless-service unchanged
statefulset.apps/rasa-reception created
查看发现绑定到了rasa-pv-nfs1
这个pv,也就对应到NFS服务器的/share/nfs1
目录
root@control-plane-1:~/rasa-test# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
rasa-app-rasa-reception-0 Bound rasa-pv-nfs1 5Gi RWO gold 53s
下面将事先训练好的数据同步到NFS服务器的/share/nfs1
目录中
[root@ai-asterisk ~]# cd /share/nfs1
[root@ai-asterisk nfs1]# ll
total 40
-rw-r--r--. 1 root root 14408 Jun 4 17:59 actions.py
-rw-r--r--. 1 root root 770 Jun 4 17:59 config.yml
-rw-r--r--. 1 root root 938 Jun 4 17:59 credentials.yml
drwxr-xr-x. 2 root root 38 Jun 4 17:59 data
-rw-r--r--. 1 root root 7751 Jun 4 17:59 domain.yml
-rw-r--r--. 1 root root 1454 Jun 4 17:59 endpoints.yml
-rw-r--r--. 1 root root 0 Jun 4 17:59 __init__.py
drwxr-xr-x. 2 root root 36 Jun 4 17:59 models
drwxr-xr-x. 2 root root 67 Jun 4 17:59 __pycache__
-rw-r--r--. 1 root root 1924 Jun 4 17:59 story_graph.dot
drwxr-xr-x. 2 root root 35 Jun 4 17:59 tests
然后删除statefulset的pod,使其重启达到重新启动rasa服务器加载新模型的目的
root@control-plane-1:~/rasa-test# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
curl-6bf6db5c4f-fr2xv 1/1 Running 0 16d 10.244.2.2 gpu-node <none> <none>
rasa-reception-0 1/1 Running 0 5m18s 10.244.2.18 gpu-node <none> <none>
root@control-plane-1:~/rasa-test# kubectl delete pod/rasa-reception-0
pod "rasa-reception-0" deleted
root@control-plane-1:~/rasa-test# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
curl-6bf6db5c4f-fr2xv 1/1 Running 0 16d 10.244.2.2 gpu-node <none> <none>
rasa-reception-0 1/1 Running 0 36s 10.244.2.19 gpu-node <none> <none>
可以看到pod被自动拉起,ip也变了。因为pv的数据生命周期和pod的生命周期是独立的,所以pod的重启不会影响到模型的数据。
下面试着访问下pod的5005端口
root@control-plane-1:~/rasa-test# curl 10.244.2.19:5005/status
{"model_file":"models\/20200526-132449.tar.gz","fingerprint":{"config":"99914b932bd37a50b983c5e7c90ae93b","core-config":"ad70948032b90c0e3231157217198d63","nlu-config":"82833edd77c80380860cf74a815a1707","domain":1239901306164435814,"nlg":"569c6d8fc8eb2342c0dda03e83ad88a9","messages":1335623048012146043,"stories":475775394484243566,"trained_at":1590470432.234018,"version":"1.10.0"},"num_active_training_jobs":0}
可以看到模型已经被导入了。
这样以后有了新的模型,只需要同步到NFS的目录中,然后删除pod,就能达到更新的目的了。
为了能从外部访问,还需要给statefulset创建一个NodePort类型的服务。
创建yaml文件rasa-service.yaml
如下
apiVersion: v1
kind: Service
metadata:
name: rasa-service
spec:
type: NodePort
selector:
app: rasa-reception
ports:
- name: http
port: 5005
targetPort: 5005
nodePort: 31111
成功创建
root@control-plane-1:~/rasa-test# kubectl apply -f rasa-service.yaml
service/rasa-service created
root@control-plane-1:~/rasa-test# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16d
rasa-headless-service ClusterIP None <none> 5005/TCP 20h
rasa-service NodePort 10.106.162.176 <none> 5005:31111/TCP 29s
31111端口能返回内容表示配置成功
在之前的博客《【Kubernetes 021】Python客户端自动创建和销毁Kubernetes资源详解》中我们成功用python客户端创建和删除了Deployment和Service,如果想要对这里的statefulset进行操作也类似。
因为statefulset对应着apps/v1
这个API,所以去apps_v1_api.py文件中去查找对应的方法,如下
之后的操作就和下面的类似,大家可以自己试下
def create_statefulset(api_instance, body):
api_response = api_instance.create_namespaced_stateful_set(
body=body,
namespace='default',
)
print(api_response)
def delete_statefulset(api_instance, name):
api_response = api_instance.delete_namespaced_stateful_set(
name=name,
namespace='default',
)
print(api_response)
statefulset因为pod的重启不会影响到数据的保存,所以尤其适合这种需要重启pod来加载配置的情况,在测试环境是非常好用的。
当然,真正更新到正式版本,还是需要生成新的image,然后通过set或者patch的方式去更新,以便进行追踪和回滚等操作。