控制器
ReplicaSet、Deployment、StatefulSet 和 DaemonSet 都是 Kubernetes 中的控制器对象,用于管理 Pod 的创建、扩展、缩减和更新等操作。
一、Deployment
适用无状态服务应用部署;Deployment 是在 ReplicaSet 的基础上提供了更高级功能的控制器。它提供了滚动更新和回滚功能
1、配置文件
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息labels: # 标签app: nginx-deploy # 具体的 key: value 配置形式name: nginx-deploy # deployment 的名字namespace: default # 所在的命名空间
spec:replicas: 3 # 期望副本数,最终运行的副本数revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数selector: # 选择器,用于找到匹配的 ReplicaSet(RS)matchLabels: # 按照标签匹配app: nginx-deploy # 匹配的标签key/valuestrategy: # 更新策略rollingUpdate: # 滚动更新配置maxSurge: 25% # 进行滚动更新时,允许超过期望副本数的额外副本个数或比例,maxUnavailable: 25% # 进行滚动更新时,不可用的最大副本个数或比例type: RollingUpdate # 更新类型,采用滚动更新template: # pod 模板metadata: # pod 的元信息labels: # pod 的标签app: nginx-deployspec: # pod 期望信息containers: # pod 的容器- image: nginx:1.7.9 # 镜像imagePullPolicy: IfNotPresent # 拉取策略name: nginx # 容器名称restartPolicy: Always # 重启策略terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
关于期望副本数
replicas
、maxSurge和
maxUnavailable:
replicas
:期望副本数;maxSurge
:允许超过期望副本数的额外副本数,绝对数值或百分比。例如,如果maxSurge
设置为 1,那么在滚动更新期间,可以将副本数量增加到期望副本数加上1;maxUnavailable
:不可用的最大副本数。绝对数值或百分比。例如,如果maxUnavailable
设置为 1,那么在滚动更新期间,最多允许有一个副本不可用;举例:
spec:replicas: 3 # 期望副本数...strategy:rollingUpdate: maxSurge: 1 # 进行滚动更新时,允许超过期望副本数的额外副本个数或比例maxUnavailable:1 # 进行滚动更新时,不可用的最大副本个数或比例type: RollingUpdate # 更新类型,采用滚动更新
在此情况下,Kubernetes会根据
maxSurge
的值先增加1个额外的副本,使得总共有4个副本。然后再逐个替换旧的副本,直到所有旧副本被替换为止。这样可以保证在更新过程中至少有3个可用的副本。然而,由于Deployment的
replicas
值是3,Kubernetes会自动将副本数量调整回到3个。它会逐个替换旧的副本,直到满足replicas
定义的副本数为止。这样确保了最终运行的副本数与replicas
的值保持一致。
2、创建/查看
# 创建一个 deployment(nginx-deploy)
[root@k8s-master ~]# kubectl create deploy nginx-deploy --image=nginx:1.7.9
deployment.apps/nginx-deploy created
# 或执行 "kubectl create -f xxx.yaml --record"
# --record 会在 annotation 中记录当前命令创建或升级了资源,后续可以查看做过哪些变动操作# 查看部署信息
[root@k8s-master ~]# kubectl get deployments --show-labels
NAME READY UP-TO-DATE AVAILABLE AGE LABELS
nginx-deploy 1/1 1 1 7h22m app=nginx-deploy# 查看 rs
[root@k8s-master ~]# kubectl get rs --show-labels
NAME DESIRED CURRENT READY AGE LABELS
nginx-deploy-78d8bf4fd7 1 1 1 4h2m app=nginx-deploy,pod-template-hash=78d8bf4fd7# 查看 pod 以及展示标签,可以看到是关联的哪个 rs
[root@k8s-master ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-deploy-78d8bf4fd7-d9sb4 1/1 Running 0 4h3m app=nginx-deploy,pod-template-hash=78d8bf4fd7
通过
NMAE
标签,可以看出:deployment > ReplicaSet(RS)> pod
3、滚动更新
...
spec:...strategy: # 更新策略rollingUpdate: # 滚动更新配置maxSurge: 25% # 进行滚动更新时,允许超过期望副本数的额外副本个数或比例,maxUnavailable: 25% # 进行滚动更新时,不可用的最大副本个数或比例type: RollingUpdate # 更新类型,采用滚动更新
只有修改了 deployment
配置文件中的 template
中的属性后,才会触发更新操作。
# 修改 nginx 版本号
[root@k8s-master ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1# 或者 通过 kubectl edit 进行修改
[root@k8s-master ~]# kubectl edit deployment/nginx-deployment # 检查Deployment的滚动更新状态
[root@k8s-master ~]# kubectl rollout status deploy <deployment_name># 查看部署描述,最后展示发生的事件列表也可以看到滚动更新过程
[root@k8s-master ~]# kubectl describe deploy <deployment_name># 获取部署信息,UP-TO-DATE 表示已经有多少副本达到了配置中要求的数目
[root@k8s-master ~]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 1/1 1 1 7h5m# 可以看到增加了一个新的 rs
[root@k8s-master ~]# kubectl get rs --show-labels
NAME DESIRED CURRENT READY AGE
nginx-deploy-754898b577 1 1 1 31m app=nginx-deploy,pod-template-hash=754898b577
nginx-deploy-78d8bf4fd7 0 0 0 7h21m app=nginx-deploy,pod-template-hash=78d8bf4fd7# 可以看到所有 pod 关联的 rs 变成了新的
[root@k8s-master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx-deploy-754898b577-gfpjh 1/1 Running 0 19m app=nginx-deploy,pod-template-hash=754898b577
多个滚动更新并行:
假设当前有 5 个 nginx:1.7.9 版本,要将版本更新为 1.9.1,当更新成功第三个以后,马上又将期望更新的版本改为 1.9.2,那么此时会立马删除之前的三个,并且立马开启更新 1.9.2 的任务。
4、回滚
默认情况下,kubernetes会在系统中保存前两次的Deployment的rollout历史记录,以便随时回退(可以修改revision history limit来更改保存的revision数)。
可以通过设置 .spec.revisonHistoryLimit 的值来指定 deployment 保留多少 revison,如果设置为 0,则不允许 deployment 回退了。
# 获取 revison 的历史列表
kubectl rollout history deployment/nginx-deploy# 可以查看对应版本详细信息
kubectl rollout history deployment/nginx-deploy --revision=2 # 回退到上一个版本
kubectl rollout undo deployment/nginx-deploy # 回退到指定的 revision 版本
kubectl rollout undo deployment/nginx-deploy --to-revision=2# 查看
kubectl get deployment
kubectl describe deployment
5、扩容/缩容
1、通过 kubectl scale
命令的replicas
参数可以进行自动扩容/缩容
2、通过 kube edit
编辑 replcas
的值也可以实现扩容/缩容
[root@k8s-master ~]# kubectl scale --replicas=6 deploy [deployName][root@k8s-master ~]# kubectl edit deploy [deployName]
扩容/缩容只是直接创建/删除副本数,没有更新 pod 的 template 因此不会创建新的 rs。
6、暂停/恢复滚动更新
由于每次对 pod template 中的信息发生修改后,都会触发更新 deployment 操作 。
如果频繁修改信息,就会产生多次更新,同时会产生多个 rs 和多个历史版本(rollout history
)而实际上只需要执行最后一次更新即可,当出现此类情况时就可以暂停 deployment 的滚动更新。
# 暂停滚动更新
kubectl rollout pause deploy <name># 恢复滚动更新
kubectl rollout resume deploy <name>
二、StatefulSet
专门针对有状态服务进行部署的一个控制器;它与 ReplicaSet 类似,但是在创建和管理 Pod 副本时保持每个 Pod 的标识和稳定的网络身份。StatefulSet 适用于需要持久化存储和稳定网络标识的应用,如数据库或有序队列。
1、配置文件
# “---” 表示yaml文件中嵌套了其他的yaml配置文件
---
apiVersion: v1
kind: Service
metadata:name: nginx # Service 对象的名字labels:app: nginx
spec:ports:- port: 80name: webclusterIP: Noneselector:app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: web # StatefulSet 对象的名字
spec:serviceName: "nginx" # 使用哪个 Service 来管理 dnsreplicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.7.9ports: # 容器内部要暴露的端口- containerPort: 80 # 暴露的端口号name: web # 该端口配置的名字volumeMounts: # 加载数据卷- name: www # 指定加载哪个数据卷mountPath: /usr/share/nginx/html # 加载到容器中的哪个目录volumeClaimTemplates: # 数据卷模板- metadata: # 数据卷描述name: www # 数据卷的名称annotations: # 数据卷注释volume.alpha.kubernetes.io/storage-class: anythingspec: # 数据卷的规约accessModes: [ "ReadWriteOnce" ] # 访问模式resources:requests:storage: 1Gi # 需要 1G 的存储资源
2、创建/查看
[root@k8s-master ~]# kubectl create -f xx.yaml# 查看 service 和 statefulset 缩写:sts
[root@k8s-master ~]# kubectl get service nginx
[root@k8s-master ~]# kubectl get statefulset web# 查看 PVC 信息
[root@k8s-master ~]# kubectl get pvc# 查看创建的 pod,这些 pod 是有序的
[root@k8s-master ~]# kubectl get pods -l app=nginx# 查看这些 pod 的 dns
# 运行一个 pod,基础镜像为 busybox 工具包,利用里面的 nslookup 可以看到 dns 信息
[root@k8s-master ~]# kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
# 容器中执行:nslookup web-0.nginx
3、扩容/缩容
两种方式:
# 第一种:scale
[root@k8s-master ~]# kubectl scale statefulset web --replicas=5# 第二种:通过 patch 更新配置文件
[root@k8s-master ~]# kubectl patch statefulset web -p '{"spec":{"replicas":3}}'
4、更新策略
对于 StatefulSet 控制器,默认情况下使用的是 RollingUpdate
策略,即滚动更新策略,一次只更新一个Pod,确保在更新过程中应用保持可用性。
4.1、RollingUpdate
StatefulSet 的滚动更新策略,同样是修改 pod template 属性后触发更新,但是由于 pod 是有序的,在 StatefulSet 中更新时是基于 pod 的顺序倒序更新的。
...
spec:updateStrategy:rollingUpdate:partition: [n] # 只会更新那些序号(web-n) >= n 的 podtype: RollingUpdate
partition 默认为 0 ,即全部更新;如果 partition 设置为 3,那么此时滚动更新时,只会更新那些 podName 序号 >= 3 的 pod。
利用该机制,可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再逐渐增大更新的 pod 数量,最终实现全部 pod 更新。
# 镜像更新
[root@k8s-master ~]# kubectl patch sts web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.9.1"}]'# 或通过 edit 更新
[root@k8s-master ~]# kubectl edit sts web[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 16m
web-1 1/1 Running 0 17m# 获取 revison 的历史列表
[root@k8s-master ~]# kubectl rollout history sts web# 可以查看对应版本详细信息:可以观察到有序的更新
[root@k8s-master ~]# kubectl rollout history sts web --revision=2
4.2、OnDelete
只有在 pod 被删除时会进行更新操作。
...
spec:updateStrategy:type: OnDelete
5、删除
# 【级联删除】删除statefulset时会同时删除pods
kubectl delete statefulset web# 【非级联删除】删除 sts 时不会删除 pods
# 注意:删除sts后,pods就没人管了,之后再删除pod不会重建的
kubectl deelte sts web --cascade=false# 之后删除service
kubectl delete service nginx# sts删除后,PVC还会保留着,数据不再使用的话也需要删除
kubectl delete pvc www-web-0 www-web-1
三、DaemonSet
DaemonSet 保证在每个 Node 上都运行一个容器副本,常用来部署一些集群的日志、监控或者其他系统管理应用。典型的应用包括:
- 日志收集,比如 fluentd,logstash 等
- 系统监控,比如 Prometheus Node Exporter,collectd,New Relic agent,Ganglia gmond 等
- 系统程序,比如 kube-proxy, kube-dns, glusterd, ceph 等
1、配置文件
apiVersion: apps/v1
kind: DaemonSet
metadata:name: fluentd
spec:selector:matchLabels:app: loggingtemplate:metadata:labels:app: loggingid: fluentdname: fluentdspec:nodeSelector: svc_type: microsvccontainers:- name: fluentd-esimage: agilestacks/fluentd-elasticsearch:v1.3.0env:- name: FLUENTD_ARGSvalue: -qqvolumeMounts: # 加载数据卷,避免数据丢失- name: containers # 数据卷名字mountPath: /var/lib/docker/containers # 将数据卷挂载到容器内的哪个目录- name: varlogmountPath: /varlogvolumes: # 定义数据卷- hostPath: # 数据卷类型;主机路径的模式,也就是与node的共享目录path: /var/lib/docker/containers # node中的共享目录name: containers- hostPath:path: /var/logname: varlog
2、指定 Node 节点
没有指定 node 节点时,系统会为每个非 master 节点部署。
DaemonSet 会忽略 Node 的 unschedulable 状态,有两种方式来指定 Pod 只运行在指定的 Node 节点上:
- nodeSelector:只调度到匹配指定 label 的 Node 上
- nodeAffinity:功能更丰富的 Node 选择器,比如支持集合操作
- podAffinity:调度到满足条件的 Pod 所在的 Node 上
2.1、nodeSelector
只调度到匹配指定标签的 Node 上;
# 给 node1 打上标签
kubectl label nodes k8s-node1 svc_type=microsvc# 查看节点标签
kubectl get no --show-labels# 然后再 daemonset 配置中设置 nodeSelector
spec:template:spec:nodeSelector:svc_type: microsvc
2.2、nodeAffinity
nodeAffinity 目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。
比如下面的例子代表调度到包含标签 wolfcode.cn/framework-name 并且值为 spring 或 springboot 的 Node 上,并且优选还带有标签 another-node-label-key=another-node-label-value 的Node。
apiVersion: v1
kind: Pod
metadata:name: with-node-affinity
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: wolfcode.cn/framework-nameoperator: Invalues:- spring- springbootpreferredDuringSchedulingIgnoredDuringExecution:- weight: 1preference:matchExpressions:- key: another-node-label-keyoperator: Invalues:- another-node-label-valuecontainers:- name: with-node-affinityimage: pauseyyf/pause
2.3、podAffinity
podAffinity 基于 Pod 的标签来选择 Node,仅调度到满足条件Pod 所在的 Node 上,支持 podAffinity 和 podAntiAffinity。这个功能比较绕,以下面的例子为例:
- 如果一个 “Node 所在空间中包含至少一个带有 auth=oauth2 标签且运行中的 Pod”,那么可以调度到该 Node
- 不调度到 “包含至少一个带有 auth=jwt 标签且运行中 Pod”的 Node 上
apiVersion: v1
kind: Pod
metadata:name: with-pod-affinity
spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: authoperator: Invalues:- oauth2topologyKey: failure-domain.beta.kubernetes.io/zonepodAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: authoperator: Invalues:- jwttopologyKey: kubernetes.io/hostnamecontainers:- name: with-pod-affinityimage: pauseyyf/pause
3、更新
默认使用 RollingUpdate,不建议使用 RollingUpdate,建议使用 OnDelete 模式,这样避免频繁更新 ds;
四、HPA 自动扩/缩容
通过监控 pod 的 cpu、内存使用率或自定义指标进行自动的扩容或缩容 pod 的数量。
通常用于 Deployment;不适用于无法扩/缩容的对象,如 DaemonSet。
- 控制管理器每隔30s(可以通过–horizontal-pod-autoscaler-sync-period修改)查询metrics的资源使用情况
- 支持三种metrics类型
- 预定义metrics(比如Pod的CPU)以利用率的方式计算
- 自定义的Pod metrics,以原始值(raw value)的方式计算
- 自定义的object metrics
- 支持两种metrics查询方式:Heapster和自定义的REST API
- 支持多metrics
1、开启指标服务
用于之后查看资源的cpu、内存的指标。
# 下载 metrics-server 组件配置文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server-components.yaml# 修改镜像地址为国内的地址
sed -i 's/k8s.gcr.io\/metrics-server/registry.cn-hangzhou.aliyuncs.com\/google_containers/g' metrics-server-components.yaml# 修改容器的 tls 配置:不验证 tls,在 containers 的 args 参数中增加 “--kubelet-insecure-tls” 参数# 安装组件
kubectl apply -f metrics-server-components.yaml# 查看 pod 状态
kubectl get pods --all-namespaces | grep metrics# 查看 pods 资源使用情况
kubectl top pods
2、cpu、内存指标监控
实现 cpu 或内存的监控,首先有个前提条件是该对象必须配置了 resources.requests.cpu
或 resources.requests.memory
才可以,然后配置当 cpu、memory 达到上述配置的百分比后进行扩容或缩容。
创建一个 HPA:
- 先准备一个好一个有做资源限制的 deployment
- 执行命令
kubectl autoscale deploy nginx-deploy --cpu-percent=20 --min=2 --max=5
(cpu利用率达到20%左右,进行扩容,上限为5个;利用率降到20%以下时,进行缩容,最低降为2个) - 通过
kubectl get hpa
可以获取 HPA 信息
# 测试:找到对应服务的 service,编写循环测试脚本提升内存与 cpu 负载
while true; do wget -q -O- http://<ip:port> > /dev/null ; done
# 可以通过多台机器执行上述命令,增加负载,当超过负载后可以查看 pods 的扩容情况 # 查看 pods 资源使用情况
kubectl top pods# 扩容测试完成后,再关闭循环执行的指令,让 cpu 占用率降下来,然后过 5 分钟后查看自动缩容情况
3、自定义 metrics
- 控制管理器开启–horizontal-pod-autoscaler-use-rest-clients
- 控制管理器的–apiserver指向API Server Aggregator
- 在API Server Aggregator中注册自定义的metrics API