4.3、资源控制器
4.3.1、概念
Kubernetes中内建了很多种controller(控制器),这些相当于一个状态机,用来控制Pod的具体状态和行为。
4.3.2、分类
ReplicationController&ReplicaSet
ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数量,即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收;
在新的Kubernetes中建议使用ReplicaSet来取代ReplicationController。ReplicaSet跟前者没有本质的不同,只是名字不一样,并且ReplicaSet支持集合式的selector;
例子
mkdir -p /usr/local/docker/kubernetes/plugins/test/res
cd /usr/local/docker/kubernetes/plugins/test/res
vim rs.yml
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: frontend
spec:
replicas: 3 # 副本数 3
selector:
matchLabels: # 标签
tier: frontend
template:
metadata: # 必须和上面定义的label匹配
labels:
tier: frontend
spec:
containers:
- name: rs-nginx
image: habor-repo.com/library/nginx:v1
env: # 容器添加环境变量
- name: GET_HOST_FROM
value: dns
ports:
- containerPort: 80 # 容器的端口
测试:
kubectl create -f rs.yml
[root@k8s-master res]# kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-2mgxs 1/1 Running 0 5s
frontend-4rb72 1/1 Running 0 5s
frontend-fxlhr 1/1 Running 0 5s # 三个正常运行
# 查看标签
[root@k8s-master res]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
frontend-2mgxs 1/1 Running 0 2m8s tier=frontend
frontend-4rb72 1/1 Running 0 2m8s tier=frontend
frontend-fxlhr 1/1 Running 0 2m8s tier=frontend
# 编辑标签
kubectl label pod frontend-2mgxs tier=frontend-1 --overwrite=true
#再次查看标签
[root@k8s-master res]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
frontend-2mgxs 1/1 Running 0 4m29s tier=frontend-1
frontend-428wm 1/1 Running 0 5s tier=frontend
frontend-4rb72 1/1 Running 0 4m29s tier=frontend
frontend-fxlhr 1/1 Running 0 4m29s tier=frontend
# 发现变成了四个容器,这是因为在定义rs.yml中的matchLabels会匹配template中的label,而此时frontend-2mgxs的label被修改了,rs就一维其中一个pod挂掉了,于是重新创建了一个pod来维持replicas=3的状态
# 查看所有的 replicaset 简写为 rs
[root@k8s-master res]# kubectl get rs
NAME DESIRED CURRENT READY AGE
frontend 3 3 3 12m
# 删除某个 rs
kubectl delete rs frontend
# 删除所有pod
kubectl delete pod --all
Deployment
Deployment为Pod和ReplicaSet提供了一个声明式定义(declarative)的方法,用来替代以前的ReplicationController来方便的管理应用。典型场景包括:
- 定义Deployment来创建Pod和ReplicaSet
- 滚动升级和回滚应用
- 扩容和缩容
- 暂停和继续
例子
vim deployment.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # 副本数 3
template:
metadata: # 必须和上面定义的label匹配
labels:
app: nginx
spec:
containers:
- name: nginx
image: habor-repo.com/library/nginx:v1
env: # 容器添加环境变量
- name: GET_HOST_FROM
value: dns
ports:
- containerPort: 80 # 容器的端口
测试:
kubectl apply -f deployment.yml
#kubectl create 和 apply 的区别
#1. kubectl create命令,是先删除所有现有的东西,重新根据yaml文件生成新的。所以要求yaml文件中的配置必须是完整的
#2. kubectl create命令,用同一个yaml 文件执行替换replace命令,将会不成功,fail掉。
# 查看 deployment
kubectl get deploy
#或者
kubectl get deployment
[root@k8s-master res]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 3m22s
# 查看 rs
[root@k8s-master res]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-5b5cd5679f 3 3 3 4m9s
# deployment创建的时候会创建对应点rs,这里的nginx-deployment-5b5cd5679f就是对应的rs
# 查看 pod
[root@k8s-master res]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-5b5cd5679f-4vwmv 1/1 Running 0 5m21s
nginx-deployment-5b5cd5679f-845ms 1/1 Running 0 5m21s
nginx-deployment-5b5cd5679f-87s8f 1/1 Running 0 5m21s
# 发现 pod名字是rs名字加上随机后缀
扩容
# 扩容为10个副本
kubectl scale deployment nginx-deployment --replicas 10
# 集群支持horizontal pod autoscaling 的话还可以自动扩展
kubectl autoscale deployment nginx-deploment --min=10 --max=15 --cpu-percent=80
更新&回滚
# ***************** 更新 ****************
# 1) kubectl set image deployment/[deployent名] [容器名]=[镜像名]
kubectl set image deployment/nginx-deployment nginx=habor-repo.com/library/nginx:v2
# 2) kubectl edit deployment/[deployent名]
kubectl edit deployment/nginx-deployment
# ***************** 回滚 ****************
# 查看历史 回滚
kubectl rollout history deployment/nginx-deployment
# 查看历史 RS(ReplicaSet)
kubectl get rs
# 1) 回滚到上一个版本
kubectl rollout undo deployment/nginx-deployment
# 2) 回滚到指定历史版本
kubectl rollout undo deployment/nginx-deployment --to-version=2
# 暂停deployment 的更新(暂停后当前的nginx继续可用,更新的pod暂时不可用)
kubectl rollout pause deployment/nginx-deployment
# 恢复deployment 更新
kubectl rollout resume deployment/nginx-deployment
# 查看回滚状态
kubectl rollout status deployment/nginx-deployment
# 如果 rollout 完成会返回一个状态值 0,必须紧跟着回滚命令执行
echo $?
更新策略
-
Deployment可以保证在升级的时候只有一定数量的Pod是down的。默认的,他会确保至少有比期望的pod数量少一个是up状态(最多一个不可用)。
-
Deployment同时也可以确保只创建出超过期望数量的一定数量的Pod。默认的,他会确保最多比期望的Pod数量多的一个Pod是up的(最多一个surge,最多超出一个)
-
未来的kubernetes版本中,将从1-1变成25%-25%(即更新的时候先创建replicas值25%新pod,然后停掉replicas的25%的旧pod)
kubectl describe deployment [deployment名]
rollover(并行rollout)
加入创建的10个replicas的nginx:v1,当前启动了三个pod的时候你把镜像修改为了nginx:v2,那么deployment会立即杀掉已经启动的nginx:v1,剩下的7个nginx会自动使用升级后的nginx:v2。
清理Policy
设置.spec.revisionHistoryLimit
来指定deployment最多保留多少个revision历史记录,如果设置为0表示没有历史记录无法回滚。
# 查看文档:
[root@k8s-master ~]# kubectl explain deployment.spec | grep re
revisionHistoryLimit <integer> # 文档说明
The number of old ReplicaSets to retain to allow rollback. This is a
"retaining all old RelicaSets".
DEPRECATED. The config this deployment is rolling back to. Will be cleared
Label selector for pods. Existing ReplicaSets whose pods are selected by
The deployment strategy to use to replace existing pods with new ones.
template <Object> -required-
Template describes the pods that will be created.
DaemonSet
DaemonSet用来确保全部(或者一些)Node上运行一个Pod的副本。当有Node加入集群时,也会为他们新增一个Pod。当有Node从集群中移除时,这些Pod会被回收。删除DaemonSet将会删除创建的所有的Pod。典型应用场景:
- 运行集群存储daemon,例如每个Node运行glusterd、ceph
- 在每个Node上运行日志收集daemon,例如fluentd、logstash
- 在每个Node上运行监控daemon,例如Prometheus NodeExplorer、collectd、Datadog代理、New Relic代理,或者Ganglia gmod
例子
vim daemonset.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: daemon-example
labels:
app: daemonset
spec:
# replicas: 3 # 不允许该参数,因为DaemonSet在Node上只能有一个副本
selector:
matchLabels:
name: daemon-example
template:
metadata: # 必须和上面定义matchLabels的label匹配
labels:
name: daemon-example
spec:
containers:
- name: nginx
image: habor-repo.com/library/nginx:v1
env: # 容器添加环境变量
- name: GET_HOST_FROM
value: dns
ports:
- containerPort: 80 # 容器的端口
测试
kubectl create -f daemonset.yml
# ds 是 daemonset 的缩写
[root@k8s-master ~]# kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemon-example 2 2 2 2 2 <none> 5s
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
daemon-example-2jm49 1/1 Running 0 30s 10.244.1.38 k8s-node1 <none> <none>
daemon-example-tqzcp 1/1 Running 0 30s 10.244.2.51 k8s-node2 <none> <none>
# 删除
kubectl delete ds daemon-example
Job
Job负责批处理任务,即仅执行一次的任务,他能保证批处理任务的一个或者多个Pod成功结束
特殊说明
-
spec.template
格式同Pod
-
RestartPolicy
仅支持Never
或OnFailure
- 单个Pod时,默认Pod成功运行后Job结束
-
.spec.completions
标志Job结束运行的Pod个数,默认是1 -
.spec.parallelism
标志并行运行的Pod个数,默认是1 -
.spec.activeDeadlineSecond
标志失败Pod的重试最大时间,超过这个时间不会继续重试(单位:秒)
例子
vim job.yml
apiVersion: batch/v1
kind: Job
metadata:
name: echo
spec:
template:
metadata:
name: echo
spec:
containers:
- name: echo
image: habor-repo.com/library/busybox:v1
command: ['sh', '-c', 'echo the job app is running! && sleep 30']
restartPolicy: Never
测试:
kubectl create -f job.yml
# 查看 job
[root@k8s-master ~]# kubectl get job
NAME COMPLETIONS DURATION AGE
echo 0/1 11s 11s
# 查看 pod
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
echo-7cn2h 1/1 Running 0 27s
# 查看日志
[root@k8s-master ~]# kubectl logs echo-7cn2h
the job app is running!
# 查看描述
[root@k8s-master ~]# kubectl describe pod echo-7cn2h
# 再次查看状态
[root@k8s-master ~]# kubectl get job
NAME COMPLETIONS DURATION AGE
echo 1/1 31s 82s
# 删除 job (注意删除job不会删除pod,还需要手动删除pod)
kubectl delete job echo
kubectl get job # 发现没有job
CronJob
管理基于时间的Job,即:
- 在给定时间点只运行一次
- 周期性的在给定时间点运行
使用前提:当前Kubernetes集群版本>=1.8。对于旧版本集群,启动API Server时,通过传递选项--runtime-config=batch/v2alph1=true
可以开启batch/v2alph1 API。
特殊说明
-
.spec.schedule
:调度,必需字段,指定任务运行周期,格式同Cron表达式 -
.spec.jobTemplate
:Job模板,必需字段,指定需要运行的任务,格式同Pod
-
.spec.startingDeadlineSecond
:启动Job
的期限(单位:秒),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的Job将被认为是失败的。如果没有指定,则没有期限。 -
.spec.concurrencyPolicy
:并发策略,可选字段,指定了如何处理被Cron Job创建的Job的并发执行。允许下面策略中的一种:-
Allow
(默认):允许并发运行Job -
Forbid
:禁止并发运行。如果前一个还没完成,直接跳过下一个 -
Replace
:取消当前正在运行的Job,用一个新的来替代
注意:当前策略只能应用于同一个Cron Job创建Job。如果存在多个Cron Job,他们创建的Job之间总是并发运行。
-
-
.spec.suspend
:挂起,该字段也是可选的,如果设置为true
,后续所有的执行都会被挂起。它对应开始执行的Job不起作用。默认是false
-
.spec.successfulJobsHistoryLimit
和.spec.failedJobsHistoryLimit
:历史限制,可选字段,指定可以保留多少完成和失败的Job。默认值是3
和1
。如果设置为0
那么相关类型的Job完成后将不会被保留。
例子
vim cronjob.yml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: habor-repo.com/library/busybox:v1
command: ['sh', '-c', 'echo [$date]the cronjob app is running!']
restartPolicy: OnFailure
测试:
# 创建
kubectl create -f cronjob.yml
# 查看 cronjob
[root@k8s-master ~]# kubectl get cronjob
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
hello */1 * * * * False 0 <none> 5s
# 查看 job 需要等待执行完毕后才能看到 job 出现
[root@k8s-master ~]# kubectl get job -w
NAME COMPLETIONS DURATION AGE
hello-1583557200 1/1 1s 8s
# 查看 pod 需要 job 执行完毕后才出现
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-1583557200-n2c8m 0/1 Completed 0 38s
# 查看日志
[root@k8s-master ~]# kubectl logs hello-1583557200-n2c8m
[] cronjob app is running!
# 删除
kubectl delete cronjob --all
StatefulSet
StatefulSet作为Controller为Pod提供的唯一标识。他可以保证scale的顺序
StatefulSet是为了解决所有状态服务的问题(对应Deployment个ReplicaSet是无状态而设计),其应用场景包括:
- 稳定持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
- 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
- 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init container来实现
- 有序收缩,有需删除(即从N-1到0)
加入有个StatefulSet的name为web:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
- 匹配 Pod name ( 网络标识 ) 的模式为:
$(statefulset名称)-$(序号)
,比如上面的示例:web-0,web-1,web-2 - StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为:
$(podname).(headless servername)
,也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化 - StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:
$(servicename).$(namespace).svc.cluster.local
,其中,“cluster.local” 指的是集群的域名
Horizontal Pod Autoscaling
应用的资源使用率都有高峰和低谷的时候,消峰填谷,提高集群整体的资源利用率,让service中的pod自定调整,即使Pod水平缩放。
# 以镜像 gcr.io/google_containers/hpa-example 举例子
kubectl run php-apache --image=gcr.io/google_containers/hpa-example --requests=cpu=200m --expose--port=80
# 创建 HPA 控制器 (相关算法参考文档)
# https://github.com/kubernetes/community/blob/master/contributors/design-proposals/horizontal-pod-autoscaler.md#autoscaling-algorithm
kubectl autoscale deployment php-apache --cpu-percent=50--min=1--max=10
# 增加负载,查看负载节点数目
kubectl run -i--tty load-generator --image=busybox /bin/sh
# whiletrue; dowget-q-O- http://php-apache.default.svc.cluster.local; done
# 实时观察php-apache的资源占用情况
# kubectl top pod -n <名称空间>
# 例如:
kubectl top pod -n kube-system
资源限制
文档:https://kubernetes.io/docs/concepts/policy/resource-quotas/
1)Pod级别限制:
Kubernetes 对资源的限制实际上是通过 cgroup 来控制的,cgroup 是容器的一组用来控制内核如何运行进程的相关属性集合。针对内存、CPU 和各种设备都有对应的 cgroup
默认情况下,Pod 运行没有 CPU 和内存的限额。这意味着系统中的任何 Pod 将能够像执行该 Pod 所在的节点一样,消耗足够多的 CPU 和内存。一般会针对某些应用的 pod 资源进行资源限制,这个资源限制是通过resources 的 requests 和 limits 来实现
例如:
kubectl explain pod.spec.containers.resources
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: default
labels:
app: myapp
spec:
containers:
- name: nginx
imagePullPolicy: IfNotPresent
image: habor-repo.com/library/nginx:v1
ports:
- name: http
protocol: TCP
containerPort: 80
resources:
limits: # 上线
cpu: 4
memory: 2Gi
requests: # 初始
cpu: 250m # 250m HZ 频率的CPU
memory: 1Gi
2)名称空间级别:
2.1)、计算资源配额
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
namespace: spark-cluster
spec:
hard:
pods: 20
requests.cpu: 20
requests.memory: 100Gi
limits.cpu: 40
limits.memory: 200Gi
2.2)、配置对象数量配额限制
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
namespace: spark-cluster
spec:
hard:
configmaps: 20 # 最大configmap
persistentvolumeclaims: 4 # 最大pvc
replicationcontrollers: 20 # 最大副本
secrets: 10
services: 10
services.loadbalancers: 2
2.3)、配置 CPU 和内存 LimitRange
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
spec:
limits:
- default: # 即 limit 的值
memory: 50Gi
cpu: 5
defaultRequest: # 即 request 的值
memory: 1Gi
cpu: 1
type: Container