这篇文章已发布一年多。较早的文章可能包含过时的内容。请检查页面信息自发布以来是否已不准确。

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

引言

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

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,如果 Pod 请求持久存储,将在 Datera Data Fabric 中进行供应。

[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 PersistentVolume 插件安装在所有 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} 个 Pets。每个 Pet 都有一个确定的名称,PetSetName-Ordinal,和一个唯一的身份。每个 Pet 最多有一个 Pod,每个 PetSet 最多有一个具有给定身份的 Pet。PetSet 确保在任何给定时间都有指定数量的具有唯一身份的“pets”正在运行。一个 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 Storage SIG