K8s存储卷入门

我们为啥需要存储卷?

默认的容器里,存储是跟容器绑定的:容器跑着的时候,你能往里面写文件,但是一旦容器崩了、删了,这些文件就全没了 ------ 你要是用容器跑个数据库,重启之后数据全没了

还有个常见的需求:同一个 Pod 里的两个容器,要共享文件。比如一个容器跑应用,生成日志,另一个容器跑日志收集工具,要把日志传到远程存储,这俩容器得能读到同一份日志文件才行。

存储卷就是来解决这两个问题的:把存储的生命周期,和容器的生命周期拆开。容器删了,只要卷还在,数据就还在;同时,卷能被多个容器挂载,实现文件共享。

K8s 里的卷分两大类:临时卷(跟 Pod 绑定,Pod 没了就没了)和持久卷(比 Pod 活得长,Pod 删了数据还在)


第一个:emptyDir,最基础的临时文件夹

emptyDir就是个跟 Pod 绑定的临时文件夹。

它是怎么工作的?

当你的 Pod 被调度到某个节点上的时候,K8s 就会在这个节点上给你建个空目录,这就是 emptyDir。只要 Pod 还在运行,这个目录就一直存在,Pod 里的所有容器,都能挂载这个目录,哪怕它们挂载的路径不一样,读写的都是同一个地方 ------ 这就实现了同一个 Pod 里的容器共享文件。

那数据安全吗?分情况:

  • 如果只是容器崩了,Pod 还在节点上,那 emptyDir 里的数据一点事都没有,容器重启了还能读到原来的文件。

  • 如果整个 Pod 都被删了,那这个目录还有里面的所有数据,都会被永久删掉,找不回来。

什么时候用它?

它就是个临时存储,适合那些不需要持久化的数据,比如:

  1. 多容器共享文件:最典型的就是日志收集场景,应用把日志写到 emptyDir,收集工具从这里读,读完就完事了,不用存。

  2. 临时计算缓存:比如你跑个任务,中间的计算结果,不用存到持久存储,任务跑完 Pod 删了,这些结果就没用了。

  3. 敏感临时数据 :你还能把它搞成内存盘!把medium设成Memory,它就用节点的内存来存数据,速度飞快,还不写磁盘,存个临时密码、密钥啥的,Pod 删了数据就没了,不会留在节点上,安全得很。

举例

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: emptydir
spec:
  containers:
  - image: httpd
    imagePullPolicy: IfNotPresent
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      sizeLimit: 2G

我们建了个 Pod,里面跑了个 httpd 容器,我们定义了一个叫cache-volume的 emptyDir 卷,把它挂载到容器的/cache目录,还限制了这个卷最大只能用 2G------ 防止你写太多把节点的磁盘给占满了。

创建完 Pod 之后,你去节点上看,会发现容器里的/cache,实际就是节点上的一个目录:

复制代码
"containerPath": "/cache",
"hostPath": "/var/lib/kubelet/pods/xxx/volumes/.../cache-volume",

你在容器里写的东西,都写到节点的这个目录里了,删了 Pod,这个目录就自动没了,完美符合临时存储的需求。


第二个:hostPath,直接用节点本地的目录

接下来是hostPath,这个卷的作用很简单:让 Pod 直接访问节点本地的文件和目录

它是怎么工作的?

把节点上的某个目录,直接挂载到 Pod 里的容器中。比如你把节点的/data目录挂载给 Pod,那 Pod 里的容器,就能直接读写节点/data里的文件,跟你自己在节点上操作一模一样。

而且这些数据是存在节点上的,跟 Pod 一点关系都没有,你删了 Pod,节点上的文件一点变化都没有,还留在那。

注意!这东西不可以乱用!

hostPath 有个大问题:它会把你的 Pod 绑死在这个节点上

比如你把 Pod 调度到了节点 A,挂载了节点 A 的/data目录,那如果之后 Pod 因为某些原因,被重新调度到了节点 B,节点 B 的/data是空的啊!Pod 就找不到原来的数据了,业务直接崩了。

所以 hostPath 根本不是给普通的业务存储用的!它就是给那些需要访问节点本地资源的特殊场景用的,比如:

  • 监控容器:要访问节点的/var/run/docker.sock,也就是 docker 的套接字,来监控节点上的容器。

  • 存储插件:要访问节点的磁盘设备,来做存储的挂载。

  • 静态配置:节点本地的一些固定配置文件,Pod 要读这些配置。

举例

把节点的/data目录挂载给 nginx:

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: hostpathtest
spec:
  containers:
  - image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data
      type: DirectoryOrCreate

这里的type: DirectoryOrCreate意思是,如果节点的/data不存在,就自动创建它。

创建完 Pod 之后,你直接在节点上往/data里写个网页文件:

复制代码
echo hello, hostwrite! > /data/index.html

然后访问 Pod 的 IP,就能看到这个文件了!因为 nginx 的网页目录,实际就是节点的/data,删了 Pod,节点上的文件还在,一点事都没有。


最难的部分:PV 和 PVC

管理员就像饭店的后厨,提前做好了一堆菜(这些菜就是 PV,也就是准备好的存储)。 你作为客人,不用管菜是怎么做的,用的什么食材,你只要跟服务员说:我要一份鱼香肉丝,要微辣的(这就是 PVC,也就是你申请存储的要求)。 服务员(也就是 K8s)就会去后厨,找到符合你要求的菜,给你端上来。

这俩是用来解耦的,管理员负责准备存储,不用管谁用;用户负责申请存储,不用管存储是怎么来的。

而且 PV 是集群级别的持久存储,不管你的 Pod 跑到哪个节点,都能访问到同一份数据,Pod 删了,数据还在,这就是我们要的持久化存储

第一步:先搞个共享存储

要搞持久存储,我们得先搞个所有节点都能访问的共享存储,不然 Pod 换了节点就找不到数据了。我们这里用 NFS,也就是网络文件系统,说白了就是个网络文件夹,所有节点都能读写,不管 Pod 在哪,都能读到同一份数据。

我们在 master 节点搭个 NFS 服务,把/nfsshare目录共享出去,所有节点都能访问,这个步骤很简单,装个服务端客户端就行了

第二步:创建 PV,把存储注册给 K8s

现在我们有了 NFS 存储,接下来要把它注册到 K8s 里,告诉 K8s:我这有个存储,你可以用,这就是创建 PV。

复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv
  labels:
    pvname: nfspv
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  nfs:
    path: /nfsshare
    server: 192.168.30.130

就是我们告诉 K8s:我这有个 5G 的存储,用的是 NFS,服务器地址是 master 的 IP,共享路径是/nfsshare,支持读写,存储类叫 slow。

访问模式:这个存储能怎么用?

就是这个存储,支持什么样的挂载权限,不同的存储支持的不一样:

  1. ReadWriteOnce:最常用的,这个存储只能被一个节点以读写的方式挂载,同一个节点上的多个 Pod 可以用它。比如云盘默认就只支持这个。

  2. ReadOnlyMany:能被很多节点以只读的方式挂载,适合存静态配置。

  3. ReadWriteMany:能被很多节点以读写的方式挂载,NFS 就支持这个。

  4. ReadWriteOncePod:只能被一个 Pod 用,哪怕同一个节点的其他 Pod 也不能用,用来保证只有一个 Pod 能读写这个存储。

回收策略:删了申请之后,存储怎么办?

就是当绑定这个 PV 的 PVC 被删了之后,这个 PV 要怎么处理:

  1. Retain:啥也不做,数据原封不动保留,你要手动去删数据,才能重新用这个 PV,适合存重要数据,防止误删。

  2. Recycle:自动把 PV 里的所有文件删掉,然后重置成可用状态,给新的 PVC 用,不过这个现在已经被弃用了,不够安全。

  3. Delete:直接把 PV 和对应的后端存储一起删掉,比如云盘,删了 PVC,云盘也自动删了,不用你管。

创建完 PV 之后,你就能看到它的状态是Available,也就是空闲的,等着被绑定。

第三步:创建 PVC,申请存储

现在管理员准备好了存储,用户就可以申请了,也就是创建 PVC,告诉 K8s:我要个什么样的存储。

复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: slow
  selector:
    matchLabels:
      pvname: "nfspv"

我要一个读写的存储,要 1G 的,存储类是 slow,还要标签是pvname: nfspv的 PV。

K8s 收到之后,就会去集群里找符合要求的 PV,找到我们刚才创建的那个 5G 的 NFS 的 PV,然后把它们绑定在一起,这时候 PVC 的状态就变成Bound了,PV 的状态也变成Bound了,搞定!

第四步:挂载给 Pod

绑定完之后,你就可以把这个 PVC 当成卷,挂载给 Pod 了,跟之前的卷一模一样:

复制代码
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - image: httpd
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: "/usr/local/apache2/htdocs"
      name: mypd
  volumes:
  - name: mypd
    persistentVolumeClaim:
      claimName: myclaim

创建完 Pod 之后,你直接在 NFS 的共享目录里写文件:

复制代码
echo pvctest! > /nfsshare/index.html

然后不管你在哪个节点,访问 Pod 的 IP,都能看到这个文件,因为 NFS 是共享的,所有节点都能访问,不管 Pod 跑到哪,都能读到同一份数据。

删了 Pod,NFS 里的文件还在,下次你再创建 Pod,挂载同一个 PVC,还是能读到原来的数据,这就是持久存储!跑数据库用这个,再也不怕容器删了数据没了


分清所有卷

|----------|-------|----------------------|-------------------|--------------------|
| 卷类型 | 数据存在哪 | 生命周期 | 跨节点能用吗? | 什么时候用它? |
| emptyDir | 节点本地 | 和 Pod 绑定,Pod 删了数据就没了 | 不行,只能同一个 Pod 的容器用 | 临时存储、多容器共享日志、临时缓存 |
| hostPath | 节点本地 | 独立于 Pod,Pod 删了数据还在节点 | 不行,只能在有这个目录的节点用 | 访问节点本地资源、系统级配置 |
| PV/PVC | 共享存储 | 永久保留,跟 Pod 没关系 | 可以,所有节点都能访问 | 业务持久存储、数据库、要长期存的数据 |


相关推荐
zhao00666 小时前
flux + kubernetes + gitops + Kustomization
kubernetes·gitops
钝挫力PROGRAMER6 小时前
关于软件架构的一些疑惑
微服务·云原生·架构
倔强的胖蚂蚁6 小时前
信创企业级 openEuler 24 部署 docker-ce 全指南
运维·docker·云原生·容器
TG_yunshuguoji10 小时前
阿里云代理商:OpenClaw+K8s协同运维 常见问题解决方案
人工智能·阿里云·kubernetes·云计算·openclaw
marsh020610 小时前
32 openclaw容器化部署:Docker与Kubernetes集成指南
docker·ai·容器·kubernetes·编程·技术
@hdd11 小时前
KubeVirt 核心架构解析:3 层组件如何协同运转虚拟机
云原生·kubernetes·虚拟机
Hns.11 小时前
自建K8S集群对接阿里云SLS
阿里云·容器·kubernetes
johnny23312 小时前
K8s管理面板:Rancher、Lens、KubeSphere、K8s Dashboard、Kite
容器·kubernetes·rancher
QC·Rex12 小时前
Kubernetes v1.36 云原生架构新特性详解:生产级集群升级指南
云原生·kubernetes·serviceaccount·selinux·集群升级·ingress nginx·动态资源分配