01、背 景
有一个渲染应用场景,单一个工作负载(Deployment)就有数百个副本,为了降低运维成本,选择了某云商的弹性容器实例产品作为载体,其按pod数量以小时计费,相较于准备大量的Node的方式要划算得多。
CPU渲染应用有个特点,在执行任务的时候,CPU会将近满负载运作,有些超清的图片渲染任务甚至需要1-2个小时才能完成渲染,在闲置的时候负载又趋近于0,是一个典型的潮汐应用场景。
那么,如果能够精准回收闲置的Pods,将能够实现极致的运维降本,但同时又是一个难题,我们知道单纯用HPA是无法实现的,所以为了实现该目标,我们研究出了如下方案实现高效的潮汐调度。 02、方 案
据了解,在Kubernetes 1.22版本开始,默认开启了pod-deletion-cost特性,允许用户设置Pod的删除成本,它是一个整数值,可以为正数、零或负数,分值越低在缩容时的优先级越高。
我们在网上翻过很多资料,大多都是通过手动修改,亦或编写脚本定时进行批量修改,都不是很理想。
最后,我们发现了Openkruise这个组件,可以通过自定义探针PodProbeMarker自动给Pods注入pod-deletion-cost的分值,将CPU使用率较低的删除成本设置为5,将CPU使用率较高的设置为10,下面将介绍具体的实现过程,为了方便截图和演示,我们在实验环境进行。
03、实践分享
实验环境
Kubernetes 1.24
Openkruise 1.6
安装
PodProbeMarker能力默认是关闭的, 你可以通过 feature-gate PodProbeMarkerGate
打开,如下:
helm install kruise openkruise/kruise --version 1.6.3 --set featureGates="PodProbeMarkerGate=true"
实践步骤
nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: hpa
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
nginx-idle: 'xxx' # 默认值设置为xxx,如果是闲置的时候会显示为ture
annotations:
controller.kubernetes.io/pod-deletion-cost: '100' # 初始删除成本我们设置为100
spec:
containers:
- name: nginx
image: nginx:1.16.1
ports:
- containerPort: 80
resources:
requests: # 必须设置,否则HPA无法运行
cpu: 100m # 设置容器运行时所需CPU资源
memory: 200Mi # 设置容器运行时所需内存资源
limits: # 不是必须设置,但是设置后可以避免压测时占用过多资源
cpu: 200m # 限制容器CPU资源为200m,m 是毫核(milli-cores)的缩写,因此200m表示0.2核
memory: 400Mi # 限制容器内存资源为400Mi
---
kind: Service
apiVersion: v1
metadata:
name: nginx
namespace: hpa
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
然后执行kubectl apply -f nginx.yaml完成部署
hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
namespace: hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 2 # Deployment可缩容的容器数量下限,需设置为大于等于1的整数
maxReplicas: 10 # 该Deployment可扩容的容器数量上限,需大于minReplicas
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # 目标资源的平均使用率,即资源使用量的平均值与其请求量之间的比例
behavior:
scaleDown:
stabilizationWindowSeconds: 300
scaleUp:
stabilizationWindowSeconds: 300
执行kubectl apply -f hpa.yaml完成部署,创建完成后我们可以通过kubectl get hpa -n hpa查看HPA的状态
idle.sh
#!/bin/bash
# 读取 /data/task.txt 文件的内容
task_status=$(cat /data/task.txt)
# 判断 task_status 的值
if [ "$task_status" -eq 0 ]; then
# 如果值为 0,表示空闲
exit 0
elif [ "$task_status" -eq 1 ]; then
# 如果值为 1,表示繁忙
exit 1
fi
probe.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: PodProbeMarker
metadata:
name: nginx-probe
namespace: hpa
spec:
selector:
matchLabels:
app: nginx
probes:
- name: Idle
containerName: nginx
probe:
exec:
command:
- /home/nginx/idle.sh
initialDelaySeconds: 10
timeoutSeconds: 3
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
markerPolicy:
- state: Succeeded
labels:
nginx-idle: 'true'
annotations:
controller.kubernetes.io/pod-deletion-cost: '5'
- state: Failed
labels:
nginx-idle: 'false'
annotations:
controller.kubernetes.io/pod-deletion-cost: '10'
podConditionType: nginx.io/idle
执行kubectl apply -f probe.yaml完成部署,在完成部署后,我们可以通过如下指令查看当前Pods的删除成本,在繁忙状态下,所有的容器删除成本均从100自动修改为10,自动注入成功,有点小激动。
kubectl get pods -l app=nginx -n hpa -o json | jq '.items[] | {POD_NAME: .metadata.name, POD_DELETION_COST: .metadata.annotations["controller.kubernetes.io/pod-deletion-cost"]}'
kubectl scale deployment nginx -n hpa --replicas=1
04、总结
综上所述,我们借助Openkruise根据Pod的负载情况自动注入pod-deletion-cost分值,从而实现渲染应用的潮汐调度,高效且精准地回收闲置Pods资源,极致节省运维成本,本期分享就到这里,谢谢!
欢迎订阅我的公众号「SRE运维手记」
参考链接
https://openkruise.io/docs/user-manuals/podprobemarker/