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