这篇文章已发布一年多。较早的文章可能包含过时的内容。请检查页面信息自发布以来是否已不准确。
使用 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。
- 下载 Kubernetes
- 在 GitHub 上参与 Kubernetes 项目
- 在 Stack Overflow 上提问(或回答问题)
- 在 k8s Slack 上与社区联系
- 在 Twitter @Kubernetesio 上关注我们获取最新更新