一、基本了解
操作K8s资源api方式:
- 原生api
- 客户端库,python客户端库
K8s支持三种客户端身份认证:
- HTTPS 证书认证:基于CA证书签名的数字证书认证(kubeconfig文件,默认路径~/.kube/config)
- HTTP Token认证:通过一个Token来识别用户(ServiceAccount)
- HTTP Base认证:用户名+密码的方式认证(1.19+已经弃用)
1.1 操作k8s API
1.安装Kubernetes客户端库。
pip install kubernetes -i https://pypi.tuna.tsinghua.edu.cn/simple
2.基于HTTPS证书认证(kubeconfig)操作k8s API。
常用资源接口类实例化 | 资源 |
---|---|
core_api = client.CoreV1Api() | namespace,pod,service,pv,pvc |
apps_api = client.AppsV1Api() | deployment,statefulset,daemonset |
networking_api = client.NetworkingV1beta1Api() | ingress |
storage_api = client.StorageV1Api() | storage_class |
from kubernetes import client,config
import os
print(os.getcwd())
kubeconfig = os.path.join(os.getcwd(),'kubeconfig.yaml') ##拿到k8s的~.kube/config文件内容。
config.load_kube_config(kubeconfig) ##指定kubeconfig配置文件(/root/.kube/config)
apps_api = client.AppsV1Api() # 资源接口类实例化,根据要操作的资源实例化不同的组。
# print(apps_api.list_deployment_for_all_namespaces())
for dp in apps_api.list_deployment_for_all_namespaces().items:
# print(dp) # 打印Deployment对象详细信息
print(dp.metadata.labels)
print(dp.metadata.name)
3.基于HTTP Token认证(ServiceAccount)操作K8s API。
#############################################################
##获取Token字符串:创建service account并绑定默认cluster-admin管理员集群角色。
# 创建用户
kubectl create serviceaccount dashboard-admin -n kube-system
# 用户授权
kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin
# 获取用户Token(1.24版本之前)
kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
# 获取用户Token(1.24版本之后)
kubectl create token dashboard-admin -n kube-system
#############################################################
from kubernetes import client,config
import os
configuration = client.Configuration()
configuration.host = "https://192.168.161.120:6443" # APISERVER地址
ca_file = os.path.join(os.getcwd(),"ca.crt") # K8s集群CA证书(/etc/kubernetes/pki/ca.crt)
configuration.ssl_ca_cert= ca_file
configuration.verify_ssl = True
##启用证书验证。
token = "eyJhbGciOiJSUzI1NiIsImtpZCI6InhBd1JkOFFocE1oX0VsRnlDcmFwQUN4ajQzblNta2FnVW1ITmk1VU12ZUEifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjk0MDQ3ODI5LCJpYXQiOjE2OTQwNDQyMjksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkYXNoYm9hcmQtYWRtaW4iLCJ1aWQiOiI2Zjg0ZTFjMC0wYTY0LTQ4NTQtYThlMy03MDZkOWYxZThkZGMifX0sIm5iZiI6MTY5NDA0NDIyOSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.GXwDKDNcCPjU23R-kYbs1Sl-73hG2p7RJShB0I8BU7cpifUhBaEKJjymx5IL_CYTl5LHRdlP0uo_sqXqrUBRBYrW5vctVq8MXLFNShSAKKLmxdIxxo4fHrWHMaRv8TU_wtKkATiXAO15q3n9YD7vN443FWK3sRuWpzh6Hvlr4dljnK_37YKiOd8eS8_dDC4BkeLo38EadnO39BwvC5lommz2LkCYMawunWkOxsI_eclPqXJ3ZkSCgEZUPD4_0CNv7D_X6S_gyMkdsCVxwFuL3EjI9ENh4GV2yKBVY69wuChlXB6vHGcYtiWzhTDdibv4l-5jIWhbwR_5CgaskKtmTQ" #指定Token字符串,下面方式获取
configuration.api_key = {"authorization": "Bearer " + token} ##固定格式。
client.Configuration.set_default(configuration)
apps_api = client.AppsV1Api()
for dp in apps_api.list_deployment_for_all_namespaces().items:
# print(dp) # 打印Deployment对象详细信息
print(dp.metadata.labels)
1.2 基本使用
1.创建一个deployment资源,名为qingjun,3个副本数,使用nginx镜像。
from kubernetes import client,config
import os
configuration = client.Configuration()
configuration.host = "https://192.168.161.120:6443" # APISERVER地址
ca_file = os.path.join(os.getcwd(),"ca.crt") # K8s集群CA证书(/etc/kubernetes/pki/ca.crt)
configuration.ssl_ca_cert= ca_file
configuration.verify_ssl = True
# 启用证书验证
token = "eyJhbGciOiJSUzI1NiIsImtpZCI6InhBd1JkOFFocE1oX0VsRnlDcmFwQUN4ajQzblNta2FnVW1ITmk1VU12ZUEifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjk0MDQ3ODI5LCJpYXQiOjE2OTQwNDQyMjksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkYXNoYm9hcmQtYWRtaW4iLCJ1aWQiOiI2Zjg0ZTFjMC0wYTY0LTQ4NTQtYThlMy03MDZkOWYxZThkZGMifX0sIm5iZiI6MTY5NDA0NDIyOSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZC1hZG1pbiJ9.GXwDKDNcCPjU23R-kYbs1Sl-73hG2p7RJShB0I8BU7cpifUhBaEKJjymx5IL_CYTl5LHRdlP0uo_sqXqrUBRBYrW5vctVq8MXLFNShSAKKLmxdIxxo4fHrWHMaRv8TU_wtKkATiXAO15q3n9YD7vN443FWK3sRuWpzh6Hvlr4dljnK_37YKiOd8eS8_dDC4BkeLo38EadnO39BwvC5lommz2LkCYMawunWkOxsI_eclPqXJ3ZkSCgEZUPD4_0CNv7D_X6S_gyMkdsCVxwFuL3EjI9ENh4GV2yKBVY69wuChlXB6vHGcYtiWzhTDdibv4l-5jIWhbwR_5CgaskKtmTQ" #指定Token字符串,下面方式获取
configuration.api_key = {"authorization": "Bearer " + token} ##固定格式。
client.Configuration.set_default(configuration)
apps_api = client.AppsV1Api()
namespace = "default"
name = "qingjun"
replicas = 3
labels = {'a':'1', 'b':'2'} # 不区分数据类型,都要加引号
image = "nginx"
body = client.V1Deployment(
api_version="apps/v1",
kind="Deployment",
metadata=client.V1ObjectMeta(name=name),
spec=client.V1DeploymentSpec(
replicas=replicas,
selector={'matchLabels': labels},
template=client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels=labels),
spec=client.V1PodSpec(
containers=[client.V1Container(
name="web",
image=image
)]
)
),
)
)
try:
apps_api.create_namespaced_deployment(namespace=namespace, body=body) ##创建命令。
except Exception as e:
status = getattr(e, "status") ##获取状态码。
if status == 400: # 400 格式错误,409 资源存在,403 没权限。
print(e)
print("格式错误")
elif status == 409:
print("deployment资源存在")
elif status == 403:
print("没权限")
2.创建一个service资源。
core_api = client.CoreV1Api()
namespace = "default"
name = "qingjun"
selector = {'a':'1', 'b':'2'} # 不区分数据类型,都要加引号
port = 80
target_port = 80
type = "NodePort"
body = client.V1Service(
api_version="v1",
kind="Service",
metadata=client.V1ObjectMeta(
name=name
),
spec=client.V1ServiceSpec(
selector=selector,
ports=[client.V1ServicePort(
port=port,
target_port=target_port
)],
type=type
)
)
try:
core_api.create_namespaced_service(namespace=namespace, body=body)
except Exception as e:
status = getattr(e, "status")
if status == 400: # 400 格式错误,409 资源存在,403 没权限。
print(e)
print("格式错误")
elif status == 409:
print("service资源存在")
elif status == 403:
print("没权限")
二、数据表格展示K8s常见资源
大致思路:
- 使用Layui从接口获取JSON数据,动态渲染表格。
- Django准备接口,以JSON格式返回。
- 接口类实例化,遍历获取接口数据,取对应字段值,组成一个字典。
- 资源增删改查采用不同HTTP方法:
HTTP方法 | 数据处理 | 说明 |
---|---|---|
POST | 新增 | 新增一个资源 |
GET | 获取 | 取得一个资源 |
PUT | 更新 | 更新一个资源 |
DELETE | 删除 | 删除一个资源 |
2.1 Namespace
1.查询资源。
##items返回一个对象,类LIST([{命名空间属性},{命名空间属性}] ),每个元素是一个类字典(命名空间属性),操作类字典
for ns in core_api.list_namespace().items:
name = ns.metadata.name ##名称。
labels = ns.metadata.labels ##标签。
create_time = ns.metadata.creation_timestamp ##创建时间。
namespace = {"name": name, "labels": labels, "create_time": create_time}
2.删除资源。
core_api.delete_namespace(name=name)
3.创建资源。
body = client.V1Namespace(
api_version="v1",
kind="Namespace",
metadata=client.V1ObjectMeta(
name=ns_name
)
)
core_api.create_namespace(body=body)
4.使用数据表格显示资源。
table.render({
elem: '#test'
,url:'{% url 'namespace_api' %}'
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'labels', title: '标签',templet: labelsFormat}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150}
]]
,page: true
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if (d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
总结:其他资源功能开发与命名空间一样,拷贝后需要的修改位置:
- 服务端:新添加一个url,函数视图
- 服务端:函数视图GET方法里修改for遍历的K8s API接口、对应字段,DELETE方法里修改删除的K8s API接口
- 前端:面包屑
- 前端:table.renader修改连接的API接口,对应表头,删除接口及提示文字
- 另外,除了Namespace、Node、PV,其他适配加命名空间
2.2 Node
1.查询。
for node in core_api.list_node_with_http_info()[0].items:
name = node.metadata.name ##名称。
labels = node.metadata.labels ##标签。
status = node.status.conditions[-1].status ##准备就绪状态。
scheduler = ("是" if node.spec.unschedulable is None else "否") ##是否可调度。
cpu = node.status.capacity['cpu'] ##cpui资源
memory = node.status.capacity['memory'] ##内存资源
kebelet_version = node.status.node_info.kubelet_version ##kubelet版本
cri_version = node.status.node_info.container_runtime_version ##CRI版本
create_time = node.metadata.creation_timestamp ##创建时间
node = {"name": name, "labels": labels, "status":status,
"scheduler":scheduler , "cpu":cpu, "memory":memory,
"kebelet_version":kebelet_version, "cri_version":cri_version,
"create_time": create_time}
2.数据表格。
table.render({
elem: '#test'
,url:'{% url 'node_api' %}'
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'labels', title: '标签',templet: labelsFormat}
,{field: 'status', title: '准备就绪'}
,{field: 'scheduler', title: '可调度'}
,{field: 'cpu', title: 'CPU'}
,{field: 'memory', title: '内存'}
,{field: 'kebelet_version', title: 'kubelet版本'}
,{field: 'cri_version', title: 'CRI版本'}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150}
]]
,page: true
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if (d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
2.3 PV
- 数据存储,Pod->PVC->PV->外部存储,例如NFS、Ceph
1.查询。
字段:名称、容量、访问模式、回收策略、状态、卷申请(PVC)/命名空间、存储类、创建时间
for pv in core_api.list_persistent_volume().items:
name = pv.metadata.name
capacity = pv.spec.capacity["storage"]
access_modes = pv.spec.access_modes
reclaim_policy = pv.spec.persistent_volume_reclaim_policy
status = pv.status.phase
if pv.spec.claim_ref is not None:
pvc_ns = pv.spec.claim_ref.namespace
pvc_name = pv.spec.claim_ref.name
pvc = "%s / %s" % (pvc_ns, pvc_name)
else:
pvc = "未绑定"
storage_class = pv.spec.storage_class_name
create_time = pv.metadata.creation_timestamp
pv = {"name": name, "capacity": capacity, "access_modes":access_modes,
"reclaim_policy":reclaim_policy , "status":status, "pvc":pvc,
"storage_class":storage_class,"create_time": create_time}
2.创建。
name = request.POST.get("name", None)
capacity = request.POST.get("capacity", None)
access_mode = request.POST.get("access_mode", None)
storage_type = request.POST.get("storage_type", None)
server_ip = request.POST.get("server_ip", None)
mount_path = request.POST.get("mount_path", None)
body = client.V1PersistentVolume(
api_version="v1",
kind="PersistentVolume",
metadata=client.V1ObjectMeta(name=name),
spec=client.V1PersistentVolumeSpec(
capacity={'storage':capacity},
access_modes=[access_mode],
nfs=client.V1NFSVolumeSource(
server=server_ip,
path="/ifs/kubernetes/%s" %mount_path
)
)
)
core_api.create_persistent_volume(body=body)
3.删除。
core_api.delete_persistent_volume(name=name)
4.数据表格。
table.render({
elem: '#test'
,url:'{% url 'pv_api' %}'
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'capacity', title: '容量'}
,{field: 'access_modes', title: '访问模式'}
,{field: 'reclaim_policy', title: '回收策略'}
,{field: 'status', title: '状态'}
,{field: 'pvc', title: 'PVC(命名空间/名称)'}
,{field: 'storage_class', title: '存储类'}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150}
]]
,page: true
,id: 'pvtb'
});
2.4 Deployment
1.查询。
字段:名称、命名空间、预期副本数、可用副本数、Pod标签选择器、镜像/状态、创建时间
for dp in apps_api.list_namespaced_deployment(namespace).items:
name = dp.metadata.name
namespace = dp.metadata.namespace
replicas = dp.spec.replicas
available_replicas = ( 0 if dp.status.available_replicas is None else dp.status.available_replicas)
labels = dp.metadata.labels
selector = dp.spec.selector.match_labels
containers = {}
for c in dp.spec.template.spec.containers:
containers[c.name] = c.image
create_time = dp.metadata.creation_timestamp
dp = {"name": name, "namespace": namespace, "replicas":replicas,
"available_replicas":available_replicas , "labels":labels, "selector":selector,
"containers":containers, "create_time": create_time}
2.创建。
name = request.POST.get("name",None)
namespace = request.POST.get("namespace",None)
image = request.POST.get("image",None)
replicas = int(request.POST.get("replicas",None))
# 处理标签
labels = {}
try:
for l in request.POST.get("labels",None).split(","):
k = l.split("=")[0]
v = l.split("=")[1]
labels[k] = v
except Exception as e:
res = {"code": 1, "msg": "标签格式错误!"}
return JsonResponse(res)
resources = request.POST.get("resources",None)
health_liveness = request.POST.get("health[liveness]",None) # {'health[liveness]': ['on'], 'health[readiness]': ['on']}
health_readiness = request.POST.get("health[readiness]",None)
if resources == "1c2g":
resources = client.V1ResourceRequirements(limits={"cpu":"1","memory":"1Gi"},
requests={"cpu":"0.9","memory":"0.9Gi"})
elif resources == "2c4g":
resources = client.V1ResourceRequirements(limits={"cpu": "2", "memory": "4Gi"},
requests={"cpu": "1.9", "memory": "3.9Gi"})
elif resources == "4c8g":
resources = client.V1ResourceRequirements(limits={"cpu": "4", "memory": "8Gi"},
requests={"cpu": "3.9", "memory": "7.9Gi"})
else:
resources = client.V1ResourceRequirements(limits={"cpu":"500m","memory":"1Gi"},
requests={"cpu":"450m","memory":"900Mi"})
liveness_probe = ""
if health_liveness == "on":
liveness_probe = client.V1Probe(http_get="/",timeout_seconds=30,initial_delay_seconds=30)
readiness_probe = ""
if health_readiness == "on":
readiness_probe = client.V1Probe(http_get="/",timeout_seconds=30,initial_delay_seconds=30)
for dp in apps_api.list_namespaced_deployment(namespace=namespace).items:
if name == dp.metadata.name:
res = {"code": 1, "msg": "Deployment已经存在!"}
return JsonResponse(res)
body = client.V1Deployment(
api_version="apps/v1",
kind="Deployment",
metadata=client.V1ObjectMeta(name=name),
spec=client.V1DeploymentSpec(
replicas=replicas,
selector={'matchLabels': labels},
template=client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels=labels),
spec=client.V1PodSpec(
containers=[client.V1Container( # https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md
name="web",
image=image,
env=[{"name": "TEST", "value": "123"}, {"name": "DEV", "value": "456"}],
ports=[client.V1ContainerPort(container_port=80)],
# liveness_probe=liveness_probe,
# readiness_probe=readiness_probe,
resources=resources,
)]
)
),
)
)
apps_api.create_namespaced_deployment(namespace=namespace, body=body)
3.删除。
apps_api.delete_namespaced_deployment(namespace=namespace, name=name)
4.数据表格。
table.render({
elem: '#test'
,url:'{% url 'deployment_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'namespace', title: '命名空间'}
,{field: 'replicas', title: '预期副本数'}
,{field: 'available_replicas', title: '可用副本数'}
,{field: 'labels', title: '标签',templet: labelsFormat}
,{field: 'selector', title: 'Pod标签选择器',templet: selectorFormat}
,{field: 'containers', title: '容器', templet: containersFormat}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo', width:150}
]]
,page: true
,id: 'dptb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
function selectorFormat(d){
result = "";
for(let key in d.selector) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.selector[key] +
'</span><br>'
}
return result
}
function containersFormat(d) {
result = "";
for(let key in d.containers) {
result += key + '=' + d.containers[key] + '<br>'
}
return result
}
2.5 DaemonSet
1.查询。
字段:名称、命名空间、预期节点数、可用节点数、Pod标签选择器、镜像、创建时间
for ds in apps_api.list_namespaced_daemon_set(namespace).items:
name = ds.metadata.name
namespace = ds.metadata.namespace
desired_number = ds.status.desired_number_scheduled
available_number = ds.status.number_available
labels = ds.metadata.labels
selector = ds.spec.selector.match_labels
containers = {}
for c in ds.spec.template.spec.containers:
containers[c.name] = c.image
create_time = ds.metadata.creation_timestamp
ds = {"name": name, "namespace": namespace, "labels": labels, "desired_number": desired_number,
"available_number": available_number,
"selector": selector, "containers": containers, "create_time": create_time}
2.创建。
name = request.POST.get("name",None)
namespace = request.POST.get("namespace",None)
image = request.POST.get("image",None)
# 处理标签
labels = {}
try:
for l in request.POST.get("labels",None).split(","):
k = l.split("=")[0]
v = l.split("=")[1]
labels[k] = v
except Exception as e:
res = {"code": 1, "msg": "标签格式错误!"}
return JsonResponse(res)
resources = request.POST.get("resources",None)
health_liveness = request.POST.get("health[liveness]",None) # {'health[liveness]': ['on'], 'health[readiness]': ['on']}
health_readiness = request.POST.get("health[readiness]",None)
if resources == "1c2g":
resources = client.V1ResourceRequirements(limits={"cpu":"1","memory":"1Gi"},
requests={"cpu":"0.9","memory":"0.9Gi"})
elif resources == "2c4g":
resources = client.V1ResourceRequirements(limits={"cpu": "2", "memory": "4Gi"},
requests={"cpu": "1.9", "memory": "3.9Gi"})
elif resources == "4c8g":
resources = client.V1ResourceRequirements(limits={"cpu": "4", "memory": "8Gi"},
requests={"cpu": "3.9", "memory": "7.9Gi"})
else:
resources = client.V1ResourceRequirements(limits={"cpu":"500m","memory":"1Gi"},
requests={"cpu":"450m","memory":"900Mi"})
liveness_probe = ""
if health_liveness == "on":
liveness_probe = client.V1Probe(http_get="/",timeout_seconds=30,initial_delay_seconds=30)
readiness_probe = ""
if health_readiness == "on":
readiness_probe = client.V1Probe(http_get="/",timeout_seconds=30,initial_delay_seconds=30)
for dp in apps_api.list_namespaced_daemon_set(namespace=namespace).items:
if name == dp.metadata.name:
res = {"code": 1, "msg": "DaemonSet已经存在!"}
return JsonResponse(res)
body = client.V1DaemonSet(
api_version="apps/v1",
kind="DaemonSet",
metadata=client.V1ObjectMeta(name=name),
spec=client.V1DeploymentSpec(
selector={'matchLabels': labels},
template=client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels=labels),
spec=client.V1PodSpec(
containers=[client.V1Container( # https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Container.md
name="web",
image=image,
env=[{"name": "TEST", "value": "123"}, {"name": "DEV", "value": "456"}],
ports=[client.V1ContainerPort(container_port=80)],
# liveness_probe=liveness_probe,
# readiness_probe=readiness_probe,
resources=resources,
)]
)
),
)
)
3.删除。
apps_api.delete_namespaced_daemon_set(namespace=namespace, name=name)
4.数据表格。
table.render({
elem: '#test'
,url:'{% url 'daemonset_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'namespace', title: '命名空间',sort: true}
,{field: 'desired_number', title: '预期节点数',width: 100}
,{field: 'available_number', title: '可用节点数',width: 100}
,{field: 'labels', title: '标签',templet: labelsFormat}
,{field: 'selector', title: 'Pod 标签选择器',templet: selecotrFormat}
,{field: 'containers', title: '容器', templet: containersFormat}
,{field: 'create_time', title: '创建时间',width: 200}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 150}
]]
,page: true
,id: 'dstb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
function selecotrFormat(d){
result = "";
for(let key in d.selector) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.selector[key] +
'</span><br>'
}
return result
}
function containersFormat(d) {
result = "";
for(let key in d.containers) {
result += key + '=' + d.containers[key] + '<br>'
}
return result
}
2.6 StatefulSet
1.查询。
字段:名称、命名空间、Service名称、预期副本数、可用副本数、Pod标签选择器、镜像、创建时间
for sts in apps_api.list_namespaced_stateful_set(namespace).items:
name = sts.metadata.name
namespace = sts.metadata.namespace
labels = sts.metadata.labels
selector = sts.spec.selector.match_labels
replicas = sts.spec.replicas
ready_replicas = ("0" if sts.status.ready_replicas is None else sts.status.ready_replicas)
#current_replicas = sts.status.current_replicas
service_name = sts.spec.service_name
containers = {}
for c in sts.spec.template.spec.containers:
containers[c.name] = c.image
create_time = sts.metadata.creation_timestamp
ds = {"name": name, "namespace": namespace, "labels": labels, "replicas": replicas,
"ready_replicas": ready_replicas, "service_name": service_name,
"selector": selector, "containers": containers, "create_time": create_time}
2.删除。
apps_api.delete_namespaced_stateful_set(namespace=namespace, name=name)
3.数据表格。
table.render({
elem: '#test'
,url:'{% url 'statefulset_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'namespace', title: '命名空间',sort: true}
,{field: 'service_name', title: 'Service名称'}
,{field: 'replicas', title: '预期副本数',width: 100}
,{field: 'ready_replicas', title: '可用副本数',width: 100}
,{field: 'labels', title: '标签',templet: labelsFormat}
,{field: 'selector', title: 'Pod 标签选择器',templet: selecotrFormat}
,{field: 'containers', title: '容器', templet: containersFormat}
,{field: 'create_time', title: '创建时间',width: 200}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 150}
]]
,page: true
,id: 'ststb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
function selecotrFormat(d){
result = "";
for(let key in d.selector) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.selector[key] +
'</span><br>'
}
return result
}
function containersFormat(d) {
result = "";
for(let key in d.containers) {
result += key + '=' + d.containers[key] + '<br>'
}
return result
}
2.7 Pod
1.查询。
字段:名称、命名空间、IP地址、标签、容器组、状态、创建时间
for po in core_api.list_namespaced_pod(namespace).items:
name = po.metadata.name
namespace = po.metadata.namespace
labels = po.metadata.labels
pod_ip = po.status.pod_ip
containers = [] # [{},{},{}]
status = "None"
# 只为None说明Pod没有创建(不能调度或者正在下载镜像)
if po.status.container_statuses is None:
status = po.status.conditions[-1].reason
else:
for c in po.status.container_statuses:
c_name = c.name
c_image = c.image
# 获取重启次数
restart_count = c.restart_count
# 获取容器状态
c_status = "None"
if c.ready is True:
c_status = "Running"
elif c.ready is False:
if c.state.waiting is not None:
c_status = c.state.waiting.reason
elif c.state.terminated is not None:
c_status = c.state.terminated.reason
elif c.state.last_state.terminated is not None:
c_status = c.last_state.terminated.reason
c = {'c_name': c_name,'c_image':c_image ,'restart_count': restart_count, 'c_status': c_status}
containers.append(c)
create_time = po.metadata.creation_timestamp
po = {"name": name, "namespace": namespace, "pod_ip": pod_ip,
"labels": labels, "containers": containers, "status": status,
"create_time": create_time}
2.删除。
core_api.delete_namespaced_pod(namespace=namespace, name=name)
3.数据表格。
table.render({
elem: '#test'
,url:'{% url 'pod_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'namespace', title: '命名空间',sort: true}
,{field: 'pod_ip', title: 'IP地址'}
,{field: 'labels', title: '标签', templet: labelsFormat}
,{field: 'containers', title: '容器组', templet: containersFormat}
,{field: 'status', title: '状态',sort: true, templet: statusFormat}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 250}
]]
,page: true
,id: 'potb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
function containersFormat(d) {
result = "";
if (d.containers) {
for(let key in d.containers) {
data = d.containers[key];
result += key + ':' + data.c_name + '=' + data.c_image + '<br>' +
'重启次数:' + data.restart_count + '<br>' +
'状态:' + data.c_status + '<br>'
}
return result
} else {
return "None"
}
}
// 如果status为None,使用容器状态显示
function statusFormat(d){
result = "";
if(d.status == "None"){
for(let key in d.containers) {
result += d.containers[key].c_status + '<br>'
}
return result
} else {
return d.status
}
}
2.8 Service
1.查询。
字段:名称、命名空间、类型、集群IP、端口信息、Pod标签选择器、后端Pod、创建时间
for svc in core_api.list_namespaced_service(namespace=namespace).items:
name = svc.metadata.name
namespace = svc.metadata.namespace
labels = svc.metadata.labels
type = svc.spec.type
cluster_ip = svc.spec.cluster_ip
ports = []
for p in svc.spec.ports: # 不是序列,不能直接返回
port_name = p.name
port = p.port
target_port = p.target_port
protocol = p.protocol
node_port = ""
if type == "NodePort":
node_port = " <br> NodePort: %s" % p.node_port
port = {'port_name': port_name, 'port': port, 'protocol': protocol, 'target_port':target_port, 'node_port': node_port}
ports.append(port)
selector = svc.spec.selector
create_time = svc.metadata.creation_timestamp
# 确认是否关联Pod
endpoint = ""
for ep in core_api.list_namespaced_endpoints(namespace=namespace).items:
if ep.metadata.name == name and ep.subsets is None:
endpoint = "未关联"
else:
endpoint = "已关联"
svc = {"name": name, "namespace": namespace, "type": type,
"cluster_ip": cluster_ip, "ports": ports, "labels": labels,
"selector": selector, "endpoint": endpoint, "create_time": create_time}
2.创建。
name = request.POST.get("name",None)
namespace = request.POST.get("namespace",None)
port = int(request.POST.get("port",None))
target_port = int(request.POST.get("target-port",None))
labels = {}
try:
for l in request.POST.get("labels",None).split(","):
k = l.split("=")[0]
v = l.split("=")[1]
labels[k] = v
except Exception as e:
res = {"code": 1, "msg": "标签格式错误!"}
return JsonResponse(res)
type = request.POST.get("type","")
body = client.V1Service(
api_version="v1",
kind="Service",
metadata=client.V1ObjectMeta(
name=name
),
spec=client.V1ServiceSpec(
selector=labels,
ports=[client.V1ServicePort(
port=port,
target_port=target_port,
)],
type=type
)
)
core_api.create_namespaced_service(namespace=namespace, body=body)
3.删除。
core_api.delete_namespaced_service(namespace=namespace, name=name)
4.数据表格。
table.render({
elem: '#test'
,url:'{% url 'service_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true, width: 150}
,{field: 'namespace', title: '命名空间',width: 150, sort: true}
,{field: 'type', title: '类型',width: 120, sort: true}
,{field: 'cluster_ip', title: '集群IP',width: 100}
,{field: 'ports', title: '端口信息',templet: portsFormat}
,{field: 'labels', title: '标签', templet: labelsFormat}
,{field: 'selector', title: 'Pod 标签选择器', templet: selecotrFormat}
,{field: 'endpoint', title: '后端 Pod'}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 150}
]]
,page: true
,id: 'svctb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
function selecotrFormat(d){
result = "";
for(let key in d.selector) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.selector[key] +
'</span><br>'
}
return result
}
function portsFormat(d) {
result = "";
for(let key in d.ports) {
data = d.ports[key];
result += '名称: ' + data.port_name + '<br>' +
'端口: ' + data.port + '<br>' +
'协议: ' + data.protocol + '<br>' +
'容器端口: ' + data.target_port + '<br>'
}
return result
}
2.9 Ingress
1.查询。
字段:名称、命名空间、HTTP、HTTPS、关联Service、创建时间
for ing in networking_api.list_namespaced_ingress(namespace=namespace).items:
name = ing.metadata.name
namespace = ing.metadata.namespace
labels = ing.metadata.labels
service = "None"
http_hosts = "None"
for h in ing.spec.rules:
host = h.host
path = ("/" if h.http.paths[0].path is None else h.http.paths[0].path)
service_name = h.http.paths[0].backend.service_name
service_port = h.http.paths[0].backend.service_port
http_hosts = {'host': host, 'path': path, 'service_name': service_name, 'service_port': service_port}
https_hosts = "None"
if ing.spec.tls is None:
https_hosts = ing.spec.tls
else:
for tls in ing.spec.tls:
host = tls.hosts[0]
secret_name = tls.secret_name
https_hosts = {'host': host, 'secret_name': secret_name}
create_time = ing.metadata.creation_timestamp
ing = {"name": name, "namespace": namespace,"labels": labels ,"http_hosts": http_hosts,
"https_hosts": https_hosts, "service": service, "create_time": create_time
2.创建。
name = request.POST.get("name",None)
namespace = request.POST.get("namespace",None)
host = request.POST.get("host",None)
path = request.POST.get("path","/")
svc_name = request.POST.get("svc_name",None)
svc_port = int(request.POST.get("svc_port",None))
body = client.NetworkingV1beta1Ingress(
api_version="networking.k8s.io/v1beta1",
kind="Ingress",
metadata=client.V1ObjectMeta(name=name, annotations={
"nginx.ingress.kubernetes.io/rewrite-target": "/"
}),
spec=client.NetworkingV1beta1IngressSpec(
rules=[client.NetworkingV1beta1IngressRule(
host=host,
http=client.NetworkingV1beta1HTTPIngressRuleValue(
paths=[client.NetworkingV1beta1HTTPIngressPath(
path=path,
backend=client.NetworkingV1beta1IngressBackend(
service_port=svc_port,
service_name=svc_name)
)]
)
)
]
)
)
networking_api.create_namespaced_ingress(namespace=namespace, body=body)
3.删除。
networking_api.delete_namespaced_ingress(namespace=namespace, name=name)
4.数据表格。
table.render({
elem: '#test'
,url:'{% url 'ingress_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true, width: 300}
,{field: 'namespace', title: '命名空间',width: 200, sort: true}
,{field: 'http_hosts', title: 'HTTP',templet: httpFormat}
,{field: 'https_hosts', title: 'HTTPS',templet: httpsFormat}
,{field: 'service', title: '关联 Service', templet: serviceFormat}
,{field: 'create_time', title: '创建时间',width: 200}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 150}
]]
,page: true
,id: 'ingtb'
});
// 标签格式化,是一个对象
function httpFormat(d){
return "域名: " + d.http_hosts.host + '<br>' + "路径: " + d.http_hosts.path + '<br>'
}
function httpsFormat(d){
if(d.https_hosts != null){
return "域名: " + d.https_hosts.host + '<br>' + "证书Secret名称: " + d.https_hosts.secret_name + '<br>';
} else {
return "None"
}
}
function serviceFormat(d) {
return "名称: " + d.http_hosts.service_name + '<br>' + "端口: " + d.http_hosts.service_port + '<br>';
}
2.10 PVC
1.查询。
字段:名称、命名空间、状态、卷名称、容量、访问模式、存储类、创建时间
for pvc in core_api.list_namespaced_persistent_volume_claim(namespace=namespace).items:
name = pvc.metadata.name
namespace = pvc.metadata.namespace
labels = pvc.metadata.labels
storage_class_name = pvc.spec.storage_class_name
access_modes = pvc.spec.access_modes
capacity = (pvc.status.capacity if pvc.status.capacity is None else pvc.status.capacity["storage"])
volume_name = pvc.spec.volume_name
status = pvc.status.phase
create_time = pvc.metadata.creation_timestamp
pvc = {"name": name, "namespace": namespace, "lables": labels,
"storage_class_name": storage_class_name, "access_modes": access_modes, "capacity": capacity,
"volume_name": volume_name, "status": status, "create_time": create_time}
2.创建。
name = request.POST.get("name", None)
namespace = request.POST.get("namespace", None)
storage_class = request.POST.get("storage_class", None)
access_mode = request.POST.get("access_mode", None)
capacity = request.POST.get("capacity", None)
body = client.V1PersistentVolumeClaim(
api_version="v1",
kind="PersistentVolumeClaim",
metadata=client.V1ObjectMeta(name=name,namespace=namespace),
spec=client.V1PersistentVolumeClaimSpec(
storage_class_name=storage_class, # 使用存储类创建PV,如果不用可去掉
access_modes=[access_mode],
resources=client.V1ResourceRequirements(
requests={"storage" : capacity}
)
)
)
core_api.create_namespaced_persistent_volume_claim(namespace=namespace, body=body)
2.删除。
core_api.delete_namespaced_persistent_volume_claim(namespace=namespace, name=name)
3.数据表格。
layui.use('table', function(){
var table = layui.table;
var $ = layui.jquery;
table.render({
elem: '#test'
,url:'{% url 'pvc_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'namespace', title: '命名空间',sort: true}
,{field: 'labels', title: '标签',templet: labelsFormat}
,{field: 'status', title: '状态',width: 130}
,{field: 'volume_name', title: '卷名称'}
,{field: 'capacity', title: '容量',width: 130}
,{field: 'access_modes', title: '访问模式'}
,{field: 'storage_class_name', title: '存储类'}
,{field: 'create_time', title: '创建时间',width: 200}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 150}
]]
,page: true
,id: 'pvctb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
2.11 ConfigMap
1.查询.
字段:名称、命名空间、数据数量、创建时间
for cm in core_api.list_namespaced_config_map(namespace=namespace).items:
name = cm.metadata.name
namespace = cm.metadata.namespace
data_length = ("0" if cm.data is None else len(cm.data))
create_time = cm.metadata.creation_timestamp
cm = {"name": name, "namespace": namespace, "data_length": data_length, "create_time": create_time}
2.删除。
core_api.delete_namespaced_config_map(name=name,namespace=namespace)
3.数据表格。
table.render({
elem: '#test'
,url:'{% url 'configmap_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'namespace', title: '命名空间',sort: true}
,{field: 'data_length', title: '数据数量'}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 150}
]]
,page: true
,id: 'cmtb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
2.12 Secret
1.查询.
字段:名称、命名空间、数据数量、创建时间
for secret in core_api.list_namespaced_secret(namespace=namespace).items:
name = secret.metadata.name
namespace = secret.metadata.namespace
data_length = ("空" if secret.data is None else len(secret.data))
create_time = secret.metadata.creation_timestamp
se = {"name": name, "namespace": namespace, "data_length": data_length, "create_time": create_time}
2.删除。
core_api.delete_namespaced_secret(namespace=namespace, name=name)
3.数据表格.文章来源:https://www.toymoban.com/news/detail-705548.html
table.render({
elem: '#test'
,url:'{% url 'secret_api' %}?namespace=' + namespace
,toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板
,defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可
title: '提示'
,layEvent: 'LAYTABLE_TIPS'
,icon: 'layui-icon-tips'
}]
,cols: [[
{field: 'name', title: '名称', sort: true}
,{field: 'namespace', title: '命名空间'}
,{field: 'data_length', title: '数据数量'}
,{field: 'create_time', title: '创建时间'}
,{fixed: 'right', title:'操作', toolbar: '#barDemo',width: 150}
]]
,page: true
,id: 'secrettb'
});
// 标签格式化,是一个对象
function labelsFormat(d){
result = "";
if(d.labels == null){
return "None"
} else {
for (let key in d.labels) {
result += '<span style="border: 1px solid #d6e5ec;border-radius: 8px">' +
key + ':' + d.labels[key] +
'</span><br>'
}
return result
}
}
2.13 优化
1.每个k8s资源都有一个时间,默认是UTC,进行格式化中国时区文章来源地址https://www.toymoban.com/news/detail-705548.html
def dt_format(dt):
current_datetime = dt + timedelta(hours=8)
dt = date.strftime(current_datetime, '%Y-%m-%d %H:%M:%S')
return dt
到了这里,关于python开发基础篇1——后端操作K8s API方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!