k8s系列-动态扩缩容部署
背景
1.本系列k8s文章全部为内网离线部署。
2.全部为root用户执行。
3.k8s版本为1.22.2
本案例中使用的image镜像,如需在生产环境内网中使用,应该全部使用内网Docker仓库中的镜像。
动态扩容概述
Horizontal Pod Autoscaler
(HPA
,Pod水平自动伸缩 ),根据平均 CPU 利用率 、平均内存利用率 或你指定的任何其他自定义指标 自动调整Deployment
、ReplicaSet
或StatefulSet
或其他类似资源,实现部署的自动扩展和缩减,让部署的规模接近于实际服务的负载。HPA不适用于无法缩放的对象 ,例如DaemonSet
。
官方文档:kubernetes.io/zh-cn/docs/...
实际生产中,一般使用这四类指标:
Resource metrics
------CPU核 和 内存利用率指标。Pod metrics
------例如网络利用率和流量。Object metrics
------特定对象的指标,比如Ingress, 可以按每秒使用请求数来扩展容器。Custom metrics
------自定义监控,比如通过定义服务响应时间,当响应时间达到一定指标时自动扩容。
metrics-server原理
HPA 前提条件
默认情况下,Horizontal Pod Autoscaler 控制器会从一系列的 API 中检索度量值。 集群管理员需要确保下述条件,以保证 HPA 控制器能够访问这些 API:
- 对于资源指标 ,将使用 metrics.k8s.io API,一般由
metrics-server
提供。 它可以作为集群插件启动。 - 对于自定义指标,将使用 custom.metrics.k8s.io API。 它由其他度量指标方案厂商的"适配器(Adapter)" API 服务器提供。 检查你的指标管道以查看是否有可用的 Kubernetes 指标适配器。
- 对于外部指标,将使用 external.metrics.k8s.io API。可能由上面的自定义指标适配器提供。
Kubernetes Metrics Server:
- Kubernetes Metrics Server 是 Cluster 的核心监控数据的聚合器,kubeadm 默认是不部署的。
- Metrics Server 供 Dashboard 等其他组件使用,是一个扩展的 APIServer,依赖于 API Aggregator。所以,在安装 Metrics Server 之前需要先在 kube-apiserver 中开启 API Aggregator。
- Metrics API 只可以查询当前的度量数据,并不保存历史数据。
- Metrics API URI 为 /apis/metrics.k8s.io/,在 k8s.io/metrics 下维护。
- 必须部署 metrics-server 才能使用该 API,metrics-server 通过调用 kubelet Summary API 获取数据。
架构图
- 自动检测周期由 kube-controller-manager 的
--horizontal-pod-autoscaler-sync-period
参数设置(默认间隔为15 秒
)。 - metrics-server 提供 metrics.k8s.io API 为pod资源的使用提供支持。
- 15s/周期 -> 查询metrics.k8s.io API -> 算法计算 -> 调用scale 调度 -> 特定的扩缩容策略执行。
HPA扩缩容算法
从最基本的角度来看,Pod 水平自动扩缩控制器根据当前指标和期望指标来计算扩缩比例。
c
期望副本数 = ceil[当前副本数 * (当前指标 / 期望指标)]
1、扩容
- 如果计算出的扩缩比例接近 1.0, 将会放弃本次扩缩, 度量指标 / 期望指标接近1.0。
2、缩容
- 冷却/延迟: 如果延迟(冷却)时间设置的太短,那么副本数量有可能跟以前一样出现抖动。 默认值是 5 分钟(5m0s)--horizontal-pod-autoscaler-downscale-stabilization
3、特殊处理
- 丢失度量值:缩小时假设这些 Pod 消耗了目标值的 100%, 在需要放大时假设这些 Pod 消耗了 0% 目标值。 这可以在一定程度上抑制扩缩的幅度。
- 存在未就绪的pod的时候:我们保守地假设尚未就绪的 Pod 消耗了期望指标的 0%,从而进一步降低了扩缩的幅度。
- 未就绪的 Pod 和缺少指标的 Pod 考虑进来再次计算使用率。 如果新的比率与扩缩方向相反,或者在容忍范围内,则跳过扩缩。 否则,我们使用新的扩缩比例。
- 指定了多个指标, 那么会按照每个指标分别计算扩缩副本数,取最大值进行扩缩。
HPA 对象定义
yaml
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: nginx
spec:
behavior:
scaleDown:
policies:
- type: Pods
value: 4
periodSeconds: 60
- type: Percent
value: 10
periodSeconds: 60
stabilizationWindowSeconds: 300
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
HPA对象默认行为
yaml
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 100
periodSeconds: 15
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
准备
下载导入镜像镜像
需要的镜像bitnami/metrics-server:0.6.1
、nginx:1.7.9
(测试扩缩容使用)。 参照上一篇文章操作
K8S开启 API Aggregator
cmd
vim /etc/kubernetes/manifests/kube-apiserver.yaml
# 添加这行 (在spec.containers.command下级,可以看到很多`- --`)
# - --enable-aggregator-routing=true
修改每个 API Server 的 kube-apiserver.yaml 配置开启 Aggregator Routing。 修改 manifests 配置后 API Server 会自动重启生效。
准备部署yaml(复制到k8s-master机器中)
components.yaml
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
rbac.authorization.k8s.io/aggregate-to-admin: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: system:aggregated-metrics-reader
rules:
- apiGroups:
- metrics.k8s.io
resources:
- pods
- nodes
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
rules:
- apiGroups:
- ""
resources:
- nodes/metrics
verbs:
- get
- apiGroups:
- ""
resources:
- pods
- nodes
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server-auth-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server:system:auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:metrics-server
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: https
selector:
k8s-app: metrics-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: metrics-server
strategy:
rollingUpdate:
maxUnavailable: 0
template:
metadata:
labels:
k8s-app: metrics-server
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls
image: bitnami/metrics-server:0.6.1
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /livez
port: https
scheme: HTTPS
periodSeconds: 10
name: metrics-server
ports:
- containerPort: 4443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /readyz
port: https
scheme: HTTPS
initialDelaySeconds: 20
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 200Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /tmp
name: tmp-dir
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
serviceAccountName: metrics-server
volumes:
- emptyDir: {}
name: tmp-dir
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
labels:
k8s-app: metrics-server
name: v1beta1.metrics.k8s.io
spec:
group: metrics.k8s.io
groupPriorityMinimum: 100
insecureSkipTLSVerify: true
service:
name: metrics-server
namespace: kube-system
version: v1beta1
versionPriority: 100
hpa-nginx.yaml(测试用)
yaml
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: hpa-nginx
spec:
maxReplicas: 10 # 最大扩容到10个节点(pod)
minReplicas: 1 # 最小扩容1个节点(pod)
metrics:
- resource:
name: cpu
target:
averageUtilization: 40 # CPU 平局资源使用率达到40%就开始扩容,低于40%就是缩容
# 设置内存
# AverageValue:40
type: Utilization
type: Resource
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hpa-nginx
---
apiVersion: v1
kind: Service
metadata:
name: hpa-nginx
spec:
type: NodePort
ports:
- name: "http"
port: 80
targetPort: 80
nodePort: 30080
selector:
service: hpa-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-nginx
spec:
replicas: 1
selector:
matchLabels:
service: hpa-nginx
template:
metadata:
labels:
service: hpa-nginx
spec:
containers:
- name: hpa-nginx
image: nginx:1.7.9
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 200m
memory: 200Mi
部署metrics-server
cmd
# 部署
kubectl apply -f components.yaml
# 查看部署情况
kubectl get pod -n kube-system | grep metrics-server
# 查看node和pod资源使用情况
kubectl top nodes
kubectl top pods
如果报错
metrics-server pod无法启动,出现日志unable to fully collect metrics: ... x509: cannot validate certificate for because ... it doesn't contain any IP SANs ...
解决方法:在metrics-server中添加--kubelet-insecure-tls
参数跳过证书校验,上面的实例yaml中已经添加了。
部署Nginx测试案例
hpa-nginx.yaml
yaml
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: hpa-nginx
spec:
maxReplicas: 10 # 最大扩容到10个节点(pod)
minReplicas: 1 # 最小扩容1个节点(pod)
metrics:
- resource:
name: cpu
target:
averageUtilization: 40 # CPU 平局资源使用率达到40%就开始扩容,低于40%就是缩容
# 设置内存
# AverageValue:40
type: Utilization
type: Resource
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hpa-nginx
---
apiVersion: v1
kind: Service
metadata:
name: hpa-nginx
spec:
type: NodePort
ports:
- name: "http"
port: 80
targetPort: 80
nodePort: 30080
selector:
service: hpa-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-nginx
spec:
replicas: 1
selector:
matchLabels:
service: hpa-nginx
template:
metadata:
labels:
service: hpa-nginx
spec:
containers:
- name: hpa-nginx
image: nginx:1.7.9
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 200m
memory: 200Mi
主要参数解释如下:
scaleTargetRef
:目标作用对象,可以是Deployment、ReplicationController或ReplicaSet。minReplicas
和maxReplicas
:Pod副本数量的最小值和最大值,系统将在这个范围内进行自动扩缩容操作,并维持每个Pod的内存使用率为40%
,这个值就是上面设置的阈值averageUtilization
。- metrics:目标指标值。在metrics中通过参数type定义指标的类型;通过参数target定义相应的指标目标值,系统将在指标数据达到目标值时(考虑容忍度的区间,见前面算法部分的说明)触发扩缩容操作。
- 对于CPU使用率,在target参数中设置
averageUtilization
定义目标平均CPU使用率。 - 对于内存资源,在target参数中设置
AverageValue
定义目标平均内存使用值。
部署
cmd
kubectl apply -f hpa-nginx.yaml
压测
安装httpd
官网下载,源码方式安装。
cmd
cd httpd-2.4.57
# prefix为安装路径
./configure --prefix=/usr/local/apache
make && make install
开始压测
cmd
# master01为集群负载IP或单节点Master的IP。
ab -n 100000 -c 800 http://master01:30080/
#-c:并发数
#-n:总请求数
观察容器扩容情况
cmd
kubectl get pods -w | grep hap
# 可以持续发起压测请求,观察容器扩容情况,容器会逐渐从1个节点扩容到10个节点。停止压测后,大概过5分钟开始缩容。