最近一位业务开发人员急匆匆地找到小白,向小白讲述了他遇到的棘手问题。原来,他新写的应用有一个图片上传功能,但是出现了两个大麻烦。
第一个问题: 每当应用程序重新部署后,用户之前上传到服务器的图片就消失不见了,这可能导致重要数据的丢失。
第二个问题: 在分布式部署环境下,图片文件分散存储在几个服务器上,这种混乱的存储方式导致有时候读取文件时会找不到,严重影响了应用的正常运行。
简单解决
小白一听这个情况就笑了,看起来不是什么大问题,于是小白和开发人员说,我给你的程序挂一块网盘吧!这样你的每个程序上传时候都上传到网盘,读取时候也去网盘读,通过这个网盘来中心化管理。
说干就干,首先找一台机器搭建一个网盘通过 NFS 服务。
- 安装 NFS 服务
nfs-kernel-server - 在
/etc/exports文件中定义要共享的目录
/mnt/nfs_share <client1>(rw,sync,no_root_squash) <client2>(ro,sync,no_root_squash)
- 导出配置的共享目录并且启动 NFS
sudo exportfs -a
sudo systemctl start nfs-kernel-server
sudo systemctl enable nfs-kernel-server # 开机自启动
网盘已经搭建好了,接下来就是如何把这个网盘 “插入” K8S了, K8S 的存储资源被抽象的叫做 Persistent Volume(PV), 既然叫做持久存储了,那自然他的生命周期和 POD 不同了,意味着POD 即使被删除,PV 仍然存在(数据也依然存在)。 PV 有多种访问模式:
- ReadWriteOnce(RWO) :这种模式表示 PV 只能被一个节点以读写方式挂载。例如,一些对数据安全和一致性要求较高的数据库应用,如 MySQL,通常会使用这种访问模式,以确保在一个时间点只有一个节点能够对数据进行读写操作,避免数据冲突。
- ReadOnlyMany(ROX) :多个节点可以以只读方式挂载该 PV。适用于存储配置文件或者只读的数据仓库等场景。比如,一个应用的配置文件存储在 PV 中,多个 Pod 可以同时读取这些配置信息,但不能修改。
- ReadWriteMany(RWX) :允许多个节点以读写方式挂载 PV。像一些共享文件存储的应用场景,如团队协作的文件编辑应用,多个用户(通过不同的 Pod 代表)可以同时对文件进行读写操作。
那我们就在 K8S 中创建一个网盘作为 PV, 通过 kubectl apply -f nfs-pv.yaml。 下面是这个yaml的文件内容:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 100Gi # 定义PV的存储容量,这里是100GB
accessModes:
- ReadWriteMany # 针对文件存储这里当然是多个服务器都可以访问啦
nfs:
server: 10.158.12.41 # NFS服务器的IP地址
path: /mnt/nfs_share
有了 PV 后,我们还需要一个Persistent Volume Claim(PVC),PV和PVC是关联的关系, PVC中定义了 POD 对存储资源的需求,比如存储大小和访问模式。应用部署时指定了这个 PVC 的话,它就会去寻找合适的PV来满足需求。如果没有合适的 PV,Pod 可能会一直处于等待状态,直到有满足要求的 PV 出现。当 Pod 被删除时,PVC 可以根据配置选择是否保留(retain模式)或者自动删除(delete模式)。在retain模式下,PVC 和与之绑定的 PV 会保留下来,以便后续的 Pod 可以继续使用;在delete模式下,PVC 被删除时会触发与之绑定的 PV 的回收机制,PV 可能会被删除或者重新标记为可用。
PVC 的创建也很简单
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi # 这里不要高于刚才PV 大小
最后来部署我们的应用,将这个网盘通过 PVC 挂到应用上。
apiVersion: v1
kind: Pod
metadata:
name: nfs-pod
spec:
containers:
- name: my-container
image: your-container - image
volumeMounts:
- name: nfs-volume
mountPath: /data # 容器内挂载存储的路径
volumes:
- name: nfs - volume
persistentVolumeClaim:
claimName: nfs-pvc
挂载了网盘后,应用只要将文件保存在 /data 下,并且通过 /data 读取文件,就完美解决了之前遇到的问题。
又来问题?
好景不长,多了一段时间,开发人员又跑来告诉小白,说在最近的运行过程中,又遇到2个问题:
- 这个 NFS 服务挂过几次,导致非常多用户投诉,能不能解决一下可用性的问题,避免单点故障。
- 随着时间文件越来越多,网盘大小很快就满了,需要扩容
这两个需求听着小白眉头紧皱,这看起来不好解决呀!经过阅读与思考,最终小白给出 2 种解决方案:
- 传统存储:使用 Ceph,它可以将数据存储在多个节点上,并且通过数据复制机制保证即使部分节点出现故障,数据仍然可以正常访问。在 K8S 环境中,可以将 原来的 NFS 替换为 Ceph作为PV,为应用提供高可用的存储,并且在数据量不断增长的情况下,Ceph 可以动态地分配资源和重新平衡数据。
- 对象存储:使用 MinIO 或者 AWS S3这样的对象存储服务来存储文件,数据是通过 API 进行管理的,而不是通过文件系统,也就是开发人员需要通过 API 和 对象存储服务器进行交互。
这两种方案开发人员都很满意,因为对象存储没有小白什么事情了,开发甚至都不需要了解 PV 和 PVC 的概念, 看起来搭建一个 minio 最省事了。
小白通过 minio 官方的 heml 搭建好后,开发人员通过 minio 的 SDK 很方便的实现了文件上传功能。
MinioClient minioClient = MinioClient.builder()
.endpoint("http://127.0.0.1:9000")
.credentials("minioadmin", "minioadmin")
.build();
minioClient.putObject(PutObjectArgs.builder()
.bucket("my-bucket")
.object("hello.jpg")
.stream(fileInputStream, fileInputStream.available(), -1)
.contentType("text/plain")
.build());
当然另一个方案 Ceph 也需要尝试起来, 毕竟它也是现代数据中心和云环境中的理想选择!
部署 Ceph 集群需要通过 Rook,它是 Ceph 的 operator
kubectl create namespace rook-ceph
kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/deploy/cluster/examples/kubernetes/ceph/common.yaml
kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/deploy/cluster/examples/kubernetes/ceph/operator.yaml
然后就可以部署 Ceph 了,根据官方的配置说明
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
name: my-ceph-cluster
namespace: rook-ceph
spec:
dataDirHostPath: /var/lib/rook
cephVersion:
image: ceph/ceph:v16.2.6
storage:
useAllNodes: true
useAllDevices: true
network:
hostNetwork: false
dashboard:
enabled: true
urlPrefix: /ceph-dashboard
port: 7000
部署好后需要最后一步,创建存储池
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: my-block-pool
namespace: rook-ceph
spec:
replicated:
size: 3
存储池相比直接用硬盘,增加了灵活性和可用性,比如通过创建多个存储池,可以根据不同的工作负载优化性能。你可以使用 SSD 创建一个快速存储池,而使用 HDD 创建一个大容量存储池,让不同场景下的经济性最大化。其次我们配置的这个 replication 参数也是为了确保数据的可用性。
最终为了进一步降低维护成本,小白直接买了一个 Azure Stroage 服务,类似于阿里云的 OSS,这下彻底一劳永逸了,开发人员做个文件上传功能?和我有啥关系,哈哈哈。
看到这里了点个赞吧!