Kubernetes学习之存储卷

一、存储卷概述
  应用程序在处理请求时,可根据其对当前请求的处理是否受影响与此前的请求,将应用划分为有状态和无状态应用两种。微服务体系中,各种应用均被拆分了众多微服务或更小的应用模块,因此往往会存在为数不少的有状态应用,于是数据持久化几乎是必然只需。
  Kubernetes提供的存储卷(volume)隶属于Pod资源,共享于Pod内的所有容器,可用于在容器的文件系统之外存储应用程序的相关数据,甚至还可以独立于Pod的生命周期之外实现数据持久化。
  Pod本身具有生命周期,故其内部运行的容器及其相关数据自身均无法持久存在。Docker支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间中,它们可以是节点文件系统或网络文件系统之上的存储系统,不过,其存储卷是与Pod资源绑定而非容器。简单来说,存储卷是定义在Pod资源之上的、可被其内部的所有容器挂载的共享目录,它关联至某外部的存储设备之上的存储空间,从而独立于容器自身的文件系统,而数据是否具有持久化能力则取决于存储卷自身是否支持持久机制。
Kubernetes学习之存储卷_第1张图片

Kubernetes支持的存储卷类型

Kubernetes学习之存储卷_第2张图片

二、临时存储卷
  Kubernetes支持存储卷类型,emptyDir存储卷的生命周期与其所属的Pod对象相同,它无法脱离Pod对象的生命周期提供数据存储功能,因此emptyDir通常仅用于数据缓存或临时存储。不过,基于emptyDir构建的gitRepo存储卷可以在Pod对象的生命周期起始时从相应的Git仓库中复制相应的数据文件到底层的emptrDir中,从而使得它具有一定意义上的持久性;但是,自Kubernetes 1.12版本起,girRepo存储卷已被废弃,所以不再进行说明。
  emptyDir存储卷是Pod对象生命周期中的一个临时存储目录,类似于Docker上的"docker挂载卷",在Pod对象启动时即被创建,而在Pod对象被移除时会被一并删除。不具持久能力的emptyDir存储卷只能用于某些特殊场景。例如,同一Pod内的多个容器间文件共享,或者作为容器数据的临时存储目录用于数据缓存系统等等。
  emptyDir存储卷则定义于.spec.volumes.emptyDir嵌套字段中,可用字段主要包含两个,具体如下:
  medium:此目录所在的存储介质的类型,可取值为"default"或"Memory",默认为default,表示使用节点的默认存储介质;"Memory"表示使用基于RAM的临时文件系统tmpfs,空间受限于内存,但是性能非常好,通过用于为容器中的应用提供缓存空间。
  sizeLimit:当前存储卷的空间限额,默认值为nil,表示不限制;不过在medium字段值为"Memory"时建议务必使用此限额。

1)编写emptyDir的yaml文件

]# cat emptydir.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: vol-emptydir-pod
spec:
  volumes:
  - name: html
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx:1.12-alpine
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  - name: pagegen
    image: alpine
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /html
    command: ["/bin/sh", "-c"]
    args:
    - while true; do
        echo $(homename) $(date) >> /html/index.html;
        sleep 10;
      done

]# kubectl apply -f emptydir.yaml 
pod/vol-emptydir-pod created

2)查看Pod详细信息

]# kubectl get pods -o wide 
NAME               READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
vol-emptydir-pod   2/2     Running   0          4s    10.244.1.95   node1   <none>           <none>

]# kubectl describe pods vol-emptydir-pod 
Name:         vol-emptydir-pod
Namespace:    default
Priority:     0
Node:         node1/172.16.2.101
Start Time:   Thu, 27 Aug 2020 19:18:58 +0800
Labels:       <none>
Annotations:  Status:  Running
IP:           10.244.1.95
IPs:
  IP:  10.244.1.95
Containers:
  nginx:
    Container ID:   docker://3f912041344a89bc846b0b0b669e7a8f5a23b121d07600a79f8eb3deaea937ec
    Image:          nginx:1.12-alpine
    Image ID:       docker-pullable://nginx@sha256:db5acc22920799fe387a903437eb89387607e5b3f63cf0f4472ac182d7bad644
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Thu, 27 Aug 2020 19:18:59 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from html (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
  pagegen:
    Container ID:  docker://937feb81e06f08dfd8b6cc4270529c3a7bd103d6357d51c198704656c5c21203
    Image:         alpine
    Image ID:      docker-pullable://alpine@sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321
    Port:          <none>
    Host Port:     <none>
    Command:
      /bin/sh
      -c
    Args:
      while true; do echo $(homename) $(date) >> /html/index.html; sleep 10; done
    State:          Running
      Started:      Thu, 27 Aug 2020 19:18:59 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /html from html (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  html:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  <unset>
  default-token-47pch:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-47pch
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age        From               Message
  ----    ------     ----       ----               -------
  Normal  Scheduled  <unknown>  default-scheduler  Successfully assigned default/vol-emptydir-pod to node1
  Normal  Pulled     <invalid>  kubelet, node1     Container image "nginx:1.12-alpine" already present on machine
  Normal  Created    <invalid>  kubelet, node1     Created container nginx
  Normal  Started    <invalid>  kubelet, node1     Started container nginx
  Normal  Pulled     <invalid>  kubelet, node1     Container image "alpine" already present on machine
  Normal  Created    <invalid>  kubelet, node1     Created container pagegen
  Normal  Started    <invalid>  kubelet, node1     Started container pagegen

3)访问Pod

]# curl  10.244.1.95
Thu Aug 27 11:18:59 UTC 2020
Thu Aug 27 11:19:09 UTC 2020

三、节点存储卷hostPath
  hostPath类型的存储卷是指将工作节点上某文件系统的目录或文件挂载于Pod中的一种存储卷,它可以独立于Pod资源的生命周期,因而具有持久性。但它是工作节点本地的存储空间,仅适用于特定情况下的存储卷要求,例如,将工作节点上的文件系统关联为Pod的存储卷,从而使得容器访问节点文件系统上的数据。这一点在运行有管理任务的系统级Pod资源需要访问节点上的文件时尤为有用。
  配置hostPath存储卷的嵌套字段共有两个:一个是用于指定工作节点上的目录路径的必选字段path;另一个是指定存储卷类型的type,它支持使用的存储卷类型包含如下几种:
  DirectoryOrCreate:指定的路径不存在时自动将其创建为权限是0755的空目录,属主属组均为kubelet。
  Directory:必须存在的目录路径。
  FileOrCreate:指定的路径不存在时自动将其创建为权限是0644的空文件,属主和属组同是kubelet。
  File:必须是存在的文件路径。
  Socket:必须存在的Socket文件路径。
  CharDevice:必须存在的字符设备文件路径。
  BlockDevice:必须存在的块设备文件路径。

1)编写hostPath存储卷yaml文件

]# cat hostPath.yaml 
apiVersion: v1
kind: Pod
metadata: 
  name: vol-hostpath-pod
spec:
  containers:
  - name: filebeat
    image: ikubernetes/filebeat:5.6.7-alpine
    imagePullPolicy: IfNotPresent
    env:
    - name: REDIS_HOST
      value: redis.ilinux.io:6379
    - name: LOG_LEVEL
      value: info
    volumeMounts:
    - name: varlog
      mountPath: /var/log
    - name: socket
      mountPath: /var/run/docker.sock
    - name: varlibdockercontainers
      mountPath: /var/lib/docker/containers
      readOnly: true
  terminationGracePeriodSeconds: 30
  volumes:
  - name: varlog
    hostPath:
      path: /var/log
  - name: varlibdockercontainers
    hostPath:
      path: /var/lib/docker/containers
  - name: socket
    hostPath:
      path: /var/run/docker.sock

]# kubectl apply -f hostPath.yaml 
pod/vol-hostpath-pod created

2)查看Pod详细信息

]# kubectl get pods -o wide 
NAME               READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
vol-hostpath-pod   1/1     Running   0          10s   10.244.1.96   node1   <none>           <none>

]# kubectl describe pods vol-hostpath-pod 
Name:         vol-hostpath-pod
Namespace:    default
Priority:     0
Node:         node1/172.16.2.101
Start Time:   Thu, 27 Aug 2020 20:03:24 +0800
Labels:       <none>
Annotations:  Status:  Running
IP:           10.244.1.96
IPs:
  IP:  10.244.1.96
Containers:
  filebeat:
    Container ID:   docker://59adc40615571579a6135abaac5dcc39ef9ee711d70427f5432fbb7329172c24
    Image:          ikubernetes/filebeat:5.6.7-alpine
    Image ID:       docker-pullable://ikubernetes/filebeat@sha256:3957f67b612aa8628f643f8ede02b71bfbabf34892ef136f1e5ee18bbc0775aa
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Thu, 27 Aug 2020 20:03:25 +0800
    Ready:          True
    Restart Count:  0
    Environment:
      REDIS_HOST:  redis.ilinux.io:6379
      LOG_LEVEL:   info
    Mounts:
      /var/lib/docker/containers from varlibdockercontainers (ro)
      /var/log from varlog (rw)
      /var/run/docker.sock from socket (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  varlog:
    Type:          HostPath (bare host directory volume)
    Path:          /var/log
    HostPathType:  
  varlibdockercontainers:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/docker/containers
    HostPathType:  
  socket:
    Type:          HostPath (bare host directory volume)
    Path:          /var/run/docker.sock
    HostPathType:  
  default-token-47pch:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-47pch
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age        From               Message
  ----    ------     ----       ----               -------
  Normal  Scheduled  <unknown>  default-scheduler  Successfully assigned default/vol-hostpath-pod to node1
  Normal  Pulled     <invalid>  kubelet, node1     Container image "ikubernetes/filebeat:5.6.7-alpine" already present on machine
  Normal  Created    <invalid>  kubelet, node1     Created container filebeat
  Normal  Started    <invalid>  kubelet, node1     Started container filebeat

.
  使用hostPath存储卷时需要注意,不同的节点上的文件获取并不完全相同,于是,那些要求事先必须存在的目录或文件的满足状态也可能会有所不同;另外,基于资源可用状态的调度器调度Pod时,hostPath资源的可用性不会被考虑在内;再者,在节点中创建的文件或目录默认仅有root可写,若是期望容器内的进程拥有写权限,则要么将它运行为特权容器,要么修改节点上目录路径的权限。
  
  那些并非执行系统管理级任务的且不受控于DaemonSet控制器的无状态应用在Pod资源被重新调度至其他节点运行时,此前创建的文件或目录大多不会存在。因此,hostPath存储卷虽然能持久保存数据,但是对于被调度器按需调度的应用来说并不适用,这时则需要用到的是独立于集群节点的持久性存储卷,即网络存储卷。

四、NFS网络存储卷
  NFS即网络文件系统(Network File System),它是一种分布式文件协议;Kubernetes的NFS文件系统用于将某事先存在的NFS服务器上导出(export)的存储空间挂载到Pod中以供容器使用。与emptyDir不同的是,NFS存储卷在Pod对象终止后仅是被卸载而非删除。另外,NFS是文件系统级别的共享服务,它支持同时存在的多路挂载请求。定义NFS存储卷时,常用到以下字段:
  server:NFS服务器的IP地址或主机名,必选字段。
  path>:NFS服务器导出(共享)的文件系统路径,必选字段。
  readOnly:是否以只读方式挂载,默认为False。

1)编写NFS存储卷的yaml文件

]# cat nfs.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: vol-nfs-pod
  labels:
    app: redis
spec:
  containers:
  - name: redis
    image: redis:4-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 6379
      name: redisport
    volumeMounts:
    - mountPath: /data
      name: redisdata
  volumes:
    - name: redisdata
      nfs:
        server: 172.16.2.250
        path: /data/redis
        readOnly: false

]# kubectl apply -f nfs.yaml 
pod/vol-nfs-pod created

2)查看Pod详细信息

]# kubectl get pods -o wide 
NAME          READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
vol-nfs-pod   1/1     Running   0          3s    10.244.1.101   node1   <none>           <none>

]# kubectl describe pods vol-nfs-pod
Name:         vol-nfs-pod
Namespace:    default
Priority:     0
Node:         node1/172.16.2.101
Start Time:   Fri, 28 Aug 2020 09:40:12 +0800
Labels:       app=redis
Annotations:  Status:  Running
IP:           10.244.1.101
IPs:
  IP:  10.244.1.101
Containers:
  redis:
    Container ID:   docker://aee82f9c44a7141548e4f5524304ccc5ab92961a1f72925747da59952bc16bb8
    Image:          redis:4-alpine
    Image ID:       docker-pullable://redis@sha256:aaf7c123077a5e45ab2328b5ef7e201b5720616efac498d55e65a7afbb96ae20
    Port:           6379/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 28 Aug 2020 09:40:14 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /data from redisdata (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  redisdata:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    172.16.2.250
    Path:      /data/redis
    ReadOnly:  false
  default-token-47pch:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-47pch
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age        From               Message
  ----    ------     ----       ----               -------
  Normal  Scheduled  <unknown>  default-scheduler  Successfully assigned default/vol-nfs-pod to node1
  Normal  Pulled     <invalid>  kubelet, node1     Container image "redis:4-alpine" already present on machine
  Normal  Created    <invalid>  kubelet, node1     Created container redis
  Normal  Started    <invalid>  kubelet, node1     Started container redis

3)进入容器对Pod做数据持久化

]# kubectl exec -it vol-nfs-pod redis-cli 
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
127.0.0.1:6379> set myname "Kubernetes"
OK
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> exit

4)删除容器再重新创建查看数据是否存在

]# kubectl delete -f nfs.yaml  
pod "vol-nfs-pod" deleted

]# kubectl apply -f nfs.yaml 
pod/vol-nfs-pod created

# kubectl exec -it vol-nfs-pod redis-cli 
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
127.0.0.1:6379> KEYS *
1) "myname"
127.0.0.1:6379> GET myname
"Kubernetes"
127.0.0.1:6379> exit

.
  从上面的命令结果中可以看出,此前创建的键myname及其数据在Pod资源重建之后依然存在,这表明在删除Pod资源时,其关联的外部存储卷并不会被一同删除。如果需要清除此类的数据,需要用户通过存储系统的管理接口手动进行。
  
5)报错及解决方法

如果启动容器时容器状态一直为"CrashLoopBackOff"状态,查看日志为如下报错:

]# kubectl logs vol-nfs-pod
chown: .: Operation not permitted

请在NFS服务器上的exports文件中添加如下挂载选项即可解决

/data/redis   *(rw,sync,no_root_squash)

你可能感兴趣的:(Kubernetes学习)