一、无状态应用与有状态应用
应用的有状态和无状态是根据应用是否有持久化保存数据的需求而言的,即持久化保存数据的应用为有状态应用,反之则为无状态的应用。常见的系统往往是有状态的应用,比如对于微博和微信这类应用,所有用户发布的内容和留言都是要保存记录的。但是一个系统往往是由众多微服务或更小的应用模块构成的。有的微服务或模块其实并没有数据持久化的需求。例如,搭建一个 Wordpress博客系统需要部署一个前端的 PHP应用,以及一个后端的 MySQL数据库。虽然整个博客系统有持久化的需求,是一个有状态的系统,但是其子模块前端PHP应用并没有将数据保存在本地,而是存放到MySQL数据库中。 所以一个 Wordpress系统可以拆解成一个无状态的前端以及一个有状态的后端。 有状态和无状态的应用在现实当中比比皆是。从实例数量上来说,无状态的应用应该会更多一些,因为对大多数的系统而言,读请求的数量往往远远高于写请求的数量。
二、容器数据持久化
因为容器是非持久化的,即当容器退出后,其内部的所有数据和状态都会丢失。同样的镜像再次启动一个新的容器实例,该实例默认不会继承之前实例的状态。这对无状态应用来说不是问题,相反是一个很好的特性,可以很好地保证无状态应用的一致性。但是如果是一个有状态的应用 (比如Mysql),这就是一场灾难了。
因此,在容器引擎的层面必须满足数据持久化的需求。Docker在容器引擎的层面提供了卷 (Volume)的概念。用户可以建立数据卷容器来为容器提供持久化的支持。容器实例需要将持久化的数据写入数据卷容器保存。当应用容器退出时,数据仍然安然地储存于数据卷容器中。此外Docker以插件的形式支持多种存储方式。通过卷插件(VolumePlugin),目前Docker 容器可以对接主机的目录、软件定义存储(如 GlusterFS及 Ceph)、云存储(如 AWS 及 GCE 等公有云提供的云储存)及储存管理解决方案(如 Flocker等)。
三、持久化卷(PV)与持久化卷请求(PVC)
为了满足容器用户在云环境储存的需求。Kubernetes 在容器编排的层面提供了持久化卷 (Persistent Volume, PV) 及持久化卷请求 (Persistent Volume Claim, PVC)的概念。持久化卷定义了具体的储存的连接信息,如NFS(Network File System)服务器的地址和端口、卷的大小及访问方式。在Openshift中,集群管理员会定义一系列的持久化卷,形成一个持久化卷的资源池。当用户部署有持久化需求的容器应用时,用户需要创建一个持久化卷请求。在这个请求中,用户申明所需存储的大小及访问方式。Kubernetes将负责根据用户的持久化卷请求找到匹配需求的持久化卷进行对接。最终的结果是容器启动后,持久化卷定义的后端存储将会被挂载到容器的指定目录。
通过 oc get pv 和 oc get pvc 命令查看PV和PVC信息。
四、持久化卷的生命周期
如下图所示,持久化卷的生命周期一共分为“供给”、“绑定”、“使用”、“回收”及“释放”五个阶段。
1、供给
在 Kubernetes 中,储存资源的供给分为两种类型:静态供给和动态供给。对于静态供给,集群管理员会创建一系列的持久化卷,形成持久化卷的资源池。动态供给是集群所在基础设施云根据需求动态地创建出持久化卷,如OpenStack。
通过oc describe pv <pv-name>命令来查看持久化卷的详细信息。
其中Access Mode(访问方式)是描述持久化卷的访问特性,目前有三种访问方式:
- ReadWriteOnce(RWO):可读可写,只能被一个Node节点挂载。
- ReadWriteMany(RWX):可读可写,可以被多个Node节点挂载。
- ReadOnlyMany(ROX):只读,能被多个Node节点挂载。
kubernetes 官方文档:https://kubernetes.io/docs/concepts/storage/persistent-volumes/
2、绑定
用户在持久化卷请求中声明需要的储存资源的特性,如大小和访问方式。kubernetes负责在持久化卷的资源池中寻找匹配的持久化卷对象,并将持久化卷请求与目标持久化卷进行对接。这时持久化卷和持久化卷请求的状态都将变成Bound,即绑定状态。
通过oc describe pvc <pvc-name>命令来查看持久化卷请求的详细信息。
3、使用
在用户部署容器时会在DeploymentConfig的容器定义中指定Volume的挂载点,并将这个挂载点和持久化卷请求关联。当容器启动时,持久化卷指定的后端储存会被挂载到容器定义的挂载点上。
通过oc describe dc <dc-name>或者oc describe pod <pod-name>命令,可以查看详细信息。下面是部分截图
4、释放
当应用下线不再使用储存时,可以删除相关的持久化卷请求,这样持久化卷的状态就会变成released,即释放。
5、回收
当持久化卷的状态变为 released 后,Kubernetes将根据持久化卷定义的回收策略回收持久化卷。目前支持的回收策略有三种 :
- Retain:保留数据,人工回收持久化卷。
- Recycle:通过 rm -rf 删除卷上的所有数据。目前只有NFS及Host Path支持这种方式。
- Delete:动态的删除后端储存。该模式需要下层IaaS支持,目前AWS EBS、GCE PD及OpenStack Cinder支持这种模式。
五、持久化卷的储存类型
Kubernetes 的持久化卷支持的后端储存的类型很多,包括宿主机的本地目录( Host Path)、 网络文件系统( NFS)、 OpenStack Cinder分布式储存(如 GlusterFS、 Ceph RBD 及 CephFS) 及云储存(如 AWS Elastic Block Store或 GCE Persistent Disk) 等。
1、EmptyDir
使用emptyDir,当Pod分配到Node上时,将会创建emptyDir,并且只要Node上的Pod一直运行,Volume就会一直存。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。注:删除容器不影响emptyDir。
2、hostPath
hostPath允许挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。
3、NFS
NFS 是Network File System的缩写,即网络文件系统。Kubernetes中通过简单地配置就可以挂载NFS到Pod中,而NFS中的数据是可以永久保存的,同时NFS支持同时写操作。Pod被删除时,Volume被卸载,内容被保留。这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递。
更多Volume类型见Kubernetes中文文档:Kubernetes Volume 类型
六、储存资源定向匹配(标签选择器)
不同用户对储存的需求不尽相同,除了大小和访问方式外,可能还有对磁盘的速度、储存所在的数据中心等有特殊的要求。为了灵活满足储存需求和储存资源的对接, Kubernetes 支持为持久化卷打上不同的标签( Label),在持久化卷请求侧则通过定义标签选择器来申明该持久化卷请求具体需要与什么样的持久化卷匹配。
通过 oc label 命令打标签。比如为PV搭上type=hello的标签:oc label pv <pv-name> type=hello
oc get pv --show-labels 命令用来查看pv的标签
目前,持久化卷请求支持两种标签选择器:matchLabels及matchExpressions。
- matchLabels选择器可以精确匹配一个或多个标签
- matchExpressions选择器支持标签的模糊匹配。用户可以使用操作符 In 或者 NotIn 对标签的值进行模糊匹配。
其他:oc rsync 命令用于将容器中某个目录的数据同步到宿主机上。比如:oc rsync docker-registry-1-blpar:/registry . 将OCP的registry同步到宿主机的当前目录上。