本文将详细介绍容器弹性伸缩的相关知识,包括K8s有哪些弹性策略?缩容的优先级?什么是HPA?HPA怎样工作?
01、k8s弹性策略有哪些?
在 Kubernetes 集群中,自动化资源管理和伸缩是保持应用高效运行的关键。Kubernetes 提供了几种不同的机制来帮助实现这一目标:水平自动伸缩(HPA, Horizontal Pod Autoscaler)、垂直自动伸缩(VPA, Vertical Pod Autoscaler)和集群自动伸缩(CA, Cluster Autoscaler)。这些伸缩器在功能和使用场景上有所不同:
- HPA:根据 CPU 使用率或其他自定义指标自动增加或减少 Pod 的副本数,如在业务高峰自动增加Pod副本数,在业务低峰自动减少Pod副本数,通常用于无状态应用;
- VPA:自动调整 Pod 的 CPU 和内存请求和限制,它的目标是为每个 Pod 分配最合适的资源量,既避免资源浪费,通常用于有状态应用或单实例应用;
- CA:根据集群的当前负载和资源需求自动增加或减少节点的数量,适用于需要根据应用负载动态调整集群大小的场景,CA 特别适用于云环境
上述所提到的HPA和VPA属于调度层弹性,CA属于资源层弹性,跨层级弹性策略可以结合使用,但不建议HPA和VPA共同使用,避免造成冲突。
02、Pods缩容优先级
在HPA自动扩容的时候,我们似乎不关心Workload中哪个Pod先启动还是后启动,反而我们更关注缩容的时候,哪个Pod被先终止,因为搞不好正在处理业务请求的Pod被它给先终止了,接下来我们就透过源码来解析Kubernetes的缩容规则吧。
kubernetes>pkg>controller>controller_utils.go
代码语言:javascript复制func (s ActivePods) Less(i, j int) bool {
// 1. Unassigned < assigned
// If only one of the pods is unassigned, the unassigned one is smaller
if s[i].Spec.NodeName != s[j].Spec.NodeName && (len(s[i].Spec.NodeName) == 0 || len(s[j].Spec.NodeName) == 0) {
return len(s[i].Spec.NodeName) == 0
}
// 2. PodPending < PodUnknown < PodRunning
if podPhaseToOrdinal[s[i].Status.Phase] != podPhaseToOrdinal[s[j].Status.Phase] {
return podPhaseToOrdinal[s[i].Status.Phase] < podPhaseToOrdinal[s[j].Status.Phase]
}
// 3. Not ready < ready
// If only one of the pods is not ready, the not ready one is smaller
if podutil.IsPodReady(s[i]) != podutil.IsPodReady(s[j]) {
return !podutil.IsPodReady(s[i])
}
// TODO: take availability into account when we push minReadySeconds information from deployment into pods,
// see https://github.com/kubernetes/kubernetes/issues/22065
// 4. Been ready for empty time < less time < more time
// If both pods are ready, the latest ready one is smaller
if podutil.IsPodReady(s[i]) && podutil.IsPodReady(s[j]) {
readyTime1 := podReadyTime(s[i])
readyTime2 := podReadyTime(s[j])
if !readyTime1.Equal(readyTime2) {
return afterOrZero(readyTime1, readyTime2)
}
}
// 5. Pods with containers with higher restart counts < lower restart counts
if maxContainerRestarts(s[i]) != maxContainerRestarts(s[j]) {
return maxContainerRestarts(s[i]) > maxContainerRestarts(s[j])
}
// 6. Empty creation time pods < newer pods < older pods
if !s[i].CreationTimestamp.Equal(&s[j].CreationTimestamp) {
return afterOrZero(&s[i].CreationTimestamp, &s[j].CreationTimestamp)
}
return false
}
总结一下,缩容的顺序如下:
- 未调度>已调度:未调度到节点的Pods被终止的优先级更高,因为它们还没有开始运行任何任务,所以优先缩减它们理所应当。
- PodPending>PodUnknown>PodRunning:PodPending表示Pod还未开始运行,PodUnknown表示Pod状态未知PodRunning表示Pod正在运行。优先缩减还未运行或者状态未知的Pods,以尽可能保持系统的稳定运行。
- Not ready>ready:Not ready的Pods被终止的优先级更高,因为它们可能存在问题,优先缩减它们可以减少系统的错误率。
- 较晚Ready>较早Ready:较晚Ready的Pods被终止的优先级更高,因为它们没有接收业务请求的可能性较大,优先缩减它们可以减少对业务的影响。
- 容器重启次数较多>较少:重启次数多的Pods被终止的优先级更高,因为它们存在问题的可能性较大,所以优先缩减它们。
- 创建时间较短>较长:创建时间短的Pods优先级更高,因为它们没有接收业务请求的可能性较大,优先缩减它们可以减少对业务的影响。
03、HPA实践
我们先创建一个测试用的负载(Workload),命名为nginx
nginx.yaml
代码语言:javascript复制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
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
然后我们将其部署到k8s上,运行结果如下
然后我们再创建一个HPA,通过scaleTargetRef设置当前HPA绑定的对象。本例中绑定名为nginx的Deployment,在确保所有Pod中容器的平均CPU使用率或平均内存使用率达到50%时触发扩缩操作(二者满足其一便会触发)。
hpa.yaml
代码语言:javascript复制apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
namespace: hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 1 # Deployment可缩容的容器数量下限,需设置为大于等于1的整数
maxReplicas: 10 # 该Deployment可扩容的容器数量上限,需大于minReplicas
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # 目标资源的平均使用率,即资源使用量的平均值与其请求量之间的比例
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 50 # 目标资源的平均使用率,即资源使用量的平均值与其请求量之间的比例
我们通过kubectl apply -f hpa.yaml执行HPA创建,并通过kubectl describe看到HPA已经创建完成
在创建完HPA后,我们可以看到nginx的副本数变成了1,是因为我们HPA设置的minReplicas为1,在负载(Workload)平均资源使用率低于50%时,会触发缩容。
好了,接下来,我们将通过压测验证下HPA扩容,在这过程中,我们可以用kubectl get hpa -w -n hpa观察容器的资源使用率和nginx 副本数量的变化,如下图,我们可以看到随着压测,内存的使用率逐渐上涨,在使用率超过50%后REPLICAS副本数会自动增加,至此HPA的伸缩容均已验证完成。
04、HPA相关操作
从v1.18开始,K8s v2beta2 API允许通过HPA的behavior字段配置扩缩行为
稳定窗口,防止扩缩容指标频繁波动 ,如下示例设置为300s作为观察期
代码语言:javascript复制behavior:
scaleDown:
stabilizationWindowSeconds: 300
scaleUp:
stabilizationWindowSeconds: 300
禁止扩容
代码语言:javascript复制behavior:
scaleUp:
selectPolicy: Disabled
禁止缩容
代码语言:javascript复制behavior:
scaleDown:
selectPolicy: Disabled
05、总结
综上所述,我们认识了什么是HPA,HPA如何工作,但是在实际的业务场景中,Pods缩容期间是否会对业务带来影响呢?该如何解决?欢迎留言,本期就介绍到这里,谢谢!
欢迎订阅我的公众号「SRE运维手记」