Kubernetes 如何在多个POD上装载相同的持久卷?

Kubernetes 如何在多个POD上装载相同的持久卷?,kubernetes,google-cloud-platform,google-kubernetes-engine,persistent-volumes,persistent-volume-claims,Kubernetes,Google Cloud Platform,Google Kubernetes Engine,Persistent Volumes,Persistent Volume Claims,我有一个三节点的GCE集群和一个带有三个副本的单pod GKE部署。我创建了PV和PVC,如下所示: # Create a persistent volume for web content apiVersion: v1 kind: PersistentVolume metadata: name: nginx-content labels: type: local spec: capacity: storage: 5Gi accessModes: - Rea

我有一个三节点的GCE集群和一个带有三个副本的单pod GKE部署。我创建了PV和PVC,如下所示:

# Create a persistent volume for web content
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nginx-content
  labels:
    type: local
spec:
  capacity:
    storage: 5Gi
  accessModes:
   - ReadOnlyMany
  hostPath:
    path: "/usr/share/nginx/html"
--
# Request a persistent volume for web content
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nginx-content-claim
  annotations:
    volume.alpha.kubernetes.io/storage-class: default
spec:
  accessModes: [ReadOnlyMany]
  resources:
    requests:
      storage: 5Gi
它们在容器等级库中被引用,如下所示:

    spec:
      containers:
      - image: launcher.gcr.io/google/nginx1
        name: nginx-container
        volumeMounts:
          - name: nginx-content
            mountPath: /usr/share/nginx/html
        ports:
          - containerPort: 80
      volumes:
      - name: nginx-content
        persistentVolumeClaim:
          claimName: nginx-content-claim

尽管我创建的卷是ReadOnlyMany,但在任何给定时间只有一个pod可以装载卷。其余部分给出“错误400:RESOURCE\u IN\u由另一个\u资源使用”。如何使这三个副本从同一卷读取相同的web内容?

您可以使用类似NFS的文件系统实现这一点。在谷歌云上,Filestore是这方面的合适产品(NFS管理)。您有一个实现配置的教程

首先,我想指出配置中的一个基本问题。请注意,当使用示例中定义的
PersistentVolumeClaim
时,根本不使用
nginx内容。您可以通过运行以下命令轻松验证:

kubectl get pv
kubectl exec -ti my-pod -- /bin/bash
在您的GKE集群上。您会注意到,除了手动创建的
nginx内容
PV
,还有一个内容是根据您应用的
PVC
自动设置的

请注意,在您的
PersistentVolumeClaim
定义中,您明确地引用了
默认的
存储类,它与手动创建的
PV
无关。实际上,即使您完全忽略了注释:

annotations:
        volume.alpha.kubernetes.io/storage-class: default
它将以完全相同的方式工作,即无论如何都将使用
default
存储类。在GKE上使用默认存储类意味着GCE持久磁盘将用作卷资源调配器。您可以阅读更多关于它的信息:

已配置卷实现,如gcePersistentDisk 通过StorageClass资源。GKE为创建默认的StorageClass 您可以选择使用标准持久磁盘类型(ext4)的磁盘。默认值 当PersistentVolumeClaim未指定 StorageClassName。您可以替换提供的默认StorageClass 用你自己的

但是,让我们继续讨论您面临的问题的解决方案

解决方案: 首先,我想强调的是,您不必使用任何类似NFS的文件系统来实现您的目标

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/mnt/source"
        name: mypd
      - mountPath: "/mnt/destination"
        name: nginx-content
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim
    - name: nginx-content
      persistentVolumeClaim:
        claimName: nginx-content-claim
如果您需要
PersistentVolume
ReadOnlyMany
模式下可用,GCE Persistent Disk是完全满足您需求的完美解决方案

它可以由多个
pod
同时以
ro
模式安装,更重要的是,可以由多个
pod
安装在不同的GKE
节点上。此外,它的配置非常简单,可以在开箱即用的GKE上运行

如果您想在
ReadWriteMany
模式下使用存储,我同意像NFS这样的东西可能是唯一的解决方案,因为GCE持久磁盘不提供这种功能

让我们仔细看看如何配置它

我们需要从定义我们的
PVC
开始。这一步实际上已经由你自己完成了,但你在进一步的步骤中迷失了一点。让我解释一下它是如何工作的

以下配置是正确的(如我所述,
注释
部分可以省略):

不过,我想补充一点重要的意见。你说:

尽管我创建的卷是现成的,但只有一个pod可以 在任何给定时间装入卷

嗯,实际上你没有。我知道这看起来有点棘手,也有点令人惊讶,但这并不是定义
accessModes
的真正方式。事实上,这是一个被广泛误解的概念。首先,您不能在
PVC
中定义访问模式,这意味着要在那里设置所需的约束。支持的访问模式是特定存储类型的固有特征。它们已由存储提供程序定义

您在
PVC
定义中实际执行的操作是请求支持特定访问模式的
PV
。请注意,它是一种列表形式,这意味着您可以提供许多不同的访问模式,您希望您的
PV
支持这些模式

基本上就像说:“嘿!存储提供商!给我一个支持
ReadOnlyMany
模式的卷。”你这样问是为了一个能满足你需求的存储。但是请记住,你可以得到比你要求的更多的东西。这也是我们在请求在GCP中支持
ReadOnlyMany
模式的
PV
时的场景。它为我们创建了一个
PersistentVolume
,它满足了我们在
accessModes
部分中列出的要求,但它还支持
ReadWriteOnce
模式。虽然我们没有要求提供同时支持
ReadWriteOnce
的存储,但您可能会同意我的观点,即内置支持这两种模式的存储完全满足了我们对支持
ReadOnlyMany
的存储的要求。所以基本上这就是它的工作方式

GCP为响应您的
PVC
而自动配置的
PV
支持这两种
accessmode
,如果您没有在
Pod
Deployment
定义中明确指定要在只读模式下装载它,默认情况下,它以读写模式装入

您可以通过连接到能够成功装载
PersistentVolume的
Pod
来轻松验证它:

kubectl exec -ti pod-name -- /bin/bash
并试图在挂载的文件系统上写一些东西

您将收到以下错误消息:

"Error 400: RESOURCE_IN_USE_BY_ANOTHER_RESOURCE"
特别关注已由一个GKE安装的GCE持久磁盘
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /var/tmp/test
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - <gke-node-name>
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-storage
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/mnt/source"
        name: mypd
      - mountPath: "/mnt/destination"
        name: nginx-content
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim
    - name: nginx-content
      persistentVolumeClaim:
        claimName: nginx-content-claim
kubectl exec -ti my-pod -- /bin/bash
cp /mnt/source/* /mnt/destination/
kubectl delete pod mypod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: nginx-content
      volumes:
      - name: nginx-content
        persistentVolumeClaim:
          claimName: nginx-content-claim
          readOnly: true