Kubernetes StatefulSets

StatefulSets(有状态系统服务设计)在Kubernetes 1.7中还是beta特性,同时StatefulSets是1.4 版本中PetSets的替代品。PetSets的用户参考1.5 升级指南 。

使用StatefulSets

在具有以下特点时使用StatefulSets:

  • 稳定性,唯一的网络标识符。
  • 稳定性,持久化存储。
  • 有序的部署和扩展。
  • 有序的删除和终止。
  • 有序的自动滚动更新。

Pod调度运行时,如果应用不需要任何稳定的标示、有序的部署、删除和扩展,则应该使用一组无状态副本的控制器来部署应用,例如 Deployment 或 ReplicaSet更适合无状态服务需求。

限制

  • StatefulSet还是beta特性,在Kubernetes 1.5版本之前任何版本都不可以使用。
  • 与所有alpha / beta 特性的资源一样,可以通过apiserver配置-runtime-config来禁用StatefulSet。
  • Pod的存储,必须基于请求storage class的PersistentVolume Provisioner或由管理员预先配置来提供。
  • 基于数据安全性设计,删除或缩放StatefulSet将不会删除与StatefulSet关联的Volume。
  • StatefulSets需要Headless Service负责Pods的网络的一致性(必须创建此服务)。

组件

示例:

  • name为nginx的Headless Service用于控制网络域。
  • StatefulSet(name为web)有一个Spec,在一个Pod中启动具有3个副本的nginx容器。
  • volumeClaimTemplates使用PersistentVolumes供应商的PersistentVolume来提供稳定的存储。
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: gcr.io/google_containers/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

部署和扩展

  • 对于具有N个副本的StatefulSet,当部署Pod时,将会顺序从{0..N-1}开始创建。
  • Pods被删除时,会从{N-1..0}的相反顺序终止。
  • 在将缩放操作应用于Pod之前,它的所有前辈必须运行和就绪。
  • 对Pod执行扩展操作时,前面的Pod必须都处于Running和Ready状态。
  • 在Pod终止之前,所有successors都须完全关闭。

不要将StatefulSet的pod.Spec.TerminationGracePeriodSeconds值设置为0,这样设置不安全,建议不要这么使用。更多说明,请参考force deleting StatefulSet Pods.

在上面示例中,会按顺序部署三个pod(name: web-0、web-1、web-2)。web-0在Running and Ready状态后开始部署web-1,web-1在Running and Ready状态后部署web-2,期间如果web-0运行失败,web-2是不会被运行,直到web-0重新运行,web-1、web-2才会按顺序进行运行。

如果用户通过StatefulSet来扩展修改部署pod副本数,比如修改replicas=1,那么web-2首先被终止。在web-2完全关闭和删除之前,web-1是不会被终止。如果在web-2被终止和完全关闭后,但web-1还没有被终止之前,此时web-0运行出错了,那么直到web-0再次变为Running and Ready状态之后,web-1才会被终止。

Pod管理

在Kubernetes 1.7及更高版本中,StatefulSet放宽了排序规则,同时通过.spec.podManagementPolicy字段保留其uniqueness和identity guarantees

OrderedReady Pod Management

OrderedReady Pod Management 是StatefulSets的默认行为。它实现了上述 “部署/扩展” 行为。

Parallel Pod Management

Parallel Pod Management告诉StatefulSet控制器同时启动或终止所有Pod。

Update Strategies

在Kubernetes 1.7及更高版本中,StatefulSet的.spec.updateStrategy字段允许配置和禁用StatefulSet中Pods的containers、labels、resource request/limits和annotations的滚动更新。

删除

当spec.updateStrategy未指定时的默认策略,OnDelete更新策略实现了传统(1.6和以前)的行为。当StatefulSet .spec.updateStrategy.type设置为OnDelete,StatefulSet控制器将不会自动更新StatefulSet中的Pod,用户必须手动删除Pods以使控制器创建新的Pod。

滚动更新

RollingUpdate更新策略实现了自动化,使StatefulSet中的Pod滚动更新。当StatefulSet .spec.updateStrategy.type设置为RollingUpdate,StatefulSet控制器将删除并重新创建StatefulSet中的每个Pod。它将以与Pod终止相同的顺序进行(从最大的序数到最小的顺序)来更新每个Pod。

Partitions

通过指定 .spec.updateStrategy.rollingUpdate.partition来分割RollingUpdate更新策略。如果指定了partition,则当更新StatefulSet时,将更新具有大于或等于partition的序数的所有Pods .spec.template,小于partition的序数的所有Pod将不会被更新。如果一个StatefulSet的.spec.updateStrategy.rollingUpdate.partition大于它.spec.replicas,它的更新.spec.template将不会被传Pods。在通常数情况下,不需要使用partition,但如果需要进行更新,推出金丝雀或执行分阶段推出,可以使用partition。

了解更多 StatefulSet: Kubernetes 中对有状态应用的运行和伸缩

K8S中文社区微信公众号

Kubernetes Replication Controller

注意:建议使用Deployment 配置 ReplicaSet (简称RS)方法来控制副本数。

ReplicationController(简称RC)是确保用户定义的Pod副本数保持不变。

ReplicationController 工作原理

在用户定义范围内,如果pod增多,则ReplicationController会终止额外的pod,如果减少,RC会创建新的pod,始终保持在定义范围。例如,RC会在Pod维护(例如内核升级)后在节点上重新创建新Pod。

注:

  • ReplicationController会替换由于某些原因而被删除或终止的pod,例如在节点故障或中断节点维护(例如内核升级)的情况下。因此,即使应用只需要一个pod,我们也建议使用ReplicationController。
  • RC跨多个Node节点监视多个pod。

示例:

replication.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

下载示例文件然后运行:

$ kubectl create -f ./replication.yaml
replicationcontroller "nginx" created

检查ReplicationController状态:

$ kubectl describe replicationcontrollers/nginx
Name:        nginx
Namespace:   default
Image(s):    nginx
Selector:    app=nginx
Labels:      app=nginx
Replicas:    3 current / 3 desired
Pods Status: 0 Running / 3 Waiting / 0 Succeeded / 0 Failed
Events:
  FirstSeen       LastSeen     Count    From                        SubobjectPath    Type      Reason              Message
  ---------       --------     -----    ----                        -------------    ----      ------              -------
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-qrm3m
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-3ntk0
  20s             20s          1        {replication-controller }                    Normal    SuccessfulCreate    Created pod: nginx-4ok8v

创建了三个pod:

Pods Status:    3 Running / 0 Waiting / 0 Succeeded / 0 Failed

列出属于ReplicationController的所有pod:

$ pods=$(kubectl get pods --selector=app=nginx --output=jsonpath={.items..metadata.name})
echo $pods
nginx-3ntk0 nginx-4ok8v nginx-qrm3m

删除ReplicationController及其Pods

使用kubectl delete命令删除ReplicationController及其所有pod。

当使用REST API或客户端库时,需要明确地执行这些步骤(将副本缩放为0,等待pod删除,然后删除ReplicationController)。

只删除 ReplicationController

在删除ReplicationController时,可以不影响任何pod。

使用kubectl,为kubectl delete指定- cascade = false选项。

使用REST API或go客户端库时,只需删除ReplicationController对象即可。

原始文件被删除后,你可以创建一个新的ReplicationController来替换它。只要旧的和新.spec.selector 相匹配,那么新的将会采用旧的Pod。

ReplicationController隔离pod

可以通过更改标签来从ReplicationController的目标集中删除Pod。

RC常用方式

  • Rescheduling(重新规划)
  • 扩展
  • 滚动更新
  • 多版本跟踪
  • 使用ReplicationControllers与关联的Services

API对象

Replication controller是Kubernetes REST API中的顶级资源。有关API对象更多详细信息,请参见:ReplicationController API对象

RC 替代方法

ReplicaSet

ReplicaSet是支持新的set-based选择器要求的下一代ReplicationController 。它主要用作Deployment协调pod创建、删除和更新。请注意,除非需要自定义更新编排或根本不需要更新,否则建议使用Deployment而不是直接使用ReplicaSets。

Deployment是一个高级的API对象,以类似的方式更新其底层的副本集和它们的Pods kubectl rolling-update。如果您希望使用这种滚动更新功能,建议您进行部署,因为kubectl rolling-update它们是声明式的,服务器端的,并具有其他功能。

Bare Pods

与用户直接创建pod的情况不同,ReplicationController会替换由于某些原因而被删除或终止的pod,例如在节点故障或中断节点维护(例如内核升级)的情况下。因此,即使应用只需要一个pod,我们也建议使用ReplicationController。

其他:JobDaemonSet

K8S中文社区微信公众号

Kubernetes Volume

默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉kubelet将重启启动它时,文件将会丢失;第二:当Pod中同时运行多个容器,容器之间需要共享文件时。Kubernetes的Volume解决了这两个问题。

建议熟悉Pod

背景

在Docker中也有一个docker Volume的概念 ,Docker的Volume只是磁盘中的一个目录,生命周期不受管理。当然Docker现在也提供Volume将数据持久化存储,但支持功能比较少(例如,对于Docker 1.7,每个容器只允许挂载一个Volume,并且不能将参数传递给Volume)。

另一方面,Kubernetes Volume具有明确的生命周期 - 与pod相同。因此,Volume的生命周期比Pod中运行的任何容器要持久,在容器重新启动时能可以保留数据,当然,当Pod被删除不存在时,Volume也将消失。注意,Kubernetes支持许多类型的Volume,Pod可以同时使用任意类型/数量的Volume。

内部实现中,一个Volume只是一个目录,目录中可能有一些数据,pod的容器可以访问这些数据。至于这个目录是如何产生的、支持它的介质、其中的数据内容是什么,这些都由使用的特定Volume类型来决定。

要使用Volume,pod需要指定Volume的类型和内容(spec.volumes字段),和映射到容器的位置(spec.containers.volumeMounts字段)。

Volume 类型

Kubernetes支持Volume类型有:

  • emptyDir
  • hostPath
  • gcePersistentDisk
  • awsElasticBlockStore
  • nfs
  • iscsi
  • fc (fibre channel)
  • flocker
  • glusterfs
  • rbd
  • cephfs
  • gitRepo
  • secret
  • persistentVolumeClaim
  • downwardAPI
  • projected
  • azureFileVolume
  • azureDisk
  • vsphereVolume
  • Quobyte
  • PortworxVolume
  • ScaleIO
  • StorageOS
  • local

emptyDir

使用emptyDir,当Pod分配到Node上时,将会创建emptyDir,并且只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。注:删除容器不影响emptyDir。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

hostPath

hostPath允许挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。

示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data

gcePersistentDisk

gcePersistentDisk可以挂载GCE上的永久磁盘到容器,需要Kubernetes运行在GCE的VM中。与emptyDir不同,Pod删除时,gcePersistentDisk被删除,但Persistent Disk 的内容任然存在。这就意味着gcePersistentDisk能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间“切换”。

提示:使用gcePersistentDisk,必须用gcloud或使用GCE API或UI 创建PD

创建PD

使用GCE PD与pod之前,需要创建它

gcloud compute disks create --size=500GB --zone=us-central1-a my-data-disk

示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    # This GCE PD must already exist.
    gcePersistentDisk:
      pdName: my-data-disk
      fsType: ext4

awsElasticBlockStore

awsElasticBlockStore可以挂载AWS上的EBS盘到容器,需要Kubernetes运行在AWS的EC2上。与emptyDir Pod被删除情况不同,Volume仅被卸载,内容将被保留。这就意味着awsElasticBlockStore能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间“切换”。

提示:必须使用aws ec2 create-volumeAWS API 创建EBS Volume,然后才能使用。

创建EBS Volume

在使用EBS Volume与pod之前,需要创建它。

aws ec2 create-volume --availability-zone eu-west-1a --size 10 --volume-type gp2

AWS EBS配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-ebs
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-ebs
      name: test-volume
  volumes:
  - name: test-volume
    # This AWS EBS volume must already exist.
    awsElasticBlockStore:
      volumeID: <volume-id>
      fsType: ext4

NFS

NFS 是Network File System的缩写,即网络文件系统。Kubernetes中通过简单地配置就可以挂载NFS到Pod中,而NFS中的数据是可以永久保存的,同时NFS支持同时写操作。Pod被删除时,Volume被卸载,内容被保留。这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递。

详细信息,请参阅NFS示例

iSCSI

iscsi允许将现有的iscsi磁盘挂载到我们的pod中,和emptyDir不同的是,删除Pod时会被删除,但Volume只是被卸载,内容被保留,这就意味着iscsi能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间“切换”。

详细信息,请参阅iSCSI示例

flocker

Flocker是一个开源的容器集群数据卷管理器。它提供各种存储后端支持的数据卷的管理和编排。

详细信息,请参阅Flocker示例

glusterfs

glusterfs,允许将Glusterfs(一个开源网络文件系统)Volume安装到pod中。不同于emptyDir,Pod被删除时,Volume只是被卸载,内容被保留。味着glusterfs能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间“切换”。

详细信息,请参阅GlusterFS示例

RBD

RBD允许Rados Block Device格式的磁盘挂载到Pod中,同样的,当pod被删除的时候,rbd也仅仅是被卸载,内容保留,rbd能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间“切换”。

详细信息,请参阅RBD示例

cephfs

cephfs Volume可以将已经存在的CephFS Volume挂载到pod中,与emptyDir特点不同,pod被删除的时,cephfs仅被被卸载,内容保留。cephfs能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间“切换”。

提示:可以使用自己的Ceph服务器运行导出,然后在使用cephfs。

详细信息,请参阅CephFS示例

gitRepo

gitRepo volume将git代码下拉到指定的容器路径中。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: server
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /mypath
      name: git-volume
  volumes:
  - name: git-volume
    gitRepo:
      repository: "git@somewhere:me/my-git-repository.git"
      revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

secret

secret volume用于将敏感信息(如密码)传递给pod。可以将secrets存储在Kubernetes API中,使用的时候以文件的形式挂载到pod中,而不用连接api。 secret volume由tmpfs(RAM支持的文件系统)支持。

详细了解secret

persistentVolumeClaim

persistentVolumeClaim用来挂载持久化磁盘的。PersistentVolumes是用户在不知道特定云环境的细节的情况下,实现持久化存储(如GCE PersistentDisk或iSCSI卷)的一种方式。

更多详细信息,请参阅PersistentVolumes示例

downwardAPI

通过环境变量的方式告诉容器Pod的信息

更多详细信息,请参见downwardAPI卷示例

projected

Projected volume将多个Volume源映射到同一个目录

目前,可以支持以下类型的卷源:

  • secret
  • downwardAPI
  • configMap

所有卷源都要求与pod在同一命名空间中。更详细信息,请参阅all-in-one volume design document

示例

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "cpu_limit"
              resourceFieldRef:
                containerName: container-test
                resource: limits.cpu
      - configMap:
          name: myconfigmap
          items:
            - key: config
              path: my-group/my-config
apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - secret:
          name: mysecret2
          items:
            - key: password
              path: my-group/my-password
              mode: 511

FlexVolume

alpha功能

更多细节在这里

AzureFileVolume

AzureFileVolume用于将Microsoft Azure文件卷(SMB 2.1和3.0)挂载到Pod中。

更多细节在这里

AzureDiskVolume

Azure是微软提供的公有云服务,如果使用Azure上面的虚拟机来作为Kubernetes集群使用时,那么可以通过AzureDisk这种类型的卷插件来挂载Azure提供的数据磁盘。

更多细节在这里

vsphereVolume

需要条件:配置了vSphere Cloud Provider的Kubernetes。有关cloudprovider配置,请参阅vSphere入门指南

vsphereVolume用于将vSphere VMDK Volume挂载到Pod中。卸载卷后,内容将被保留。它同时支持VMFS和VSAN数据存储。

重要提示:使用POD之前,必须使用以下方法创建VMDK。

创建一个VMDK卷

  • 使用vmkfstools创建。先将ssh接入ESX,然后使用以下命令创建vmdk
vmkfstools -c 2G /vmfs/volumes/DatastoreName/volumes/myDisk.vmdk
  • 使用vmware-vdiskmanager创建
shell vmware-vdiskmanager -c -t 0 -s 40GB -a lsilogic myDisk.vmdk

示例

apiVersion: v1
kind: Pod
metadata:
  name: test-vmdk
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-vmdk
      name: test-volume
  volumes:
  - name: test-volume
    # This VMDK volume must already exist.
    vsphereVolume:
      volumePath: "[DatastoreName] volumes/myDisk"
      fsType: ext4

更多例子在这里

Quobyte

在kubernetes中使用Quobyte存储,需要提前部署Quobyte软件,要求必须是1.3以及更高版本,并且在kubernetes管理的节点上面部署Quobyte客户端。

详细信息,请参阅这里

PortworxVolume

Portworx能把你的服务器容量进行蓄积(pool),将你的服务器或者云实例变成一个聚合的高可用的计算和存储节点。

PortworxVolume可以通过Kubernetes动态创建,也可以在Kubernetes pod中预先配置和引用。示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-portworx-volume-pod
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /mnt
      name: pxvol
  volumes:
  - name: pxvol
    # This Portworx volume must already exist.
    portworxVolume:
      volumeID: "pxvol"
      fsType: "<fs-type>"

更多细节和例子可以在这里找到

ScaleIO

ScaleIO是一种基于软件的存储平台(虚拟SAN),可以使用现有硬件来创建可扩展共享块网络存储的集群。ScaleIO卷插件允许部署的pod访问现有的ScaleIO卷(或者可以为持久卷声明动态配置新卷,请参阅 Scaleio Persistent Volumes)。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: pod-0
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: pod-0
    volumeMounts:
    - mountPath: /test-pd
      name: vol-0
  volumes:
  - name: vol-0
    scaleIO:
      gateway: https://localhost:443/api
      system: scaleio
      volumeName: vol-0
      secretRef:
        name: sio-secret
      fsType: xfs

详细信息,请参阅ScaleIO示例

StorageOS

StorageOS是一家英国的初创公司,给无状态容器提供简单的自动块存储、状态来运行数据库和其他需要企业级存储功能,但避免随之而来的复杂性、刚性以及成本。

核心:是StorageOS向容器提供块存储,可通过文件系统访问。

StorageOS容器需要64位Linux,没有额外的依赖关系,提供免费开发许可证。

安装说明,请参阅StorageOS文档

apiVersion: v1
kind: Pod
metadata:
  labels:
    name: redis
    role: master
  name: test-storageos-redis
spec:
  containers:
    - name: master
      image: kubernetes/redis:v1
      env:
        - name: MASTER
          value: "true"
      ports:
        - containerPort: 6379
      volumeMounts:
        - mountPath: /redis-master-data
          name: redis-data
  volumes:
    - name: redis-data
      storageos:
        # The `redis-vol01` volume must already exist within StorageOS in the `default` namespace.
        volumeName: redis-vol01
        fsType: ext4

有关动态配置和持久卷声明的更多信息,请参阅StorageOS示例

Local

目前处于 Kubernetes 1.7中的 alpha 级别。

Local 是Kubernetes集群中每个节点的本地存储(如磁盘,分区或目录),在Kubernetes1.7中kubelet可以支持对kube-reserved和system-reserved指定本地存储资源。

通过上面的这个新特性可以看出来,Local Storage同HostPath的区别在于对Pod的调度上,使用Local Storage可以由Kubernetes自动的对Pod进行调度,而是用HostPath只能人工手动调度Pod,因为Kubernetes已经知道了每个节点上kube-reserved和system-reserved设置的本地存储限制。

示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
  annotations:
        "volume.alpha.kubernetes.io/node-affinity": '{
            "requiredDuringSchedulingIgnoredDuringExecution": {
                "nodeSelectorTerms": [
                    { "matchExpressions": [
                        { "key": "kubernetes.io/hostname",
                          "operator": "In",
                          "values": ["example-node"]
                        }
                    ]}
                 ]}
              }'
spec:
    capacity:
      storage: 100Gi
    accessModes:
    - ReadWriteOnce
    persistentVolumeReclaimPolicy: Delete
    storageClassName: local-storage
    local:
      path: /mnt/disks/ssd1

请注意,本地PersistentVolume需要手动清理和删除。

有关local卷类型的详细信息,请参阅 Local Persistent Storage user guide

Using subPath

有时,可以在一个pod中,将同一个卷共享,使其有多个用处。volumeMounts.subPath特性可以用来指定卷中的一个子目录,而不是直接使用卷的根目录。

以下是使用单个共享卷的LAMP堆栈(Linux Apache Mysql PHP)的pod的示例。HTML内容映射到其html文件夹,数据库将存储在mysql文件夹中:

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

Resources

emptyDir Volume的存储介质(Disk,SSD等)取决于kubelet根目录(如/var/lib/kubelet)所处文件系统的存储介质。不限制emptyDir或hostPath Volume使用的空间大小,不对容器或Pod的资源隔离。

参考:部分内容引用 feisky博客

K8S中文社区微信公众号

Kubernetes Deployment

Deployment为PodReplica Set(升级版的 Replication Controller)提供声明式更新。

你只需要在 Deployment 中描述您想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。您可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。

注意:您不该手动管理由 Deployment 创建的 Replica Set,否则您就篡越了 Deployment controller 的职责!下文罗列了 Deployment 对象中已经覆盖了所有的用例。如果未有覆盖您所有需要的用例,请直接在 Kubernetes 的代码库中提 issue。

典型的用例如下:

  • 使用Deployment来创建ReplicaSet。ReplicaSet在后台创建pod。检查启动状态,看它是成功还是失败。
  • 然后,通过更新Deployment的PodTemplateSpec字段来声明Pod的新状态。这会创建一个新的ReplicaSet,Deployment会按照控制的速率将pod从旧的ReplicaSet移动到新的ReplicaSet中。
  • 如果当前状态不稳定,回滚到之前的Deployment revision。每次回滚都会更新Deployment的revision。
  • 扩容Deployment以满足更高的负载。
  • 暂停Deployment来应用PodTemplateSpec的多个修复,然后恢复上线。
  • 根据Deployment 的状态判断上线是否hang住了。
  • 清除旧的不必要的 ReplicaSet。

创建 Deployment

下面是一个 Deployment 示例,它创建了一个 ReplicaSet 来启动3个 nginx pod。

下载示例文件并执行命令:

$ kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml --record
deployment "nginx-deployment" created

将kubectl的 --record 的 flag 设置为 true可以在 annotation 中记录当前命令创建或者升级了该资源。这在未来会很有用,例如,查看在每个 Deployment revision 中执行了哪些命令。

然后立即执行 get 将获得如下结果:

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

输出结果表明我们希望的repalica数是3(根据deployment中的.spec.replicas配置)当前replica数( .status.replicas)是0, 最新的replica数(.status.updatedReplicas)是0,可用的replica数(.status.availableReplicas)是0。

过几秒后再执行get命令,将获得如下输出:

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

我们可以看到Deployment已经创建了3个 replica,所有的 replica 都已经是最新的了(包含最新的pod template),可用的(根据Deployment中的.spec.minReadySeconds声明,处于已就绪状态的pod的最少个数)。执行kubectl get rs和kubectl get pods会显示Replica Set(RS)和Pod已创建。

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-2035384211   3         3         0       18s

您可能会注意到 ReplicaSet 的名字总是<Deployment的名字>-<pod template的hash值>。

$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-2035384211-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=2035384211
nginx-deployment-2035384211-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=2035384211
nginx-deployment-2035384211-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=2035384211

刚创建的Replica Set将保证总是有3个 nginx 的 pod 存在。

注意: 您必须在 Deployment 中的 selector 指定正确的 pod template label(在该示例中是 app = nginx),不要跟其他的 controller 的 selector 中指定的 pod template label 搞混了(包括 Deployment、Replica Set、Replication Controller 等)。Kubernetes 本身并不会阻止您任意指定 pod template label ,但是如果您真的这么做了,这些 controller 之间会相互打架,并可能导致不正确的行为。

Pod-template-hash label

注意:这个 label 不是用户指定的!

注意上面示例输出中的 pod label 里的 pod-template-hash label。当 Deployment 创建或者接管 ReplicaSet 时,Deployment controller 会自动为 Pod 添加 pod-template-hash label。这样做的目的是防止 Deployment 的子ReplicaSet 的 pod 名字重复。通过将 ReplicaSet 的 PodTemplate 进行哈希散列,使用生成的哈希值作为 label 的值,并添加到 ReplicaSet selector 里、 pod template label 和 ReplicaSet 管理中的 Pod 上。

更新Deployment

注意: Deployment 的 rollout 当且仅当 Deployment 的 pod template(例如.spec.template)中的label更新或者镜像更改时被触发。其他更新,例如扩容Deployment不会触发 rollout。

假如我们现在想要让 nginx pod 使用nginx:1.9.1的镜像来代替原来的nginx:1.7.9的镜像。

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated

我们可以使用edit命令来编辑 Deployment,修改 .spec.template.spec.containers[0].image ,将nginx:1.7.9 改写成 nginx:1.9.1。

$ kubectl edit deployment/nginx-deployment
deployment "nginx-deployment" edited

查看 rollout 的状态,只要执行:

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

Rollout 成功后,get Deployment:

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

UP-TO-DATE 的 replica 的数目已经达到了配置中要求的数目。

CURRENT 的 replica 数表示 Deployment 管理的 replica 数量,AVAILABLE 的 replica 数是当前可用的replica数量。

我们通过执行kubectl get rs可以看到 Deployment 更新了Pod,通过创建一个新的 ReplicaSet 并扩容了3个 replica,同时将原来的 ReplicaSet 缩容到了0个 replica。

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         0       6s
nginx-deployment-2035384211   0         0         0       36s

执行 get pods只会看到当前的新的 pod:

$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1564180365-khku8   1/1       Running   0          14s
nginx-deployment-1564180365-nacti   1/1       Running   0          14s
nginx-deployment-1564180365-z9gth   1/1       Running   0          14s

下次更新这些 pod 的时候,只需要更新 Deployment 中的 pod 的 template 即可。

Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)。

Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。默认的,它会确保最多比期望的Pod数量多一个的 Pod 是 up 的(最多1个 surge )。

在未来的 Kuberentes 版本中,将从1-1变成25%-25%。

例如,如果您自己看下上面的 Deployment,您会发现,开始创建一个新的 Pod,然后删除一些旧的 Pod 再创建一个新的。当新的Pod创建出来之前不会杀掉旧的Pod。这样能够确保可用的 Pod 数量至少有2个,Pod的总数最多4个。

$ kubectl describe deployments
Name:           nginx-deployment
Namespace:      default
CreationTimestamp:  Tue, 15 Mar 2016 12:01:06 -0700
Labels:         app=nginx
Selector:       app=nginx
Replicas:       3 updated | 3 total | 3 available | 0 unavailable
StrategyType:       RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
OldReplicaSets:     <none>
NewReplicaSet:      nginx-deployment-1564180365 (3/3 replicas created)
Events:
  FirstSeen LastSeen    Count   From                     SubobjectPath   Type        Reason              Message
  --------- --------    -----   ----                     -------------   --------    ------              -------
  36s       36s         1       {deployment-controller }                 Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
  23s       23s         1       {deployment-controller }                 Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
  23s       23s         1       {deployment-controller }                 Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
  23s       23s         1       {deployment-controller }                 Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
  21s       21s         1       {deployment-controller }                 Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
  21s       21s         1       {deployment-controller }                 Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3

我们可以看到当我们刚开始创建这个 Deployment 的时候,创建了一个 ReplicaSet(nginx-deployment-2035384211),并直接扩容到了3个 replica。

当我们更新这个 Deployment 的时候,它会创建一个新的 ReplicaSet(nginx-deployment-1564180365),将它扩容到1个replica,然后缩容原先的 ReplicaSet 到2个 replica,此时满足至少2个 Pod 是可用状态,同一时刻最多有4个 Pod 处于创建的状态。

接着继续使用相同的 rolling update 策略扩容新的 ReplicaSet 和缩容旧的 ReplicaSet。最终,将会在新的 ReplicaSet 中有3个可用的 replica,旧的 ReplicaSet 的 replica 数目变成0。

Rollover(多个rollout并行)

每当 Deployment controller 观测到有新的 deployment 被创建时,如果没有已存在的 ReplicaSet 来创建期望个数的 Pod 的话,就会创建出一个新的 ReplicaSet 来做这件事。已存在的 ReplicaSet 控制 label 与.spec.selector匹配但是 template 跟.spec.template不匹配的 Pod 缩容。最终,新的 ReplicaSet 将会扩容出.spec.replicas指定数目的 Pod,旧的 ReplicaSet 会缩容到0。

如果您更新了一个的已存在并正在进行中的 Deployment,每次更新 Deployment都会创建一个新的 ReplicaSet并扩容它,同时回滚之前扩容的 ReplicaSet ——将它添加到旧的 ReplicaSet 列表中,开始缩容。

例如,假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是当还只有3个nginx:1.7.9的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个nginx:1.7.9的 Pod 都创建完成后才开始改变航道。

Label selector 更新

我们通常不鼓励更新 label selector,我们建议实现规划好您的 selector。

任何情况下,只要您想要执行 label selector 的更新,请一定要谨慎并确认您已经预料到所有可能因此导致的后果。

  • 增添 selector 需要同时在 Deployment 的 spec 中更新新的 label,否则将返回校验错误。此更改是不可覆盖的,这意味着新的 selector 不会选择使用旧 selector 创建的 ReplicaSet 和 Pod,从而导致所有旧版本的 ReplicaSet 都被丢弃,并创建新的 ReplicaSet。
  • 更新 selector,即更改 selector key 的当前值,将导致跟增添 selector 同样的后果。
  • 删除 selector,即删除 Deployment selector 中的已有的 key,不需要对 Pod template label 做任何更改,现有的 ReplicaSet 也不会成为孤儿,但是请注意,删除的 label 仍然存在于现有的 Pod 和 ReplicaSet 中。

回退Deployment

有时候您可能想回退一个 Deployment,例如,当 Deployment 不稳定时,比如一直 crash looping。

默认情况下,kubernetes 会在系统中保存前两次的 Deployment 的 rollout 历史记录,以便您可以随时回退(您可以修改revision history limit来更改保存的revision数)。

注意: 只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。

其他的更新,比如扩容 Deployment 不会创建 revision——因此我们可以很方便的手动或者自动扩容。这意味着当您回退到历史 revision 是,直有 Deployment 中的 Pod template 部分才会回退。

假设我们在更新 Deployment 的时候犯了一个拼写错误,将镜像的名字写成了nginx:1.91,而正确的名字应该是nginx:1.9.1:

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.91
deployment "nginx-deployment" image updated

Rollout 将会卡住。

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

按住 Ctrl-C 停止上面的 rollout 状态监控。

您会看到旧的 replica(nginx-deployment-1564180365 和 nginx-deployment-2035384211)和新的 replica (nginx-deployment-3066724191)数目都是2个。

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   2         2         0       25s
nginx-deployment-2035384211   0         0         0       36s
nginx-deployment-3066724191   2         2         2       6s

看下创建 Pod,您会看到有两个新的 ReplicaSet 创建的 Pod 处于 ImagePullBackOff 状态,循环拉取镜像。

$ kubectl get pods
NAME                                READY     STATUS             RESTARTS   AGE
nginx-deployment-1564180365-70iae   1/1       Running            0          25s
nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s
nginx-deployment-3066724191-eocby   0/1       ImagePullBackOff   0          6s

注意,Deployment controller会自动停止坏的 rollout,并停止扩容新的 ReplicaSet。

$ kubectl describe deployment
Name:           nginx-deployment
Namespace:      default
CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
Labels:         app=nginx
Selector:       app=nginx
Replicas:       2 updated | 3 total | 2 available | 2 unavailable
StrategyType:       RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
OldReplicaSets:     nginx-deployment-1564180365 (2/2 replicas created)
NewReplicaSet:      nginx-deployment-3066724191 (2/2 replicas created)
Events:
  FirstSeen LastSeen    Count   From                    SubobjectPath   Type        Reason              Message
  --------- --------    -----   ----                    -------------   --------    ------              -------
  1m        1m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
  22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
  22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
  22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
  21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
  21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3
  13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
  13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-1564180365 to 2
  13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 2

为了修复这个问题,我们需要回退到稳定的 Deployment revision。

检查 Deployment 升级的历史记录

首先,检查下 Deployment 的 revision:

$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment":
REVISION    CHANGE-CAUSE
1           kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml--record
2           kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3           kubectl set image deployment/nginx-deployment nginx=nginx:1.91

因为我们创建 Deployment 的时候使用了--recored参数可以记录命令,我们可以很方便的查看每次 revision 的变化。

查看单个revision 的详细信息:

$ kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" revision 2
  Labels:       app=nginx
          pod-template-hash=1159050644
  Annotations:  kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
  Containers:
   nginx:
    Image:      nginx:1.9.1
    Port:       80/TCP
     QoS Tier:
        cpu:      BestEffort
        memory:   BestEffort
    Environment Variables:      <none>
  No volumes.

回退到历史版本

现在,我们可以决定回退当前的 rollout 到之前的版本:

$ kubectl rollout undo deployment/nginx-deployment
deployment "nginx-deployment" rolled back

也可以使用 --revision参数指定某个历史版本:

$ kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment "nginx-deployment" rolled back

与 rollout 相关的命令详细文档见kubectl rollout

该 Deployment 现在已经回退到了先前的稳定版本。如您所见,Deployment controller产生了一个回退到revison 2的DeploymentRollback的 event。

$ kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           30m

$ kubectl describe deployment
Name:           nginx-deployment
Namespace:      default
CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
Labels:         app=nginx
Selector:       app=nginx
Replicas:       3 updated | 3 total | 3 available | 0 unavailable
StrategyType:       RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
OldReplicaSets:     <none>
NewReplicaSet:      nginx-deployment-1564180365 (3/3 replicas created)
Events:
  FirstSeen LastSeen    Count   From                    SubobjectPath   Type        Reason              Message
  --------- --------    -----   ----                    -------------   --------    ------              -------
  30m       30m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
  29m       29m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
  29m       29m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
  29m       29m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
  29m       29m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
  29m       29m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 2
  29m       29m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
  29m       29m         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-1564180365 to 2
  2m        2m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-3066724191 to 0
  2m        2m          1       {deployment-controller }                Normal      DeploymentRollback  Rolled back deployment "nginx-deployment" to revision 2
  29m       2m          2       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3

清理 Policy

您可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。

Deployment 扩容

您可以使用以下命令扩容 Deployment:

$ kubectl scale deployment nginx-deployment --replicas 10
deployment "nginx-deployment" scaled

假设您的集群中启用了horizontal pod autoscaling,您可以给 Deployment 设置一个 autoscaler,基于当前 Pod的 CPU 利用率选择最少和最多的 Pod 数。

$ kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80
deployment "nginx-deployment" autoscaled

比例扩容

RollingUpdate Deployment 支持同时运行一个应用的多个版本。或者 autoscaler 扩 容 RollingUpdate Deployment 的时候,正在中途的 rollout(进行中或者已经暂停的),为了降低风险,Deployment controller 将会平衡已存在的活动中的 ReplicaSet(有 Pod 的 ReplicaSet)和新加入的 replica。这被称为比例扩容。

例如,您正在运行中含有10个 replica 的 Deployment。maxSurge=3,maxUnavailable=2。

$ kubectl get deploy
NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     10        10        10           10          50s

您更新了一个镜像,而在集群内部无法解析。

$ kubectl set image deploy/nginx-deployment nginx=nginx:sometag
deployment "nginx-deployment" image updated

镜像更新启动了一个包含ReplicaSet nginx-deployment-1989198191的新的rollout,但是它被阻塞了,因为我们上面提到的maxUnavailable。

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   5         5         0         9s
nginx-deployment-618515232    8         8         8         1m

然后发起了一个新的Deployment扩容请求。autoscaler将Deployment的repllica数目增加到了15个。Deployment controller需要判断在哪里增加这5个新的replica。如果我们没有谁用比例扩容,所有的5个replica都会加到一个新的ReplicaSet中。如果使用比例扩容,新添加的replica将传播到所有的ReplicaSet中。大的部分加入replica数最多的ReplicaSet中,小的部分加入到replica数少的ReplciaSet中。0个replica的ReplicaSet不会被扩容。

在我们上面的例子中,3个replica将添加到旧的ReplicaSet中,2个replica将添加到新的ReplicaSet中。rollout进程最终会将所有的replica移动到新的ReplicaSet中,假设新的replica成为健康状态。

$ kubectl get deploy
NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     15        18        7            8           7m
$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   7         7         0         7m
nginx-deployment-618515232    11        11        11        7m

暂停和恢复Deployment

您可以在发出一次或多次更新前暂停一个 Deployment,然后再恢复它。这样您就能多次暂停和恢复 Deployment,在此期间进行一些修复工作,而不会发出不必要的 rollout。

例如使用刚刚创建 Deployment:

$ kubectl get deploy
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     3         3         3            3           1m
[mkargaki@dhcp129-211 kubernetes]$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   3         3         3         1m

使用以下命令暂停 Deployment:

$ kubectl rollout pause deployment/nginx-deployment
deployment "nginx-deployment" paused

然后更新 Deplyment中的镜像:

$ kubectl set image deploy/nginx nginx=nginx:1.9.1
deployment "nginx-deployment" image updated

注意新的 rollout 启动了:

$ kubectl rollout history deploy/nginx
deployments "nginx"
REVISION  CHANGE-CAUSE
1   <none>

$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   3         3         3         2m

您可以进行任意多次更新,例如更新使用的资源:

$ kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
deployment "nginx" resource requirements updated

Deployment 暂停前的初始状态将继续它的功能,而不会对 Deployment 的更新产生任何影响,只要 Deployment是暂停的。

最后,恢复这个 Deployment,观察完成更新的 ReplicaSet 已经创建出来了:

$ kubectl rollout resume deploy nginx
deployment "nginx" resumed
$ KUBECTL get rs -w
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   2         2         2         2m
nginx-3926361531   2         2         0         6s
nginx-3926361531   2         2         1         18s
nginx-2142116321   1         2         2         2m
nginx-2142116321   1         2         2         2m
nginx-3926361531   3         2         1         18s
nginx-3926361531   3         2         1         18s
nginx-2142116321   1         1         1         2m
nginx-3926361531   3         3         1         18s
nginx-3926361531   3         3         2         19s
nginx-2142116321   0         1         1         2m
nginx-2142116321   0         1         1         2m
nginx-2142116321   0         0         0         2m
nginx-3926361531   3         3         3         20s
^C
$ KUBECTL get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-2142116321   0         0         0         2m
nginx-3926361531   3         3         3         28s

注意: 在恢复 Deployment 之前您无法回退一个已经暂停的 Deployment。

Deployment 状态

Deployment 在生命周期中有多种状态。在创建一个新的 ReplicaSet 的时候它可以是 progressing 状态, complete 状态,或者 fail to progress 状态。

进行中的 Deployment

Kubernetes 将执行过下列任务之一的 Deployment 标记为 progressing 状态:

  • Deployment 正在创建新的ReplicaSet过程中。
  • Deployment 正在扩容一个已有的 ReplicaSet。
  • Deployment 正在缩容一个已有的 ReplicaSet。
  • 有新的可用的 pod 出现。

您可以使用kubectl rollout status命令监控 Deployment 的进度。

完成的 Deployment

Kubernetes 将包括以下特性的 Deployment 标记为 complete 状态:

  • Deployment 最小可用。最小可用意味着 Deployment 的可用 replica 个数等于或者超过 Deployment 策略中的期望个数。
  • 所有与该 Deployment 相关的replica都被更新到了您指定版本,也就说更新完成。
  • 该 Deployment 中没有旧的 Pod 存在。

您可以用kubectl rollout status命令查看 Deployment 是否完成。如果 rollout 成功完成,kubectl rollout status将返回一个0值的 Exit Code。

$ kubectl rollout status deploy/nginx
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx" successfully rolled out
$ echo $?
0

失败的 Deployment

您的 Deployment 在尝试部署新的 ReplicaSet 的时候可能卡住,用于也不会完成。这可能是因为以下几个因素引起的:

  • 无效的引用
  • 不可读的 probe failure
  • 镜像拉取错误
  • 权限不够
  • 范围限制
  • 程序运行时配置错误

探测这种情况的一种方式是,在您的 Deployment spec 中指定spec.progressDeadlineSeconds。spec.progressDeadlineSeconds 表示 Deployment controller 等待多少秒才能确定(通过 Deployment status)Deployment进程是卡住的。

下面的kubectl命令设置progressDeadlineSeconds 使 controller 在 Deployment 在进度卡住10分钟后报告:

$ kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'
"nginx-deployment" patched

当超过截止时间后,Deployment controller 会在 Deployment 的 status.conditions中增加一条DeploymentCondition,它包括如下属性:

  • Type=Progressing
  • Status=False
  • Reason=ProgressDeadlineExceeded

浏览 Kubernetes API conventions 查看关于status conditions的更多信息。

注意: kubernetes除了报告Reason=ProgressDeadlineExceeded状态信息外不会对卡住的 Deployment 做任何操作。更高层次的协调器可以利用它并采取相应行动,例如,回滚 Deployment 到之前的版本。

注意: 如果您暂停了一个 Deployment,在暂停的这段时间内kubernetnes不会检查您指定的 deadline。您可以在 Deployment 的 rollout 途中安全的暂停它,然后再恢复它,这不会触发超过deadline的状态。

您可能在使用 Deployment 的时候遇到一些短暂的错误,这些可能是由于您设置了太短的 timeout,也有可能是因为各种其他错误导致的短暂错误。例如,假设您使用了无效的引用。当您 Describe Deployment 的时候可能会注意到如下信息:

$ kubectl describe deployment nginx-deployment
<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

执行 kubectl get deployment nginx-deployment -o yaml,Deployement 的状态可能看起来像这个样子:

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

最终,一旦超过 Deployment 进程的 deadline,kuberentes 会更新状态和导致 Progressing 状态的原因:

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

您可以通过缩容 Deployment的方式解决配额不足的问题,或者增加您的 namespace 的配额。如果您满足了配额条件后,Deployment controller 就会完成您的 Deployment rollout,您将看到 Deployment 的状态更新为成功状态(Status=True并且Reason=NewReplicaSetAvailable)。

Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable

Type=Available、 Status=True 以为这您的Deployment有最小可用性。 最小可用性是在Deployment策略中指定的参数。Type=Progressing 、 Status=True意味着您的Deployment 或者在部署过程中,或者已经成功部署,达到了期望的最少的可用replica数量(查看特定状态的Reason——在我们的例子中Reason=NewReplicaSetAvailable 意味着Deployment已经完成)。

您可以使用kubectl rollout status命令查看Deployment进程是否失败。当Deployment过程超过了deadline,kubectl rollout status将返回非0的exit code。

$ kubectl rollout status deploy/nginx
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline
$ echo $?
1

操作失败的 Deployment

所有对完成的 Deployment 的操作都适用于失败的 Deployment。您可以对它扩/缩容,回退到历史版本,您甚至可以多次暂停它来应用 Deployment pod template。

清理Policy

您可以设置 Deployment 中的 .spec.revisionHistoryLimit 项来指定保留多少旧的 ReplicaSet。 余下的将在后台被当作垃圾收集。默认的,所有的 revision 历史就都会被保留。在未来的版本中,将会更改为2。

注意: 将该值设置为0,将导致所有的 Deployment 历史记录都会被清除,该 Deployment 就无法再回退了。

用例

金丝雀 Deployment

如果您想要使用 Deployment 对部分用户或服务器发布 release,您可以创建多个 Deployment,每个 Deployment 对应一个 release,参照 managing resources 中对金丝雀模式的描述。

编写 Deployment Spec

在所有的 Kubernetes 配置中,Deployment 也需要apiVersion,kind和metadata这些配置项。配置文件的通用使用说明查看 部署应用,配置容器,和 使用 kubectl 管理资源 文档。

Deployment也需要 .spec section.

Pod Template

.spec.template 是 .spec中唯一要求的字段。

.spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段。

另外为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他controller重复了,参考selector)和适当的重启策略。

.spec.template.spec.restartPolicy 可以设置为 Always , 如果不指定的话这就是默认配置。

Replicas

.spec.replicas 是可以选字段,指定期望的pod数量,默认是1。

Selector

.spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。

如果被指定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝。如果 .spec.selector 没有被指定, .spec.selector.matchLabels 默认是 .spec.template.metadata.labels。

在Pod的template跟.spec.template不同或者数量超过了.spec.replicas规定的数量的情况下,Deployment会杀掉label跟selector不同的Pod。

注意: 您不应该再创建其他label跟这个selector匹配的pod,或者通过其他Deployment,或者通过其他Controller,例如ReplicaSet和ReplicationController。否则该Deployment会被把它们当成都是自己创建的。Kubernetes不会阻止您这么做。

如果您有多个controller使用了重复的selector,controller们就会互相打架并导致不正确的行为。

策略

.spec.strategy 指定新的Pod替换旧的Pod的策略。 .spec.strategy.type 可以是"Recreate"或者是 "RollingUpdate"。"RollingUpdate"是默认值。

Recreate Deployment

.spec.strategy.type==Recreate时,在创建出新的Pod之前会先杀掉所有已存在的Pod。

Rolling Update Deployment

.spec.strategy.type==RollingUpdate时,Deployment使用rolling update 的方式更新Pod 。您可以指定maxUnavailable 和 maxSurge 来控制 rolling update 进程。

Max Unavailable

.spec.strategy.rollingUpdate.maxUnavailable 是可选配置项,用来指定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百分比(例如10%)。通过计算百分比的绝对值向下取整。如果.spec.strategy.rollingUpdate.maxSurge 为0时,这个值不可以为0。默认值是1。

例如,该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻可以用的Pod数量至少是期望Pod数量的70%。

Max Surge

.spec.strategy.rollingUpdate.maxSurge 是可选配置项,用来指定可以超过期望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如10%)。当MaxUnavailable为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。

例如,该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。

Progress Deadline Seconds

.spec.progressDeadlineSeconds 是可选配置项,用来指定在系统报告Deployment的failed progressing——表现为resource的状态中type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到这种状态时就会自动回滚。

如果设置该参数,该值必须大于 .spec.minReadySeconds。

Min Ready Seconds

.spec.minReadySeconds是一个可选配置项,用来指定没有任何容器crash的Pod并被认为是可用状态的最小秒数。默认是0(Pod在ready后就会被认为是可用状态)。进一步了解什么什么后Pod会被认为是ready状态,参阅 Container Probes

Rollback To

.spec.rollbackTo 是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。

Revision

.spec.rollbackTo.revision是一个可选配置项,用来指定回退到的revision。默认是0,意味着回退到历史中最老的revision。

Revision History Limit

Deployment revision history存储在它控制的ReplicaSets中。

.spec.revisionHistoryLimit 是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset或会被保留,将资源存储在etcd中,是用kubectl get rs查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦您删除的旧的RepelicaSet,您的Deployment就无法再回退到那个revison了。

如果您将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。

Paused

.spec.paused是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout。Deployment被创建之后默认是非paused。

Deployment 的替代选择

kubectl rolling update

Kubectl rolling update 虽然使用类似的方式更新Pod和ReplicationController。但是我们推荐使用Deployment,因为它是声明式的,客户端侧,具有附加特性,例如即使滚动升级结束后也可以回滚到任何历史版本。

说明:本文由宋净超翻译,出至其个人博客 。

K8S中文社区微信公众号

Kubernetes Replica Sets

ReplicaSet(RS)是Replication Controller(RC)的升级版本。ReplicaSet 和  Replication Controller之间的唯一区别是对选择器的支持。ReplicaSet支持labels user guide中描述的set-based选择器要求, 而Replication Controller仅支持equality-based的选择器要求。

如何使用ReplicaSet

大多数kubectl 支持Replication Controller 命令的也支持ReplicaSets。rolling-update命令除外,如果要使用rolling-update,请使用Deployments来实现。

虽然ReplicaSets可以独立使用,但它主要被 Deployments用作pod 机制的创建、删除和更新。当使用Deployment时,你不必担心创建pod的ReplicaSets,因为可以通过Deployment实现管理ReplicaSets。

何时使用ReplicaSet

ReplicaSet能确保运行指定数量的pod。然而,Deployment 是一个更高层次的概念,它能管理ReplicaSets,并提供对pod的更新等功能。因此,我们建议你使用Deployment来管理ReplicaSets,除非你需要自定义更新编排。

这意味着你可能永远不需要操作ReplicaSet对象,而是使用Deployment替代管理 。

示例

frontend.yaml

apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: frontend
  # these labels can be applied automatically
  # from the labels in the pod template if not set
  # labels:
    # app: guestbook
    # tier: frontend
spec:
  # this replicas value is default
  # modify it according to your case
  replicas: 3
  # selector can be applied automatically
  # from the labels in the pod template if not set,
  # but we are specifying the selector here to
  # demonstrate its usage.
  selector:
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier, operator: In, values: [frontend]}
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
          # If your cluster config does not include a dns service, then to
          # instead access environment variables to find service host
          # info, comment out the 'value: dns' line above, and uncomment the
          # line below.
          # value: env
        ports:
        - containerPort: 80

将此配置保存到(frontend.yaml)并提交到Kubernetes集群时,将创建定义的ReplicaSet及其管理的pod。

$ kubectl create -f frontend.yaml
replicaset "frontend" created
$ kubectl describe rs/frontend
Name:          frontend
Namespace:     default
Image(s):      gcr.io/google_samples/gb-frontend:v3
Selector:      tier=frontend,tier in (frontend)
Labels:        app=guestbook,tier=frontend
Replicas:      3 current / 3 desired
Pods Status:   3 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
  FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----                -------------    --------    ------            -------
  1m           1m          1        {replicaset-controller }             Normal      SuccessfulCreate  Created pod: frontend-qhloh
  1m           1m          1        {replicaset-controller }             Normal      SuccessfulCreate  Created pod: frontend-dnjpy
  1m           1m          1        {replicaset-controller }             Normal      SuccessfulCreate  Created pod: frontend-9si5l
$ kubectl get pods
NAME             READY     STATUS    RESTARTS   AGE
frontend-9si5l   1/1       Running   0          1m
frontend-dnjpy   1/1       Running   0          1m
frontend-qhloh   1/1       Running   0          1m

ReplicaSet as an Horizontal Pod Autoscaler target

ReplicaSet也可以作为 Horizontal Pod Autoscalers (HPA)的目标 。也就是说,一个ReplicaSet可以由一个HPA来自动伸缩。以下是针对我们在上一个示例中创建的ReplicaSet的HPA示例。

hpa-rs.yaml 
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50
kubectl create -f hpa-rs.yaml

K8S中文社区微信公众号

Kubernetes Pod概述

本文主要介绍Pod,了Kubernetes对象模型中可部署的最小对象。

了解Pod

Pod是Kubernetes创建或部署的最小/最简单的基本单位,一个Pod代表集群上正在运行的一个进程。

一个Pod封装一个应用容器(也可以有多个容器),存储资源、一个独立的网络IP以及管理控制容器运行方式的策略选项。Pod代表部署的一个单位:Kubernetes中单个应用的实例,它可能由单个容器或多个容器共享组成的资源。

Docker是Kubernetes Pod中最常见的runtime ,Pods也支持其他容器runtimes。

Kubernetes中的Pod使用可分两种主要方式:

  • Pod中运行一个容器。“one-container-per-Pod”模式是Kubernetes最常见的用法; 在这种情况下,你可以将Pod视为单个封装的容器,但是Kubernetes是直接管理Pod而不是容器。
  • Pods中运行多个需要一起工作的容器。Pod可以封装紧密耦合的应用,它们需要由多个容器组成,它们之间能够共享资源,这些容器可以形成一个单一的内部service单位 - 一个容器共享文件,另一个“sidecar”容器来更新这些文件。Pod将这些容器的存储资源作为一个实体来管理。

关于Pod用法其他信息请参考:

每个Pod都是运行应用的单个实例,如果需要水平扩展应用(例如,运行多个实例),则应该使用多个Pods,每个实例一个Pod。在Kubernetes中,这样通常称为Replication。Replication的Pod通常由Controller创建和管理。更多信息,请参考Pods和控制器

Pods如何管理多个容器

Pods的设计可用于支持多进程的协同工作(作为容器),形成一个cohesive的Service单位。Pod中的容器在集群中Node上被自动分配,容器之间可以共享资源、网络和相互依赖关系,并同时被调度使用。

请注意,在单个Pod中共同管理多个容器是一个相对高级的用法,应该只有在容器紧密耦合的特殊实例中使用此模式。例如,有一个容器被用作WEB服务器,用于共享volume,以及一个单独“sidecar”容器需要从远程获取资源来更新这些文件,如下图所示:

pod diagram

Pods提供两种共享资源:网络和存储。

网络

每个Pod被分配一个独立的IP地址,Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用localhost相互通信。当Pod中的容器与Pod 外部通信时,他们必须协调如何使用共享网络资源(如端口)。

存储

Pod可以指定一组共享存储volumes。Pod中的所有容器都可以访问共享volumes,允许这些容器共享数据。volumes 还用于Pod中的数据持久化,以防其中一个容器需要重新启动而丢失数据。有关Kubernetes如何在Pod中实现共享存储的更多信息,请参考Volumes。

使用Pod

你很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kuberentes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。

注意:重启Pod中的容器跟重启Pod不是一回事。Pod只提供容器的运行环境并保持容器的运行状态,重启容器不会造成Pod重启。

Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod的。

Pod和Controller

Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。

包含一个或者多个Pod的Controller示例:

通常,Controller会用你提供的Pod Template来创建相应的Pod。

Pod模板

Pod模板是包含了其他对象(如Replication ControllersJobs和 DaemonSets)中的pod定义 。Controllers控制器使用Pod模板来创建实际需要的pod。

pod模板类似cookie cutters。“一旦饼干被切掉,饼干和刀将没有关系”。随后对模板的后续更改甚至切换到新模板对已创建的pod并没有任何的影响。

参考:宋净超博客

K8S中文社区微信公众号