1 卷挂载

Tip
  1. 容器中的磁盘文件是短暂的,{==容器重启后==},会以最干净的状态启动,之前的文件就会丢失
  2. 一个pod里有多个容器,可能需要共享一些文件的情况
  3. 数据需要持久化的情况
kubectl explain pod.spec.volumes
# 注意这个是 pod的
kubectl explain pod.spec.volumes.emptyDir
# 定义完 pod的后,我们需要定义pod中容器的 volume mount
kubectl explain pod.spec.containers.volumeMounts

官方文档

1.1 emptyDir

Tip
  1. 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除
  2. 容器崩溃重启并不会导致 Pod 被从节点上移除,因此容器崩溃期间 emptyDir 卷中的数据是安全
  3. 实际是将节点上的目录挂载到 Pod 中的不同容器的不同或相同位置,所以他们可以共享数据,因为实际读的是节点上的目录
# 创建一个pod ,里面有2个容器,一个写入数据到index.html
# 一个读取
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-empty-dir
  # namespace: test
spec:
  volumes:
  - name: html
    emptyDir: {}
    #emptyDir:
    #   medium: Memory
  containers: # 这里配置了2个容器,挂载到自己容器里不同的目录
  - name: nginx-c
    image: nginx:1.12-alpine
    volumeMounts:
    - name: html          # 这个必须是上面定义的volumes中的name
      mountPath: /usr/share/nginx/html # 挂载到容器里的哪个目录
  - name: make-page-c
    image: alpine
    volumeMounts:
    - name: html
      mountPath: /html # 挂载到不同的目录
    command: ["/bin/sh", "-c"]
    args:
    - while true; do
        echo $(hostname) $(date) >> /html/index.html;
        sleep 10;
      done
查看卷在节点中的位置
k get po -o wide # 先看在哪个节点
k get po pod-vol-empty-dir -o yaml
    uid: 8c809c4c-6b74-4088-b416-d7dd11743cdb
# 去对应的节点上
cd /var/lib/kubelet/pods/8c809c4c-6b74-4088-b416-d7dd11743cdb
tree -L 2
  .
  ├── containers
  │   ├── make-page-c
  │   └── nginx-c
  ├── etc-hosts
  ├── plugins
  │   └── kubernetes.io~empty-dir
  └── volumes
      ├── kubernetes.io~empty-dir
      └── kubernetes.io~projected
tree volumes/kubernetes.io~empty-dir
  volumes/kubernetes.io~empty-dir
  └── html
      └── index.html
# 在 这个节点上的html目录增加文件, 2个容器都能看到

1.2 hostPath

Tip

将主机节点文件系统上的文件或目录挂载到你的 Pod 中

hostPath

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: nginx:1.14.2
    name: test-container
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # 宿主上目录位置
      path: /nginx_html
      #  如果宿主机上没有 该目录则创建
      type: DirectoryOrCreate
k get po -o wide
  test-pd             1/1     Running   0          78s   10.244.1.45   node1
# 到相应的节点上
echo hello > /nginx_html/index.html
curl 10.244.1.45 # 显示hello

1.3 nfs

Tip

nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享

服务端安装
#在一台主机上安装 ,这里我们直接用一个节点来. (当然如果你服务器多.就用一个新的.)
# 这里我们用node1节点
yum install nfs-utils rpcbind -y
systemctl start nfs-server
# 查看当前nf服务所支持的版本
cat /proc/fs/nfsd/versions

# 1. 创建一个你要共享的目录
mkdir -p /data/nfs
chown 755 /data/nfs
#nfsnobody 安装启动nfs服务后有的一个用户
chown -R nfsnobody.nfsnobody /data/nfs
systemctl start nfs 
systemctl start  rpcbind

# *号 位可以设置成ip段 192.168.124.0/24 表示这些ip段的才可以访问
echo "/data/nfs *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
# 这个不行...
#echo "/data/nfs *(rw,sync,all_squash)" >> /etc/exports
exportfs -r # 更新配置,重新读取配置
exportfs #显示共享了哪些目录
systemctl restart nfs
systemctl restart  rpcbind
echo "hello" > /data/nfs/index.html
其他节点上挂载
# node1 的ip地址 192.168.66.110
# /nfs 你本地想要挂载到的目录, 随便创建一个, 需要有
mkdir /nfs
mount -t nfs -o rw,nosuid,noexec,nodev 192.168.66.110:/data/nfs /nfs
ll /nfs # 能看到index.html
pod-vol-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-nfs
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: nginx-data
  volumes:
    - name: nginx-data
      nfs:
        server: 192.168.66.110 # 就是nfs服务端的ip
        path: /data/nfs  #nfs服务器里 共享的目录
k create -f  pod-vol-nfs.yaml
# curl pod ip 可以看到hello

1.4 downwardAPI

2 投射卷

官方文档

3 pv pvc

3.1 概念

Diagram
Tip
  • 一个pod 可以关联多个pvc, 一个pvc 只能绑定一个pv, 一个pv 可以被多个pvc 绑定

3.2 创建pv

pv-nfs-1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-1
  labels:
    release: stable
spec:
1  capacity:
2    storage: 100Mi
9  volumeMode: Filesystem
3  accessModes:
    - ReadWriteMany
4  # storageClassName: nfsslow
5  persistentVolumeReclaimPolicy: Retain
6  nfs:
7    path:  "/data/nfs"
8    server: 192.168.66.110
1
容量配置,并不是说你设置多少,就只能给这么多, 这需要你的后端存储的支持
2
Gi表示以1024 来算的, 如果写5G表示以1000来算
3
该PV的访问模式, ReadWriteMany(表示可以被多个node节点读写,有些支持,有些不支持) ReadWriteOnce 等
4
pv的类型,多个不同的pv可以有相同的类型,用于PVC 想要绑定什么类型的pv,pvc设置的时候也有这个字段,写上它想要的pv类型
5
pv回收策略
6
pv 申请的空间从哪里来
7
NFS上的共享目录
8
NFS的IP地址
9
卷的模式, 目前支持Filesystem(文件系统)和Block(块),其中Block类型需要后端存储支持,默认为文件系统
pv的回收策略
策略 描述
Retain 默认就是Retain,就是说删除pvc的时候,他绑定的pv保留,数据也保留
Recycle 表示pv里数据删除,但是pv还是保留的.
k8s会创建一个pod .里面有rm 命令 来进行对数据删除
delete 删除pvc时, 同时删除pv (deprecated: 表示强烈的不推荐)
pv 的状态
pv 的STATUS 描述
Available 可用,没有被PVC绑定的空闲资源
Bound 已绑定,已经被PVC绑定
Released 已释放,PVC被删除,但是资源还未被重新使用
Failed 失败,自动回收失败
k get pv
NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv-nfs-1   100Mi      RWX            Retain           Available                                   1s

3.3 创建pvc

pvc-nfs-1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-nfs-1
spec:
  accessModes:
1    - ReadWriteMany
  volumeMode: Filesystem
  resources:
    requests:
2      storage: 100Mi
3  selector:
    matchLabels:
      release: "stable"
1
注意 这个要与 pv一致
2
对应的pv 必须大于等于这个大小
3
标签过滤选择哪个 pv,也可以不用写,这样pvc选择的时候可能就是看需要的容量了
# pvc 的状态 Bound
k get pvc
# 可以看到 pvc对象的volumeName字段 设置为了pv的名字pv-nfs-1,这就是表示pvc绑定了pv
k get pvc pvc-nfs-1 -o yaml  |grep volumeName
# pv 的状态变成Bound, CLAIM 显示成 pvc的name
k get pv
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
pv-nfs-1       100Mi      RWX            Retain          Bound    default/pvc-nfs-1                           101s

3.4 创建pod绑定pvc

apiVersion: v1
kind: Pod
metadata:
  name: pod-pv-pvc
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    volumeMounts:
    - name: ng-data
      mountPath: /usr/share/nginx/html
  volumes:
  - name: ng-data
    persistentVolumeClaim: # 指定pvc的name
      claimName: pvc-nfs-1

3.5 storageClass

官方文档

Tip

storageClass 没有命名空间一说.
只能有一个默认的 sc, 如果设置多个, 那么创建pvc 未指定 storageClassName 会报错 Internal error occurred: 2 default StorageClasses were found

k get storageclass -A
k get sc -A 

我们前面创建时没有指定storageClassName ,实际上会设置为默认的那个sc. 除非你设置为 “” 空字符串

下面的显示是举个例子, 默认的会显示 (default)
NAME                 PROVISIONER               AGE
standard (default)   kubernetes.io/gce-pd      1d
gold                 kubernetes.io/gce-pd      1d
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: example-nfs
provisioner: example.com/external-nfs
parameters:
  server: nfs-server.example.com
  path: /share
  readOnly: "false"
# 正常情况下你要创建一个能用的 sc, provisioner 要有,这里我做个测试.
# 创建完比后, 设置为默认
k annotate sc example-nfs storageclass.kubernetes.io/is-default-class=true
pvc-nfs-2.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-nfs-2
spec:
  accessModes:
    - ReadWriteMany # (1)
  volumeMode: Filesystem
  resources:
    requests:
      storage: 500Mi
k create -f pvc-nfs-2.yaml
k get pvc #发现是pending 状态
# storageClassName: example-nfs pv的storageClassName已经被设置为默认的了.
# 因为这个 sc 不ok, 所以没有动态创建pv
k get pvc pvc-nfs-2 -o yaml 
pv-2.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-0002
  labels:
    release: stable
spec:
  capacity: #
    storage: 500Mi #
  volumeMode: Filesystem
  accessModes: #
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: example-nfs
  nfs:
    path:  "/data/nfs/abc"
    server: 192.168.66.110

创建这个pv后, 我们发现上面的 pvc 与pv 绑定了, 说明有这个storageClassName的pv 的应该优先级更高

3.5.1 nfs

nfs storage class

NFS subdir 外部驱动

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
# 先 pull  看是否能下载下来,  / 左边是 上面添加的repo名
helm pull nfs-subdir-external-provisioner/nfs-subdir-external-provisioner
# 会在当前目录下 看到  下载的文件 nfs-subdir-external-provisioner-4.0.18.tgz
tar xf nfs-subdir-external-provisioner-4.0.18.tgz
cd nfs-subdir-external-provisioner
tree
  .
  ├── Chart.yaml
  ├── ci
  │   └── test-values.yaml
  ├── README.md
  ├── templates
  │   ├── clusterrolebinding.yaml
  │   ├── clusterrole.yaml
  │   ├── deployment.yaml
  │   ├── _helpers.tpl
  │   ├── persistentvolumeclaim.yaml
  │   ├── persistentvolume.yaml
  │   ├── poddisruptionbudget.yaml
  │   ├── podsecuritypolicy.yaml
  │   ├── rolebinding.yaml
  │   ├── role.yaml
  │   ├── serviceaccount.yaml
  │   └── storageclass.yaml
  └── values.yaml  #修改这个文件

自己构建nfs-subdir-external-provisioner镜像

value.yaml 部分要修改的内容
replicaCount: 1
strategyType: Recreate

image:
1  repository: registry.cn-hangzhou.aliyuncs.com/your-namespace/nfs-subdir-external-provisioner
2  tag: v4.0.18
  pullPolicy: IfNotPresent
imagePullSecrets: [
3  {"name":"regcred"}
]

nfs:
  server: 192.168.66.110
  path: /data/nfs
  mountOptions:
  volumeName: nfs-subdir-external-provisioner-root
  # Reclaim policy for the main nfs volume
  reclaimPolicy: Retain
1
自己构建的镜像地址
2
镜像版本
3
docker login aliyun 后创建的secret 名
# 上面使用了自己的aliyun 镜像仓库
docker login --username=your-name registry.cn-hangzhou.aliyuncs.com
kubectl create secret generic regcred \
    --from-file=.dockerconfigjson=/root/.docker/config.json  \
    --type=kubernetes.io/dockerconfigjson  \
    --namespace=kube-system
# 安装
helm install nfs-subdir-external-provisioner . -n kube-system --debug
# 查看
ks get po
    nfs-subdir-external-provisioner-6db4fd89d9-nwqfv   1/1     Running   0  17h
helm list -A
k get sc
k get sc nfs-client -o yaml #(1)
storageClass 的yaml 里provisioner, 可以这样获取
ks get po nfs-subdir-external-provisioner-6db4fd89d9-nwqfv -oyaml |grep PROVISIONER_NAME -A 1
ks describe po nfs-subdir-external-provisioner-6db4fd89d9-nwqfv  |grep PROVISIONER_NAME -A 1
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    meta.helm.sh/release-name: nfs-subdir-external-provisioner
    meta.helm.sh/release-namespace: kube-system
  creationTimestamp: "2023-07-19T08:37:44Z"
  labels:
    app: nfs-subdir-external-provisioner
    app.kubernetes.io/managed-by: Helm
    chart: nfs-subdir-external-provisioner-4.0.18
    heritage: Helm
    release: nfs-subdir-external-provisioner
  name: nfs-client
  resourceVersion: "1055715"
  uid: 5ae9beff-7646-4022-8acc-52396ea9723b
parameters:
  archiveOnDelete: "true"
provisioner: cluster.local/nfs-subdir-external-provisioner
reclaimPolicy: Delete
pod-pvc-with-sc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-nfs-with-sc
spec:
  accessModes:
    - ReadWriteMany
  volumeMode: Filesystem
  resources:
    requests:
      storage: 200Mi
1  storageClassName: nfs-client
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-pv-pvc-with-sc
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    volumeMounts:
    - name: ng-data
      mountPath: /usr/share/nginx/html
  volumes:
  - name: ng-data
    persistentVolumeClaim: # 指定pvc的name
      claimName: pvc-nfs-with-sc
1
前面已经自动创建了sc, 当然我们可以自己手动创建一个.
利用这个sc对应的provisioner组件自动创建pv
还有如果没有指定的sc不存在, 那么它会找对应pv,会与有设置这个storageClassName的pv进行绑定
k create -f pod-pvc-with-sc.yaml
k get pvc
NAME              STATUS   VOLUME                                     CAPACITY  ACCESS MODES  STORAGECLASS
pvc-nfs-with-sc   Bound    pvc-c5aebbf9-cd9e-43ba-866c-33bef06b4c2a   200Mi     RWX           nfs-client
k get pv
NAME                                       CAPACITY STATUS   CLAIM                     STORAGECLASS
pvc-c5aebbf9-cd9e-43ba-866c-33bef06b4c2a   200Mi    Bound    default/pvc-nfs-with-sc   nfs-client
# 获取这个pv 使用的 nfs 共享目录
k get pv pvc-c5aebbf9-cd9e-43ba-866c-33bef06b4c2a -o yaml |grep nfs: -A 3
# 最后到nfs服务器node1 上 ,看到 共享目录有带pvc的名字
    /data/nfs/default-pvc-nfs-with-sc-pvc-c5aebbf9-cd9e-43ba-866c-33bef06b4c2a

3.6 问题

3.6.1 pvc 处于pending

  • PVC的空间申请大小大于PV的大小
  • PVC的StorageClassName没有和PV的一致
  • PVC的accessModes和PV的不一致

3.6.2 挂载PVC的Pod一直处于Pending

  • PVC没有创建成功/PVC不存在
  • PVC和Pod不在同一个Namespace
Back to top