本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

使用 Kubernetes Pet Sets 和 FlexVolumes 以及 Datera Elastic Data Fabric 扩展有状态应用程序

引言

随着客户从无状态工作负载转向运行有状态应用程序,Kubernetes 中的持久卷是基础。虽然 Kubernetes 已经支持 MySQL、Kafka、Cassandra 和 Couchbase 等有状态应用程序一段时间,但 Pet Sets 的引入显著改善了这种支持。特别是,通过 Pet Sets 实现的置备和启动的排序过程、扩展和持久关联的能力,使得自动化扩展“宠物”(需要一致处理和持久放置的应用程序)成为可能。

Datera 是一种用于云部署的弹性块存储,通过 FlexVolume 框架与 Kubernetes 无缝集成。基于容器的第一原则,Datera 允许应用程序资源置备与底层物理基础设施解耦。这带来了清晰的契约(即,不依赖或直接了解底层物理基础设施)、声明性格式,并最终为有状态应用程序带来了可移植性。

虽然 Kubernetes 允许通过 yaml 配置灵活地定义底层应用程序基础设施,但 Datera 允许将该配置传递给存储基础设施以提供持久性。通过 Datera AppTemplates 的概念,在 Kubernetes 环境中,有状态应用程序可以实现自动化扩展。

部署持久存储

持久存储使用 Kubernetes PersistentVolume 子系统进行定义。PersistentVolumes 是卷插件,定义了独立于使用它的 Pod 生命周期存在的卷。它们以 NFS、iSCSI 或云提供商特定的存储系统实现。Datera 开发了一个用于 PersistentVolumes 的卷插件,可以在 Datera Data Fabric 上为 Kubernetes Pod 置备 iSCSI 块存储。

Datera 卷插件由 minion 节点上的 kubelet 调用,并通过其 REST API 将调用转发到 Datera Data Fabric。下面是一个使用 Datera 插件部署 PersistentVolume 的示例

 apiVersion: v1

 kind: PersistentVolume

 metadata:

   name: pv-datera-0

 spec:

   capacity:

     storage: 100Gi

   accessModes:

     - ReadWriteOnce

   persistentVolumeReclaimPolicy: Retain

   flexVolume:

     driver: "datera/iscsi"

     fsType: "xfs"

     options:

       volumeID: "kube-pv-datera-0"

       size: “100"

       replica: "3"

       backstoreServer: "[tlx170.tlx.daterainc.com](http://tlx170.tlx.daterainc.com/):7717”

此清单定义了一个 100 GB 的 PersistentVolume,用于在 Datera Data Fabric 中进行置备,以防 Pod 请求持久存储。

[root@tlx241 /]# kubectl get pv

NAME          CAPACITY   ACCESSMODES   STATUS      CLAIM     REASON    AGE

pv-datera-0   100Gi        RWO         Available                       8s

pv-datera-1   100Gi        RWO         Available                       2s

pv-datera-2   100Gi        RWO         Available                       7s

pv-datera-3   100Gi        RWO         Available                       4s

配置

Datera PersistenceVolume 插件安装在所有 minion 节点上。当一个 Pod 落在 minion 节点上,并有一个绑定到先前置备的持久存储的有效声明时,Datera 插件会将请求转发到 Datera Data Fabric 上创建卷。PersistentVolume 清单中指定的所有选项都会在置备请求时发送到插件。

一旦在 Datera Data Fabric 中置备了卷,卷就会作为 iSCSI 块设备呈现给 minion 节点,并且 kubelet 会挂载此设备以供容器(在 Pod 中)访问。

使用持久存储

Kubernetes PersistentVolumes 与使用 PersistentVolume Claims 的 Pod 一起使用。一旦定义了声明,它就会绑定到符合声明规范的 PersistentVolume。上面定义的 PersistentVolume 的典型声明如下所示:

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

 name: pv-claim-test-petset-0

spec:

 accessModes:

   - ReadWriteOnce

 resources:

   requests:

     storage: 100Gi

当定义此声明并将其绑定到 PersistentVolume 后,资源可以在 Pod 规范中使用

[root@tlx241 /]# kubectl get pv

NAME          CAPACITY   ACCESSMODES   STATUS      CLAIM                            REASON    AGE

pv-datera-0   100Gi      RWO           Bound       default/pv-claim-test-petset-0             6m

pv-datera-1   100Gi      RWO           Bound       default/pv-claim-test-petset-1             6m

pv-datera-2   100Gi      RWO           Available                                              7s

pv-datera-3   100Gi      RWO           Available                                              4s


[root@tlx241 /]# kubectl get pvc

NAME                     STATUS    VOLUME        CAPACITY   ACCESSMODES   AGE

pv-claim-test-petset-0   Bound     pv-datera-0   0                        3m

pv-claim-test-petset-1   Bound     pv-datera-1   0                        3m

Pod 可以像下面这样使用 PersistentVolume Claim:

apiVersion: v1

kind: Pod

metadata:

 name: kube-pv-demo

spec:

 containers:

 - name: data-pv-demo

   image: nginx

   volumeMounts:

   - name: test-kube-pv1

     mountPath: /data

   ports:

   - containerPort: 80

 volumes:

 - name: test-kube-pv1

   persistentVolumeClaim:

     claimName: pv-claim-test-petset-0

结果是一个 Pod 使用 PersistentVolume Claim 作为卷。它反过来将请求发送到 Datera 卷插件,以在 Datera Data Fabric 中置备存储。

[root@tlx241 /]# kubectl describe pods kube-pv-demo

Name:       kube-pv-demo

Namespace:  default

Node:       tlx243/172.19.1.243

Start Time: Sun, 14 Aug 2016 19:17:31 -0700

Labels:     \<none\>

Status:     Running

IP:         10.40.0.3

Controllers: \<none\>

Containers:

 data-pv-demo:

   Container ID: [docker://ae2a50c25e03143d0dd721cafdcc6543fac85a301531110e938a8e0433f74447](about:blank)

   Image:   nginx

   Image ID: [docker://sha256:0d409d33b27e47423b049f7f863faa08655a8c901749c2b25b93ca67d01a470d](about:blank)

   Port:    80/TCP

   State:   Running

     Started:  Sun, 14 Aug 2016 19:17:34 -0700

   Ready:   True

   Restart Count:  0

   Environment Variables:  \<none\>

Conditions:

 Type           Status

 Initialized    True

 Ready          True

 PodScheduled   True

Volumes:

 test-kube-pv1:

   Type:  PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)

   ClaimName:   pv-claim-test-petset-0

   ReadOnly:    false

 default-token-q3eva:

   Type:        Secret (a volume populated by a Secret)

   SecretName:  default-token-q3eva

   QoS Tier:  BestEffort

Events:

 FirstSeen LastSeen Count From SubobjectPath Type Reason Message

 --------- -------- ----- ---- ------------- -------- ------ -------

 43s 43s 1 {default-scheduler } Normal Scheduled Successfully assigned kube-pv-demo to tlx243

 42s 42s 1 {kubelet tlx243} spec.containers{data-pv-demo} Normal Pulling pulling image "nginx"

 40s 40s 1 {kubelet tlx243} spec.containers{data-pv-demo} Normal Pulled Successfully pulled image "nginx"

 40s 40s 1 {kubelet tlx243} spec.containers{data-pv-demo} Normal Created Created container with docker id ae2a50c25e03

 40s 40s 1 {kubelet tlx243} spec.containers{data-pv-demo} Normal Started Started container with docker id ae2a50c25e03

持久卷在 minion 节点(本例中为 tlx243)上显示为 iSCSI 设备

[root@tlx243 ~]# lsscsi

[0:2:0:0]    disk    SMC      SMC2208          3.24  /dev/sda

[11:0:0:0]   disk    DATERA   IBLOCK           4.0   /dev/sdb


[root@tlx243 datera~iscsi]# mount  ``` grep sdb

/dev/sdb on /var/lib/kubelet/pods/6b99bd2a-628e-11e6-8463-0cc47ab41442/volumes/datera~iscsi/pv-datera-0 type xfs (rw,relatime,attr2,inode64,noquota)

Pod 中运行的容器将此设备挂载到清单中指定的 /data

[root@tlx241 /]# kubectl exec kube-pv-demo -c data-pv-demo -it bash

root@kube-pv-demo:/# mount  ``` grep data

/dev/sdb on /data type xfs (rw,relatime,attr2,inode64,noquota)

使用 Pet Sets

通常,Pod 被视为无状态单元,因此如果其中一个不健康或被取代,Kubernetes 会直接将其处置。相比之下,PetSet 是一组有状态的 Pod,它具有更强的身份概念。PetSet 的目标是通过为应用程序的单个实例分配不依赖于底层物理基础设施的身份来解耦这种依赖关系。

PetSet 需要 {0..n-1} 个 Pet。每个 Pet 都有一个确定性名称,PetSetName-Ordinal,和一个唯一的身份。每个 Pet 最多有一个 Pod,每个 PetSet 最多有一个具有给定身份的 Pet。PetSet 确保在任何给定时间都有指定数量的具有唯一身份的“宠物”在运行。Pet 的身份包括:

  • 稳定的主机名,可在 DNS 中访问
  • 序数索引
  • 稳定存储:链接到序数和主机名

使用 PersistentVolume Claim 的典型 PetSet 定义如下所示:

# A headless service to create DNS records

apiVersion: v1

kind: Service

metadata:

 name: test-service

 labels:

   app: nginx

spec:

 ports:

 - port: 80

   name: web

 clusterIP: None

 selector:

   app: nginx

---

apiVersion: apps/v1alpha1

kind: PetSet

metadata:

 name: test-petset

spec:

 serviceName: "test-service"

 replicas: 2

 template:

   metadata:

     labels:

       app: nginx

     annotations:

       [pod.alpha.kubernetes.io/initialized:](http://pod.alpha.kubernetes.io/initialized:) "true"

   spec:

     terminationGracePeriodSeconds: 0

     containers:

     - name: nginx

       image: [gcr.io/google\_containers/nginx-slim:0.8](http://gcr.io/google_containers/nginx-slim:0.8)

       ports:

       - containerPort: 80

         name: web

       volumeMounts:

       - name: pv-claim

         mountPath: /data

 volumeClaimTemplates:

 - metadata:

     name: pv-claim

     annotations:

       [volume.alpha.kubernetes.io/storage-class:](http://volume.alpha.kubernetes.io/storage-class:) anything

   spec:

     accessModes: ["ReadWriteOnce"]

     resources:

       requests:

         storage: 100Gi

我们有以下可用的 PersistentVolume Claims:

[root@tlx241 /]# kubectl get pvc

NAME                     STATUS    VOLUME        CAPACITY   ACCESSMODES   AGE

pv-claim-test-petset-0   Bound     pv-datera-0   0                        41m

pv-claim-test-petset-1   Bound     pv-datera-1   0                        41m

pv-claim-test-petset-2   Bound     pv-datera-2   0                        5s

pv-claim-test-petset-3   Bound     pv-datera-3   0                        2s

当置备此 PetSet 时,将实例化两个 Pod:

[root@tlx241 /]# kubectl get pods

NAMESPACE     NAME                        READY     STATUS    RESTARTS   AGE

default       test-petset-0               1/1       Running   0          7s

default       test-petset-1               1/1       Running   0          3s

下面是前面实例化的 PetSet test-petset 的样子:

[root@tlx241 /]# kubectl describe petset test-petset

Name: test-petset

Namespace: default

Image(s): [gcr.io/google\_containers/nginx-slim:0.8](http://gcr.io/google_containers/nginx-slim:0.8)

Selector: app=nginx

Labels: app=nginx

Replicas: 2 current / 2 desired

Annotations: \<none\>

CreationTimestamp: Sun, 14 Aug 2016 19:46:30 -0700

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

No volumes.

No events.

一旦实例化了一个 PetSet(如下面的 test-petset),当增加副本数量(即使用该 PetSet 启动的 Pod 数量)时,会实例化更多的 Pod,并且更多的 PersistentVolume Claims 会绑定到新的 Pod:

[root@tlx241 /]# kubectl patch petset test-petset -p'{"spec":{"replicas":"3"}}'

"test-petset” patched


[root@tlx241 /]# kubectl describe petset test-petset

Name: test-petset

Namespace: default

Image(s): [gcr.io/google\_containers/nginx-slim:0.8](http://gcr.io/google_containers/nginx-slim:0.8)

Selector: app=nginx

Labels: app=nginx

Replicas: 3 current / 3 desired

Annotations: \<none\>

CreationTimestamp: Sun, 14 Aug 2016 19:46:30 -0700

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

No volumes.

No events.


[root@tlx241 /]# kubectl get pods

NAME                        READY     STATUS    RESTARTS   AGE

test-petset-0               1/1       Running   0          29m

test-petset-1               1/1       Running   0          28m

test-petset-2               1/1       Running   0          9s

打补丁后,PetSet 现在运行 3 个 Pod。

当上述 PetSet 定义被修补以增加一个副本时,它会在系统中引入一个额外的 Pod。这反过来又会在 Datera Data Fabric 上置备一个额外的卷。因此,当 PetSet 扩展时,卷会动态置备并附加到 Pod。

为了支持持久性和一致性的概念,如果 Pod 从一个 minion 移动到另一个 minion,卷会附加(挂载)到新的 minion 节点,并从旧的 minion 分离(卸载),以保持对数据的持久访问。

结论

这演示了 Kubernetes 如何使用 Pet Sets 编排有状态和无状态工作负载。虽然 Kubernetes 社区正在努力扩展 FlexVolume 框架的功能,但我们很高兴这个解决方案使得 Kubernetes 能够在数据中心中更广泛地运行。

加入并贡献:Kubernetes 存储 SIG