ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

4.1 K8s存储之 Volume

存储卷volumes

官方文档:https://kubernetes.io/docs/concepts/storage/volumes/

​ 存储卷属于Pod不属于容器,同一个Pod内的多个容器可以共享此存储卷。这是因为每个Pod都有一个Pause的基础容器,所有Pod内的存储卷、命名空间等都是分配给这个Pause容器的,Pod内的主容器就是共享这个pause容器的网络名称空间(ipc、net、uts)及存储卷。

**存储卷种类:**使用kubectl explain pods.spec.volumes查看支持的存储卷:

1、emptyDir、gitRepo

  • emptyDir:空目录,用作临时目录或者缓存使用,随着Pod的删除而删除(Pod重启不会删除),按需创建,只在节点本地使用。当用作缓存时(emptyDir.medium=Memory),emptyDir关联的宿主机目录是宿主机的内存。

  • gitRepo:宿主机使用git clone来创建一个目录,然后将该目录以emptyDir的形式挂载到pod中。一旦gitRepo卷创建完毕后,无论是本地目录的修改还是远程仓库的修改,都不会影响对方。

[root@k8smaster volumes]# cat pod-vol-emptyDir2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: httpd
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ['/bin/httpd','-f','-h','/data/web/html']
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: html
      mountPath: /data/web/html
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /data
    command:
    - "/bin/sh"
    - "-c"
    - "while true;do echo $(date) >>/data/index.html ;sleep 1;done"
  volumes:
  - name: html
    emptyDir: {}  #emptyDir.medium=""|Memory表示使用磁盘还是使用内存。默认使用磁盘

2、hostPath

将主机节点的文件系统中的文件或目录挂载到集群中。

节点级的持久化,如果节点宕机,数据丢失。

[root@k8smaster volumes]# cat pod-vol-hostPath.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-hostpath
  namespace: default
spec:
  containers:
  - name: myapp-container
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
  - name: html
    hostPath:
      path: /data/pod/volume1
      type: DirectoryOrCreate  #可选

hostPath.type支持的取值:

ValueBehavior
空字符串(默认值)用于向后兼容,这意味着在安装hostPath卷之前不会执行任何检查。
DirectoryOrCreate如果给定的目录中不存在,则会根据需要在此处创建一个空目录,权限设置为0755,与Kubelet命令具有相同的组和属主。
Directory给定的目录必须存在
FileOrCreate如果给定的文件不存在,则会根据需要在此处创建一个空文件,权限设置为0644,与Kubelet命令具有相同的组和属主。
File给定的文件必须存在
Socket给定的unix socker必须存在
CharDevice给定的字符设备必须存在
BlockDevice给定的块设备必须存在

3、传统网络存储:

  • SAN(存储区域网络):iSCSI,……
  • NAS(网络附加存储):nfs,cifs,……
[root@k8smaster volumes]# cat pod-vol-nfs.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp-vol-nfs
  namespace: default
spec:
  containers:
  - name: myapp-pod-vol-nfs
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html  #要挂载的volume名称
      mountPath: /usr/share/nginx/html
  volumes:
  - name: html
    nfs: 
      path: /data/volumes
      server: 192.168.5.241
      
#注意: 使用nfs时,所有Node节点要安装nfs-utils包,否则mount命令无法实现nfs类型的挂载;此外nfs服务支持多客户端同时读写操作(nfs server自己具有持锁能力),但是nfs性能可能不建议在大规模环境中使用

4、分布式存储:

一般是文件系统级别、块级别

  • glusterfs,rbd(ceph),cephfs,……
  • vsphereVolume

示例:glusterfs-volume

#在glusterfs上创建volume
方式1:
直接使用heketi创建,例如创建3个volume:
heketi-cli volume create --size=50 --durability=none --name es1
heketi-cli volume create --size=50 --durability=none --name es2
heketi-cli volume create --size=50 --durability=none --name es3

1、创建连接外置的gusterfs的endpoint

apiVersion: v1
kind: Endpoints
metadata:
  name: glusterfs-cluster
subsets:
- addresses:
  - ip: 172.22.62.31 #glusterfs-node的IP
  ports:
  - port: 1  #port为1-65535之间的任意值,实际没有用到这个值
- addresses:
  - ip: 172.22.62.36
  ports:
  - port: 1
- addresses:
  - ip: 172.22.62.40
  ports:
  - port: 1
---
apiVersion: v1
kind: Service
metadata:
  name: glusterfs-cluster
spec:
  ports:
  - port: 1  

ep也可以这样写:

apiVersion: v1
kind: Endpoints
metadata:
  name: glusterfs-cluster
subsets:
- addresses:
  - ip: 172.22.62.31
  - ip: 172.22.62.36
  - ip: 172.22.62.40
  ports:
  - port: 1
    protocol: TCP

2、在glusterfs中创建一个名为“es1”的volume,(这步骤我们已经在之前通过heketi创建了)

3、pod中使用volume:

volumes:
- name: glusterfsvol
  glusterfs:
    endpoints: glusterfs-cluster  #glustfs-cluster
    path: es1  #Glusterfs volume name
    readOnly: true

5、云(端)存储:使用场景是k8s集群托管在云服务器上

  • awsEBS,Azure Disk,gcePersistentDisk……

6、PersistentVolumeClaim:

pvc与pv是一一对应关系。一旦pvc与pv绑定后,此时该pvc就可以定义为一个存储卷。可以实现对多种存储后端的抽象。

示例:glusterfs手动创建的pv

apiVersion: v1
kind: PersistentVolume
metadata:
  name: gluster-es1
  labels:
    name: gluster-es1
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: "test"
  glusterfs:
    endpoints: "glusterfs-cluster"
    path: "es1"
    readOnly: false
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: gluster-es2
  labels:
    name: gluster-es2
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: "test"
  glusterfs:
    endpoints: "glusterfs-cluster"
    path: "es2"
    readOnly: false
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: gluster-es3
  labels:
    name: gluster-es3
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: "test"
  glusterfs:
    endpoints: "glusterfs-cluster"
    path: "es3"
    readOnly: false

7、特殊的存储卷:

  • configMap
  • secret
  • downwardAPI 节点和pod的某些信息
apiVersion: v1
kind: Pod
metadata:
    name: test-env-pod
    namespace: kube-system
spec:
    containers:
    - name: test-env-pod
      image: busybox:latest
      command: ["/bin/sh", "-c", "env"]
      env:
      - name: POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
      - name: POD_NAMESPACE
        valueFrom:
          fieldRef:
            fieldPath: metadata.namespace
      - name: POD_IP
        valueFrom:
          fieldRef:
            fieldPath: status.podIP
#我们可以看到上面我们使用了一种新的方式来设置env的值:valueFrom。另外我们需要注意的是 POD 的 name 和 namespace 属于元数据,是在 POD 创建之前就已经定下来了的,所以我们使用 metata 获取就可以了,但是对于 POD 的 IP 则不一样,因为我们知道 POD IP 是不固定的,POD 重建了就变了,它属于状态数据,所以我们使用 status 去获取。

#除了使用fieldRef获取 POD 的基本信息外,还可以通过resourceFieldRef去获取容器的资源请求和资源限制信息。