在K8S中存在很多controller,包括ReplicaSet, ReplicationController, Deployment, StatefulSet等,这里不研究ReplicationController,因为不建议使用。本节将对这些controller进行学习和总结,参见文档 Controller。

ReplicaSet

ReplicaSet的目的是在任何时候都maintain一组稳定的pod replicas,常用来确保指定数量的pod replica可用。ReplicaSet具有selector,replicas, podTemplate字段。通过pod的metadata.ownerReferences字段来关联ReplicaSet和Pod。

注意:当创建的pod的label符合ReplicaSet的selector,且该pod不属于任何controller,则该pod会被自动归为该ReplicaSet拥有,导致ReplicaSet下的pod不相同;同时,会影响ReplicaSet本身的replica pod的个数。

建议使用Deployment来管理ReplicaSet,而不是直接使用,除非需要定制化的更新测例或不需要更新。

下面是ReplicaSet的示例。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

ReplicaSet中.spec.template.metadata.labels必须跟spec.selector 相同,不然会被API拒绝。

使用kubectl delete可以删除ReplicaSet及其pod,Garbage collection默认会删除所有相关pod。若只想删除ReplicaSet而不删除pod,需要指定--cascade=false。删除ReplicaSet后,可以创建具有相同selector的ReplicaSet来管理原先的pod,但不会使用新ReplicaSet的podTemplate来更新原先的pod。若想自动更新pod,使用Deployment。

Deployment

Deployment对Pod和ReplicaSet提供声明式的更新。在描述预期状态后,Deployment会以可控的频率下使得实际状态符合预期状态。

下面是Deployment的示例,将创建一个ReplicaSet来创建3个pod。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

下面是一些查看命令:

#查看deployment的rollout status
kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out

#查看创建的ReplicaSet
kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-75675f5897   3         3         3       18s

ReplicaSet的名称为[DEPLOYMENT-NAME]-[RANDOM-STRING]。Random-String是根据pod-template-hash随机生成的(项目中就是pod-template-hash的值)。这里的ReplicaSet用来确定replicas数量的pod可用。

注意:指定selector和labels时需要注意不跟其他controller重复,k8s不会限制这种冲突,但会造成不期望的行为。

pod-template-hash label被deployment添加到每个创建的ReplicaSet。该label是根据ReplicaSet中的PodTemplate取hash生成的。

Deployment的rollout只能因为Pod template变了,包括label和image,其他更新像scale up/down并不触发rollout。

下面是更新image的方法:

kubectl set image deployment/nginx-deployment nginx=nginx:1.91 --record
#edit deployment,update image
kubectl edit deployment.v1.apps/nginx-deployment

查看rollout status:

$kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...

$kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           36s

$kubectl describe deploy <deploy-name>

#deployment由rs 2035384211 更新为 rs 1564180365
$kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       6s
nginx-deployment-2035384211   0         0         0       36s

默认情况下,Deployment保证最多25%的pod不可靠,pod数量最多超出25%。

-Rollout & Rollback

每次rollout都会创建revision,默认系统会保存deployment的rollout,可通过指定revision来rollback deployment。通过.spec.revisionHistoryLimit来设置revision保存的数量,默认为10. Old replicaSet保存在etcd中。

下面的命令用来查看rollout history并rollback到指定的revision。

#查看deployment的rollout history
$kubectl rollout history deployment.v1.apps/nginx-deployment
deployments "nginx-deployment"
REVISION    CHANGE-CAUSE
1           kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml --record=true
2           kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1 --record=true
3           kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.91 --record=true

#查看deployment的特定revision
$kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2

#rollback到上一个revision
$kubectl rollout undo deployment.v1.apps/nginx-deployment

#rollback到指定revision
$kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2

#查看deployment的event来查看rollback情况
$kubectl describe deployment nginx-deployment
  Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
  Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0

除了更新deployment的PodTemplate之外,还可以scale deployment。若cluster启动HPA(horizontal Pod autoscaling), 可以设置deployment的autoscaler。

kubectl scale deployment.v1.apps/nginx-deployment --replicas=1
kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80

-Pause & Resume Deployment

可以通过pause和resume deployment来避免deployment在多次更新时rollout。Pause后,之前的配置仍然起作用,之后的一系列更新还没起作用。.spec.paused为boolean值,指定是否处于暂停状态。

# Pause deployment
kubectl rollout pause deployment.v1.apps/nginx-deployment

#Resume deployment
kubectl rollout resume deployment.v1.apps/nginx-deployment

.spec.strategy用来指定new pod更新old pod的策略,.spec.strategy.type可以为“Recreate” or “RollingUpdate”,前者在创建new pod前删除所有old pod;后者通过rolling update的方式来更新,需要指定maxUnavailable和maxSurge,可以是比例,也可以是具体值,但不能同时为0,默认值为25%.

StatefulSets

StatefulSets主要用来管理有状态的app。跟Deployment一样,StatefulSet用来管理Pod,但不同的是,StatefulSets中的每个pod都维护一个严格的identifier并在reschedule时不变。

StatefulSet具有以下特点:

  • Stable, unique network identifiers.
  • Stable, persistent storage.
  • Ordered, graceful deployment and scaling.
  • Ordered, automated rolling updates.

限制:

  • Pod的storage要么基于请求的storage class由PersistentVolume Provisioner设置,要么由admin提前设置好;
  • 删除或scale down StatefulSet将不会删除相关的volumes,用于保证数据安全;
  • StatefulSets目前需要创建headless service来负责pod的netework identity,用户负责创建该headless service;
  • StatefulSet删除后不保证pod结束,pod会按顺序gracefully的结束;
  • 当使用默认的Pod Management Policy(OrderedReady)作为rollupdate policy,可能需要人工干预来走出broken state。

StatefulSet通常包含一个headless service, statefulset和Persistent volumes.下面是一个示例.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

k8s 1.8及之后版本,.spec.selector必须与.spec.template.metadata.labels相同。

StatefulSet中的pod具有唯一的identity,由ordinal index,stable network identity,stable storage组成。该identity绑定在pod上,不管pod被分配到那个node。若StatefulSet存在N个pod, ordinal index为0~N-1.

Pod的hostname由StatefulSet的name以及其ordinal index组成,$(statefulset name)-$(ordinal)。示例中将创建3个pod,分别名为web-0, web-1, web-2.

StatefulSet使用headless service来控制pod的domain。Service控制的domain的格式为$(service name).$(namespace).svc.cluster.local,其中cluster.local为cluster domain。当Pod创建后,获得匹配的DNS subdomain,格式为$(podname).$(governing service domain),其中governing service由StatefulSet中的serviceName字段来指定。下图给出之前的关系。

d7b4e1e478ca02ab19e6cdd1226999e8.png

K8s为每个VolumeClaimTemplate创建一个PersistentVolume,示例中每个pod将获取一个PersistentVolume,其storage class为my-storage-class并具有1GB的存储。当不指定storage class时,将使用默认的storage class。当Pod被schedule到node上时,它的volumeMounts将mount生成的PersistentVolume。

注意:PersistentVolumes在pod或StatefulSet删除后并不会被删除,需要人工删除。

StatefulSet创建pod后会在pod上添加http://statefulset.kubernetes.io/pod-name的label,值为pod name。

StatefulSet上部署和扩展的规则:创建pod的顺序由0到N,删除的顺序正好相反;扩展时,前面的pod必须是Running和Ready状态,删除时,后续pod必须是terminated。

K8S 1.7之后,可以使用.spec.podManagementPolicy来放松对顺序的要求,可以只保证uniqueness and identity。当Policy为OrderedReady时,也是默认设置,将保证顺序;Policy为Parallel将并行处理scale。

K8S 1.7之后,指定.spec.updateStrategy来设置更新策略,当.spec.updateStrategy.type为OnDelete时,在pod template改动后将不会自动更新Pods;为RollingUpdate,默认策略,将自动更新pod。RollingUpdate更新策略可以设置partition,通过设置字段 .spec.updateStrategy.rollingUpdate.partition来实现,当pod template更新后,只有ordinal大于等于partition的pod才会更新,小于partition的pod不更新。当.spec.replicas小于partition,所有pod不更新。

DaemonSet

DaemonSet确保全部或一些Node上运行一份Pod,当增加node时自动在其上创建pod,删除node时,其上的pod被回收。删除DaemonSet,将删除所有相关的pod。

一些使用DaemonSet的场景:

3c5cbc60db4862ce04e8b18884881528.png

下面是DaemonSet的示例。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

.spec.selector包含2个field:matchLabels和matchExpressions。当2个都指定时,为And的关系。默认DaemonSet将在所有node上创建pod,但若指定nodeSelector或affinity,pod将只在指定node上创建。

正常情况下,pod运行在哪个node上由k8s scheduler决定,而DaemonSet pods的调度是通过DaemonSet Controller确定。

与Daemon Pods交互的方法:1. DaemonSet中的pod向其他service推送消息;2. NodeIP以及known port,使用NodeIP:Port直接访问;3.DNS,使用相同的pod selector创建headless service,使用endpoints资源来发现DaemonSet;4. Service,创建具有相同pod selector的service,来访问DaemonSet中一个Pod。

当node label改变时,DaemonSet将会删除不符合条件node上的pod并向新符合条件node上创建pod。可以删除DaemonSet,当指定--cascade=false时不会删除相关pod。

当需要在所有或一些node上确保运行指定pod或pod需要比其他pod更早启动时,使用DaemonSet。

------------------>

Garbage Collection

用来删除不具有owner的objects。每个从属object通过metadata.ownerReferences来指定其owner object。ownerReferences有时被自动设置,也可以手动设置,不可以跨namespace。

通过设置deleteOptions参数中的propagationPolicy field来指定删除策略,可以为"Orphan", "Foreground", or "Background". K8S 1.9之前,默认为Orphan, 之后,默认会删除从属objects。

Orphan将只删除owner object,不删除从属objects。Foreground和Background将会删除owner object和从属objects,只是前者先删从属object,后者先删owner object。

下面是通过API来删除的示例。

kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset 
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' 
  -H "Content-Type: application/json"

kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset 
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' 
  -H "Content-Type: application/json"

kubectl proxy --port=8080
curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/replicasets/my-repset 
  -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' 
  -H "Content-Type: application/json"

下面是使用kubectl来删除的示例。cascade为false表示不删除从属object,默认为true。

kubectl delete replicaset my-repset --cascade=false

<------------------

Job

Job创建一个或多个pod并确保指定数量的pod成功终止。当指定数量的pod成功终止后,job结束。场景:当创建的pod不能完成时,job会创建新的pod来完成任务。

下面是Job的示例。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

下面在shell中获取对应的pod:

pods=$(kubectl get pods --selector=job-name=pi --output=jsonpath='{.items[*].metadata.name}')
echo $pods

restartPolicy只允许Never或OnFailure。

通过设置.spec.completions和.spec.parallelism属性来完成不同的任务,前者表示成功完成的任务数量,后者表示并行任务数量,默认都为1.当.spec.parallelism设置为0时,job将被暂停。

通过.spec.backoffLimit来设置pod失败后的重试次数,默认为6,重试的delay时间指数式增长,最长6分钟。

当Job complete,将不会创建pod,old pod也不会被删除,这样可以查看log。同时,Job也存在,这样可以查看status。可以通过kubectl delete来人工删除。

Job终止可能因为失败次数超过.spec.backoffLimit,或者因为job的运行时间炒超过设置的.spec.activeDeadlineSeconds。当Job的运行时间超过activeDeadlineSeconds,所有运行的pod被终止,Job状态变为Failed,reason: DeadlineExceeded。

-Cron Job

终止的Job不被回收会浪费资源,可以使用cron job来管理job并清理job。

Cron Job创建基于时间调度的job,时区为master node的时区。当Cron job大于100次没有被调度,将不会调度job并打印error信息。Cron job只负责创建符合schedule的job,而job负责pod的管理。

-TTL Controller

TTL Controller提供了一种机制来限制resource object执行完存活的时间,目前只处理Job。

一种自动清理结束的job是使用TTL Controller,通过设置Job的.spec.ttlSecondsAfterFinished属性。该属性可以在resouce object创建之后或执行结束之后被修改。

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-with-ttl
spec:
  ttlSecondsAfterFinished: 100
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never

通常创建job时不需要指定.spec.selector,Job自动添加selector并保证与其他selector不重合。你也可以手动设置.spec.selector。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐