# K8s 持久化存储
# 1. Volume
Container(容器)中的磁盘文件是短暂的,当容器崩溃时,kubelet 会重新启动容器,Container 会以最干净的状态启动,最初的文件将丢失。另外,当一个 Pod 运行多个 Container 时,各个容器可能需要共享一些文件。Kubernetes Volume 可以解决这两个问题。
- 一些需要持久化数据的程序才会用到 Volumes,或者一些需要共享数据的容器需要 volumes。
- 日志收集的需求需要在应用程序的容器里面加一个 sidecar,这个容器是一个收集日志的容器,比如 filebeat,它通过 volumes 共享应用程序的日志文件目录。
# 1.1 EmptyDir 实现数据共享
和上述 volume 不同的是,如果删除 Pod,emptyDir 卷中的数据也将被删除,一般 emptyDir 卷用于 Pod 中的不同 Container 共享数据。
# cat nginx-deploy.yaml | |
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
name: nginx-deploy | |
labels: | |
app: nginx-deploy | |
namespace: default | |
spec: | |
selector: | |
matchLabels: | |
app: nginx-deploy | |
replicas: 1 | |
template: | |
metadata: | |
labels: | |
app: nginx-deploy | |
spec: | |
restartPolicy: Always | |
volumes: | |
- name: share-volume | |
emptyDir: {} | |
containers: | |
- name: nginx | |
image: nginx:latest | |
imagePullPolicy: IfNotPresent | |
resources: | |
limits: | |
memory: 1024Mi | |
cpu: 1 | |
requests: | |
memory: 128Mi | |
cpu: 100m | |
volumeMounts: | |
- name: share-volume | |
mountPath: /opt | |
- name: nginx2 | |
image: nginx:latest | |
imagePullPolicy: IfNotPresent | |
resources: | |
limits: | |
memory: 1024Mi | |
cpu: 1 | |
requests: | |
memory: 128Mi | |
cpu: 100m | |
command: | |
- sh | |
- '-c' | |
- sleep 3600 | |
volumeMounts: | |
- name: share-volume | |
mountPath: /mnt |
# 1.2 Volumes HostPath 挂载宿主机路径
hostPath 卷可将节点上的文件或目录挂载到 Pod 上,用于 Pod 自定义日志输出或访问 Docker 内部的容器等。
[root@k8s-master01 ~]# cat nginx-deploy.yaml | |
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
name: nginx-deploy | |
labels: | |
app: nginx-deploy | |
namespace: default | |
spec: | |
selector: | |
matchLabels: | |
app: nginx-deploy | |
replicas: 1 | |
template: | |
metadata: | |
labels: | |
app: nginx-deploy | |
spec: | |
restartPolicy: Always | |
volumes: | |
- name: share-volume | |
emptyDir: {} | |
- name: tz-config | |
hostPath: | |
path: /usr/share/zoneinfo/Asia/Shanghai | |
type: "" | |
- name: timezone | |
hostPath: | |
path: /etc/timezone | |
type: "" | |
containers: | |
- name: nginx | |
image: nginx:latest | |
imagePullPolicy: IfNotPresent | |
resources: | |
limits: | |
memory: 1024Mi | |
cpu: 1 | |
requests: | |
memory: 128Mi | |
cpu: 100m | |
volumeMounts: | |
- name: share-volume | |
mountPath: /opt | |
- name: tz-config | |
mountPath: /usr/share/zoneinfo/Asia/Shanghai | |
- name: tz-config | |
mountPath: /etc/localtime | |
- name: timezone | |
mountPath: /etc/timezone | |
- name: nginx2 | |
image: nginx:latest | |
imagePullPolicy: IfNotPresent | |
resources: | |
limits: | |
memory: 1024Mi | |
cpu: 1 | |
requests: | |
memory: 128Mi | |
cpu: 100m | |
command: | |
- sh | |
- '-c' | |
- sleep 3600 | |
volumeMounts: | |
- name: share-volume | |
mountPath: /mnt |
hostPath 卷常用的 type(类型)如下:
- type 为空字符串:默认选项,意味着挂载 hostPath 卷之前不会执行任何检查。
- DirectoryOrCreate:如果给定的 path 不存在任何东西,那么将根据需要创建一个权限为 0755 的空目录,和 Kubelet 具有相同的组和权限。
- Directory:目录必须存在于给定的路径下。
- FileOrCreate:如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为 0644,和 Kubelet 具有相同的组和所有权。
- File:文件,必须存在于给定路径中。
- Socket:UNIX 套接字,必须存在于给定路径中。
- CharDevice:字符设备,必须存在于给定路径中。
- BlockDevice:块设备,必须存在于给定路径中。
# 1.3 挂载 NFS 至容器
#1. 安装 nfs | |
# yum install nfs-utils -y | |
# mkdir /data/nfs -p | |
# vim /etc/exports | |
/data 192.168.1.0/24(rw,no_root_squash) | |
# exportfs -arv | |
# systemctl start nfs-server && systemctl enable nfs-server && systemctl status nfs-server | |
#2. 测试客户端挂载 | |
# showmount -e 192.168.1.75 | |
# mount -t nfs 192.168.1.75:/data/nfs /mnt | |
#3.Deploy 挂载 NFS | |
[root@k8s-master01 ~]# cat nginx-deploy-nfs.yaml | |
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
name: nginx-deploy | |
labels: | |
app: nginx-deploy | |
annotations: | |
app: nginx-deploy | |
namespace: default | |
spec: | |
selector: | |
matchLabels: | |
app: nginx-deploy | |
replicas: 1 | |
template: | |
metadata: | |
labels: | |
app: nginx-deploy | |
annotations: | |
app: nginx-deploy | |
spec: | |
restartPolicy: Always | |
volumes: | |
- name: nfs-volume | |
nfs: | |
server: 192.168.1.75 | |
path: /data/nfs | |
- name: tz-config | |
hostPath: | |
path: /usr/share/zoneinfo/Asia/Shanghai | |
type: "" | |
- name: timezone | |
hostPath: | |
path: /etc/timezone | |
type: "" | |
containers: | |
- name: nginx-deploy | |
image: nginx:latest | |
imagePullPolicy: IfNotPresent | |
resources: | |
limits: | |
memory: 1024Mi | |
cpu: 1 | |
requests: | |
memory: 128Mi | |
cpu: 100m | |
volumeMounts: | |
- name: nfs-volume | |
mountPath: /usr/share/nginx/html | |
- name: tz-config | |
mountPath: /usr/share/zoneinfo/Asia/Shanghai | |
- name: tz-config | |
mountPath: /etc/localtime | |
- name: timezone | |
mountPath: /etc/timezone |
# 2. PV、PVC
PersistentVolume:简称 PV,是由 Kubernetes 管理员设置的存储,可以配置 Ceph、NFS、GlusterFS 等常用存储配置,相对于 Volume 配置,提供了更多的功能,比如生命周期的管理、大小的限制。PV 分为静态和动态。
PersistentVolumeClaim:简称 PVC,是对存储 PV 的请求,表示需要什么类型的 PV,需要存储的技术人员只需要配置 PVC 即可使用存储,或者 Volume 配置 PVC 的名称即可。
# 2.1 PV 回收策略
- Retain:保留,该策略允许手动回收资源,当删除 PVC 时,PV 仍然存在,PV 被视为已释放,管理员可以手动回收卷。
- Recycle:回收,如果 Volume 插件支持,Recycle 策略会对卷执行 rm -rf 清理该 PV,并使其可用于下一个新的 PVC,但是本策略将来会被弃用,目前只有 NFS 和 HostPath 支持该策略。
- Delete:删除,如果 Volume 插件支持,删除 PVC 时会同时删除 PV,动态卷默认为 Delete,目前支持 Delete 的存储后端包括 AWS EBS, GCE PD, Azure Disk, or OpenStack Cinder 等。
- 可以通过 persistentVolumeReclaimPolicy: Recycle 字段配置
# 2.2 PV 访问策略
- ReadWriteOnce:可以被单节点以读写模式挂载,命令行中可以被缩写为 RWO。
- ReadOnlyMany:可以被多个节点以只读模式挂载,命令行中可以被缩写为 ROX。
- ReadWriteMany:可以被多个节点以读写模式挂载,命令行中可以被缩写为 RWX。
- ReadWriteOncePod :只允许被单个 Pod 访问,需要 K8s 1.22 + 以上版本,并且是 CSI 创建的 PV 才可使用,缩写为 RWOP
Volume Plugin | ReadWriteOnce | ReadOnlyMany | ReadWriteMany | ReadWriteOncePod |
---|---|---|---|---|
AzureFile | ✓ | ✓ | ✓ | - |
CephFS | ✓ | ✓ | ✓ | - |
CSI | depends on the driver | depends on the driver | depends on the driver | depends on the driver |
FC | ✓ | ✓ | - | - |
FlexVolume | ✓ | ✓ | depends on the driver | - |
HostPath | ✓ | - | - | - |
iSCSI | ✓ | ✓ | - | - |
NFS | ✓ | ✓ | ✓ | - |
RBD | ✓ | ✓ | - | - |
VsphereVolume | ✓ | - | - (works when Pods are collocated) | - |
PortworxVolume | ✓ | - | ✓ | - |
# 2.3 存储分类
- 文件存储:一些数据可能需要被多个节点使用,比如用户的头像、用户上传的文件等,实现方式:NFS、NAS、FTP、CephFS 等。
- 块存储:一些数据只能被一个节点使用,或者是需要将一块裸盘整个挂载使用,比如数据库、Redis 等,实现方式:Ceph、GlusterFS、公有云。
- 对象存储:由程序代码直接实现的一种存储方式,云原生应用无状态化常用的实现方式,实现方式:一般是符合 S3 协议的云存储,比如 AWS 的 S3 存储、Minio、七牛云等。
# 2.4 PV 配置示例 NFS
apiVersion: v1 | |
kind: PersistentVolume | |
metadata: | |
name: nfs-pv1 | |
spec: | |
capacity: | |
storage: 5Gi | |
volumeMode: Filesystem | |
accessModes: | |
- ReadWriteOnce | |
persistentVolumeReclaimPolicy: Retain | |
storageClassName: nfs-slow | |
nfs: | |
path: /data/pv1 | |
server: 192.168.1.75 |
capacity:容量配置
volumeMode:卷的模式,目前支持 Filesystem(文件系统) 和 Block(块),其中 Block 类型需要后端存储支持,默认为文件系统
accessModes:该 PV 的访问模式
storageClassName:PV 的类,一个特定类型的 PV 只能绑定到特定类别的 PVC;
persistentVolumeReclaimPolicy:回收策略
mountOptions:非必须,新版本中已弃用
nfs:NFS 服务配置,包括以下两个选项
- path:NFS 上的共享目录
- server:NFS 的 IP 地址
# 2.5 PV 配置示例 HostPath
apiVersion: v1 | |
kind: PersistentVolume | |
metadata: | |
name: hostpath | |
spec: | |
capacity: | |
storage: 5Gi | |
volumeMode: Filesystem | |
accessModes: | |
- ReadWriteOnce | |
persistentVolumeReclaimPolicy: Retain | |
storageClassName: hostpath | |
hostPath: | |
path: "/mnt/data" |
hostPath:hostPath 服务配置
- path:宿主机路径
# 2.6 PV 的状态
- Available:可用,没有被 PVC 绑定的空闲资源。
- Bound:已绑定,已经被 PVC 绑定。
- Released:已释放,PVC 被删除,但是资源还未被重新使用。
- Failed:失败,自动回收失败。
# 2.7 PVC 绑定 PV
apiVersion: v1 | |
kind: PersistentVolumeClaim | |
metadata: | |
name: nfs-pvc | |
spec: | |
storageClassName: nfs-slow | |
accessModes: | |
- ReadWriteOnce | |
resources: | |
requests: | |
storage: 5Gi |
- PVC 的空间申请大小≤PV 的大小
- PVC 的 StorageClassName 和 PV 的一致
- PVC 的 accessModes 和 PV 的一致
# 2.8 Depoyment 挂载 PVC
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
labels: | |
app: nginx-deploy | |
name: nginx-deploy | |
spec: | |
replicas: 3 | |
selector: | |
matchLabels: | |
app: nginx-deploy | |
template: | |
metadata: | |
labels: | |
app: nginx-deploy | |
spec: | |
volumes: | |
- name: nfs-pvc-storage #volume 名称 | |
persistentVolumeClaim: | |
claimName: nfs-pvc #PVC 名称 | |
containers: | |
- image: nginx | |
name: nginx | |
volumeMounts: | |
- name: nfs-pvc-storage | |
mountPath: /usr/share/nginx/html |
挂载 PVC 的 Pod 一直处于 Pending:
- PVC 没有创建成功或 PVC 不存在
- PVC 和 Pod 不在同一个 Namespace