学会了k8s的基本使用,下面就可以开始一些进阶的学习了,例如二次开发。想象有一个网页管理工具,管理人员只需要点击一个按钮,后台就会自动按照预先设定好的参数创建资源。当资源使用完毕,又可以点击另一个按钮自动删除资源。这一节我们就一起来学习如何用python来完成这一目的。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
之前我们一直用的kubectl这个客户端去连接的API server,其通过~/.kube/config
文件内的信息完成验证,进而进行API交互。所以其实理论上,只要有程序能获得同样的认证信息,就可以和kubectl一样完成这样的交互。于是官方用各种语言写了这样的一个客户端,并开源在了Github上,见这里。其中星标最多的当属python客户端了,于是我们就来试一试这款官方推出的python客户端。
对于用户config文件内容感兴趣的可以参考我的另一篇博客《【Kubernetes 018】cfssl创建证书并结合RBAC的RoleBinding配置新用户config文件操作详解》
官方的Readme里面已经给出了各种代码示例,从这些示例里面大概能总结出操作的一些基本步骤,我这里按照我自己的理解总结如下
注意,因为k8s中的API分支多大几十种,在选择的时候要仔细点,不然会出现API版本不匹配的报错。
我这里操作使用的环境如下
还是以创建《部署Rasa框架到Kubernetes集群并分配GPU资源》同样的资源来作为操作演示。
直接利用pip安装即可,或者在pycharm里面安装,如下
之后就可以在项目中引入了
from kubernetes import client, config
我这里创建了一个叫k8s_client
的项目,然后将config
文件拷贝到项目的根目录,用下面的方法从文件导入认证信息
BASE_URL = '/home/fuhx/python_projects/k8s/k8s_client/'
cf = config.load_kube_config(path.join(BASE_URL, 'config'))
如果要创建Deployment,创建一个apps_v1_api.py的API实例如下
apps_v1_api = client.AppsV1Api()
如果要创建Service,创建一个core_v1_api.py的API实例如下
core_v1_api = client.CoreV1Api()
我这里在项目根目录下创建了yaml
目录,里面放了两个yaml文件rasa-deployment.yaml
和rasa-service.yaml
分别创建Deployment和Service。这里的两个文件是上一篇博客《部署Rasa框架到Kubernetes集群并分配GPU资源》中用到的文件,但是添加了一些字段,如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: rasa-deployment-2
spec:
replicas: 1
selector:
matchLabels:
app: rasa-2
template:
metadata:
labels:
app: rasa-2
spec:
containers:
- name: myrasa
image: myrasa:v2
ports:
- containerPort: 5005
command: ["/bin/sh","-c","rasa init --no-prompt;rasa run --enable-api"]
livenessProbe:
httpGet:
port: 5005
path: /
initialDelaySeconds: 1
periodSeconds: 3
resources:
limits:
aliyun.com/gpu-mem: 1
volumeMounts:
- mountPath: /app
name: rasa-app
- mountPath: /tmp
name: rasa-tmp
volumes:
- name: rasa-app
emptyDir: {}
- name: rasa-tmp
emptyDir: {}
需要注意,这里除了修改了下名字以与现有资源不冲突以外,还添加了下面这一段
selector:
matchLabels:
app: rasa-2
直接用kubectl的时候不用添加但是这里必须要添加,或许是因为这里的规范比较严格,不然在执行的时候会出现如下报错
...
...
kubernetes.client.rest.ApiException: (422)
Reason: Unprocessable Entity
HTTP response headers: HTTPHeaderDict({'Content-Type': 'application/json', 'Date': 'Fri, 22 May 2020 08:04:23 GMT', 'Content-Length': '672'})
HTTP response body: {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"Deployment.apps \"rasa-deployment-2\" is invalid: [spec.selector: Required value, spec.template.metadata.labels: Invalid value: map[string]string{\"app\":\"rasa-2\"}: `selector` does not match template `labels`]","reason":"Invalid","details":{"name":"rasa-deployment-2","group":"apps","kind":"Deployment","causes":[{"reason":"FieldValueRequired","message":"Required value","field":"spec.selector"},{"reason":"FieldValueInvalid","message":"Invalid value: map[string]string{\"app\":\"rasa-2\"}: `selector` does not match template `labels`","field":"spec.template.metadata.labels"}]},"code":422}
Process finished with exit code 1
Service需要注意修改端口避免跟现有资源冲突
apiVersion: v1
kind: Service
metadata:
name: rasa-service-2
spec:
type: NodePort
selector:
app: rasa-2
ports:
- name: http
port: 5005
targetPort: 5005
nodePort: 31112
这里因为两个资源属于不同API分支,要用不同的类实例去创建,所以不能放在同一个文件用用---
隔开。
之后就可以分别导入备用了
with open(path.join(BASE_URL, 'yaml/rasa_deployment.yaml')) as f:
dep = yaml.safe_load(f)
with open(path.join(BASE_URL, 'yaml/rasa_service.yaml')) as f:
svc = yaml.safe_load(f)
yaml和json一样用直接load进来变为字典格式,不过yaml为了安全起见有一个safe_load命令,只会去解析一些基本yaml语法,一般情况下用safe得就可以了
分别调用API实例得方法即可,这里单独创建了4个函数用来处理
def create_deployment(api_instance, body):
api_response = api_instance.create_namespaced_deployment(
body=body,
namespace='default')
print(api_response)
# print("Deployment created. status='%s'" % str(api_response.status))
def delete_deployment(api_instance, name):
api_response = api_instance.delete_namespaced_deployment(
name=name,
namespace='default'
)
print(api_response)
def create_service(api_instance, body):
api_response = api_instance.create_namespaced_service(
body=body,
namespace='default'
)
print(api_response)
def delete_service(api_instance, name):
api_response = api_instance.delete_namespaced_service(
name=name,
namespace='default'
)
print(api_response)
最后在main函数中去调用即可,我这里就不写复杂得逻辑判断了
BASE_URL = '/home/fuhx/python_projects/k8s/k8s_client/'
cf = config.load_kube_config(path.join(BASE_URL, 'config'))
core_v1_api = client.CoreV1Api()
apps_v1_api = client.AppsV1Api()
with open(path.join(BASE_URL, 'yaml/rasa_deployment.yaml')) as f:
dep = yaml.safe_load(f)
with open(path.join(BASE_URL, 'yaml/rasa_service.yaml')) as f:
svc = yaml.safe_load(f)
create_deployment(apps_v1_api, dep)
# create_service(core_v1_api, svc)
# delete_deployment(apps_v1_api, 'rasa-deployment-2')
# delete_service(core_v1_api, 'rasa-service-2')
如果API方法没有报错就可以认为资源的操作没有问题,这时候就可以去集群内部检查下资源状况,当然安装了ELK或者Prometheus的就直接图形界面查看了。
root@control-plane-1:~# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
curl-6bf6db5c4f-fr2xv 1/1 Running 0 2d3h 10.244.2.2 gpu-node <none> <none>
nvidia-deployment-5f4bbd9457-2rj7r 1/1 Running 1 2d3h 10.244.2.4 gpu-node <none> <none>
nvidia-deployment-5f4bbd9457-hfxqh 1/1 Running 1 2d3h 10.244.2.3 gpu-node <none> <none>
rasa-deployment-2-6446bc5d4d-dp229 1/1 Running 0 19s 10.244.2.12 gpu-node <none> <none>
rasa-deployment-586ff4666-hf6xm 1/1 Running 0 2d2h 10.244.2.8 gpu-node <none> <none>
root@control-plane-1:~# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d23h
rasa-service NodePort 10.108.95.50 <none> 5005:31111/TCP 43h
rasa-service-2 NodePort 10.102.214.112 <none> 5005:31112/TCP 31s
从另外一台机器上试一下Service的端口服务
[root@ai-asterisk ~]# curl -X POST 172.29.57.202:31112/webhooks/rest/webhook -d '{"sender":"xiaofu","message":"How are you?"}'
[{"recipient_id":"xiaofu","text":"I am a bot, powered by Rasa."}]
这里只是抛砖引玉,简单找了两个资源尝试了一下添加和删除,更多的API等着我们在实际项目中慢慢探索。有了python客户端我们就可以将k8s的自动化操作融入到一些运维平台中了,如果有机会我们以后再分享更多这方面的内容。