3. 控制器(Controller)
3.1 副本集(ReplicaSet)
定义:副本集(ReplicaSet)的目的是为了保证一组稳定的Pod副本在任意给定时刻都在运行。因此,它通常用于保证特定数量的相同Pod的可用性。
副本集使用一些字段进行定义,这些字段包含了一个选择器(指定如何识别可以获取的pods)、一个数字(表示应该维持多少个副本)、一个Pod模板(pod template,指定了新的Pod的创建标准)。然后,复副本集将根据需要进行创建或删除pods以达到所需的数量,从而实现其目的。当副本集需要创建新的Pod时,就会使用Pod模板。
副本集和Pod之间的链接是通过Pod的metadata.ownerReferences
字段,该字段指定了当前对象是属于哪个资源的,正是通过这个链接,副本集才知道它所维护的pods的状态,并据此进行计划。一个副本集主要是通过使用选择器辨识新的Pod,如果一个Pod没有OwnerReference(所属资源)或者OwnerReference(所属资源)不是一个控制器,它一旦和副本集的选择器匹配上了,它将会被副本集立即获取。
副本集(ReplicaSet)保证了特定数量的Pod副本在任意状态都是处于运行状态。然而,Deployment是一个相对比较高层次的概念,它负责管理副本集(ReplicaSet)以及提供对pods的声明性更新以及许多其他有用的功能。相对于直接使用副本集(ReplicaSets),更加推荐使用Deployments来操作,除非需要自定义更新业务流程、或者根本不需要更新。因为实际上可能从不需要操作副本集对象,所以从这个角度也是推荐使用Deployments来替换,在spec部分来定义应用。下面是直接创建副本集的一个实例:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# 下面是定义副本集的部分
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
下面的命令将上述的清单保存到frontend.yaml
并将其提交给K8S集群,它会创建定义的副本集(ReplicaSet)和它所管理的Pod:
kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml
注意yaml文件的缩进,很容易出错的,将上述的镜像改成我自己打包的镜像,或者在worker node上拉镜像再改名(我这里是后者):
docker pull registry.cn-shanghai.aliyuncs.com/hhu/gb-frontend:v3
docker tag registry.cn-shanghai.aliyuncs.com/hhu/gb-frontend:v3 gcr.io/google_samples/gb-frontend:v3
docker rmi registry.cn-shanghai.aliyuncs.com/hhu/gb-frontend:v3
查看当前部署的副本集(ReplicaSets):
kubectl get rs
# 本地结果
NAME DESIRED CURRENT READY AGE
frontend 3 3 3 5m24s
curl-66959f6557 1 1 1 15d
jenkins-7958858b5d 1 1 1 11d
my-nginx-64fc468bd4 0 0 0 15d
可以看到刚刚创建的frontend(上述的镜像和配置文件国内是拉不下来的),也可以检查副本集(Replicaset)的状态:
# 查看frontend的状态
kubectl describe rs/frontend
# 结果
Name: frontend
Namespace: default
Selector: tier=frontend
Labels: app=guestbook
tier=frontend
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: tier=frontend
Containers:
php-redis:
Image: gcr.io/google_samples/gb-frontend:v3
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 4m26s replicaset-controller Created pod: frontend-chgz5
Normal SuccessfulCreate 4m26s replicaset-controller Created pod: frontend-lmqv7
Normal SuccessfulCreate 4m26s replicaset-controller Created pod: frontend-4mklc
查看Pod(有3个frontend的Pod):
# 查看Pod
kubectl get Pods
# 结果
NAME READY STATUS RESTARTS AGE
curl-66959f6557-pn49b 1/1 Running 2 15d
frontend-4mklc 1/1 Running 0 5m4s
frontend-chgz5 1/1 Running 0 5m4s
frontend-lmqv7 1/1 Running 0 5m4s
jenkins-7958858b5d-27qlx 1/1 Running 1 11d
当然还可以验证这些Pod的owner reference是设置到了frontend副本集(ReplicaSet),可以通过获取某个运行的Pod的yaml文件查看:
# 查看frontend-4mklc的pod的yaml文件
kubectl get pods frontend-4mklc -o yaml
# 部分输出
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2019-04-28T06:27:56Z"
generateName: frontend-
labels:
tier: frontend
name: frontend-4mklc
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: frontend
uid: c2d0bc7c-697e-11e9-a84a-000c292a92cd
resourceVersion: "3146592"
selfLink: /api/v1/namespaces/default/pods/frontend-4mklc
uid: c2e03103-697e-11e9-a84a-000c292a92cd
spec:
containers:
- image: gcr.io/google_samples/gb-frontend:v3
imagePullPolicy: IfNotPresent
name: php-redis
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-gb6cw
readOnly: true
...
【采集非模板Pod】
当创建裸仓(bare Pod)并没有问题,强烈推荐确保裸仓不要有和任何副本集(ReplicaSet)标签选择器匹配的,因为副本集(ReplicaSet)不限制模板指定哪些Pod属于该副本集——它可以以规定的方式获取其他的Pod,以之前的前端副本集为例,Pod在下面的清单中指定:
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
tier: frontend
spec:
containers:
- name: hello1
image: registry.cn-shanghai.aliyuncs.com/hhu/hello-app:2.0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
tier: frontend
spec:
containers:
- name: hello2
image: registry.cn-shanghai.aliyuncs.com/hhu/hello-app:1.0
上述的Pod没有控制器作为它们的owner reference、没有匹配的前端副本集,假设在前端副本集已经部署了、并这个副本集已经被设置为它的初始化Pod的副本,然后创建了Pod,已经将这个Pod设置为初始Pod副本去填充必要的副本数量:kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml
,新的Pod将会通过副本集(ReplicaSet)获取,然后然后立即终止,因为副本集会超过其所需的计数。注意这里的顺序是创建副本集–>再通过副本集创建Pod,如果先创建Pod(kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml
),再创建副本集(kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml
),此时通过kubectl get Pods
只会发现只有一个Pod。
【副本集RepicaSet清单】
副本集也是K8S中的API对象,一个ReplicaSet需要apiVersion
、kind
以及metadata
字段,对于ReplicaSet,kind
永远都仅仅是ReplicaSet
,副本集也需要.spec
。
Pod模板,.spec.template
字段是Pod模板,它也需要标签,在之前的frontend.yaml
栗子中,有一个标签tier: frontend
,注意不要和其他控制器的标签选择器重叠,以免它们使用这个Pod,对于Pod模板的重启策略(restart policy字段.spec.template.spec.restartPolicy
)只能指定为Always
(这也是默认值)。
Pod 标签选择器,即.spec.selector
,用于辨识Pod,在之前的frontend.yaml
栗子中,选择器位:
matchLabels:
tier: frontend
在副本集(ReplicaSet)中,.spec.template.metadata.labels
必须和spec.selector
,否则会直接被API拒绝请求。注意:2个ReplicaSet指定相同的.spec.selector
、但指定的.spec.template.metadata.labels
和.spec.template.spec
字段不同,每个ReplicaSet会忽略其他ReplicaSet创建的pod。
副本:可以通过.spec.replicas
设置多少个Pod应该并行运行,然后由副本集控制添加、删除Pod以满足这个数字,如果不指定,默认为1
。
【副本集的操作】
ReplicaSet常见的行为有:
1.删除副本集以及由其创建的Pod
必须将下面的-d
选项中的propagationPolicy
设置为Background
或者Foreground
:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"
2.仅仅删除副本集
必须将下面的propagationPolicy
设置为Orphan
:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"
原始的副本集删除后,可以创建新的副本替换它并接管它之前创建的Pod,只要新的副本集中的.spec.selector
和之前的一样就行,但新的副本集不会做出任何努力使现有的pod匹配新的、不同的pod模板。如果要升级Pod到新的,需要使用 rolling update。
3.将Pod从副本集中隔离
这里是需要修改Pod上的标签即可,以这种方式从副本集中隔离开来的Pod会自动被新的Pod替换。
4.对副本集进行伸缩
通过更新.spec.replicas
可以轻松对副本集进行伸缩,ReplicaSet Controller将确保这个数量的Pod副本。
5.作为水平Pod自动定标器目标的副本集
副本集也可以是水平Pod自动分频器( Horizontal Pod Autoscalers-HPA)的目标,即副本集可以由hpa自动缩放,下面是栗子:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: frontend
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
保存上述清单到hpa-rs.yaml
并提交到K8S集群就会创建定义的HPA,它将根据复制的Pod的CPU使用情况自动缩放目标副本集kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml
,还可以使用kubectl autoscale
完成同样的事:kubectl autoscale rs frontend --max=10
(这种方式更简单)。