kubernetes 如何在多个Pod上挂载同一个持久卷?

des4xlb0  于 2023-08-03  发布在  Kubernetes
关注(0)|答案(5)|浏览(196)

我有一个三节点的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_IN_USE_BY_ANOTHER_RESOURCE”。如何才能使所有三个副本都从同一卷读取相同的Web内容?

eh57zj3b

eh57zj3b1#

首先我想指出你们的配置中的一个基本缺陷。请注意,当您使用示例中定义的PersistentVolumeClaim时,您根本没有使用nginx-contentPersistentVolume。您可以通过运行以下命令轻松验证它:

kubectl get pv

字符串
在您的GKE集群上。您会注意到,除了您手动创建的nginx-contentPV之外,还有一个基于您应用的PVC自动配置的nginx-content
请注意,在PersistentVolumeClaim定义中,您明确地引用了default存储类,它与手动创建的PV无关。实际上,即使你完全省略注解:

annotations:
        volume.alpha.kubernetes.io/storage-class: default


它将以完全相同的方式工作,即无论如何都将使用default存储类。如果在GKE上使用默认存储类,则意味着GCE持久磁盘将用作您的卷调配器。你可以在这里阅读更多关于它:
gcePersistentDisk等卷实现是通过StorageClass资源配置的。GKE为您创建一个默认的StorageClass,它使用标准持久磁盘类型(ext4)。当PersistentVolumeClaim未指定StorageClassName时,将使用默认StorageClass。您可以将提供的默认StorageClass替换为您自己的。
但让我们继续讨论你面临的问题的解决方案。

解决方案:

首先,我想强调您不必使用任何类似NFS的文件系统来实现您的目标
如果您需要PersistentVolumeReadOnlyMany模式下可用,GCE持久磁盘是完全满足您需求的完美解决方案。
它可以安装在ro模式由多个Pods在同一时间和什么是更重要的多个Pods,调度在不同的GKEnodes。此外,它的配置非常简单,而且它可以在GKE上运行。
如果你想在ReadWriteMany模式下使用你的存储,我同意像NFS这样的东西可能是唯一的解决方案,因为GCE持久磁盘不提供这样的功能。
让我们仔细看看如何配置它。
我们需要从定义PVC开始。这一步实际上已经由你自己完成了,但你在接下来的步骤中迷失了一点。让我解释一下它是如何工作的。
以下配置是正确的(正如我提到的annotations部分可以省略):

# Request a persistent volume for web content
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nginx-content-claim
spec:
  accessModes: [ReadOnlyMany]
  resources:
    requests:
      storage: 5Gi


不过,我想补充一点重要的评论。你说:
即使我将卷创建为ReadOnlyMany,但在任何给定时间只有一个pod可以挂载卷。
其实你没有。我知道这看起来有点棘手,有点令人惊讶,但这并不是定义accessModes的方式。事实上,这是一个被广泛误解的概念。首先,你不能在PVC中定义访问模式,这是你想要的约束。支持的访问模式是特定存储类型的固有特性。它们已由存储提供商定义。
PVC定义中实际做的是请求支持特定访问模式的PV。请注意,它的形式是
a list**,这意味着您可以提供许多不同的访问模式,您希望您的PV支持。
基本上就像是在说:* “嘿!存储提供商!请给予我一个支持ReadOnlyMany模式的卷。"* 您要求的存储以满足您的要求。但是请记住,你可以得到比你要求的更多的东西。这也是我们在GCP中要求PV支持ReadOnlyMany模式时的场景。它为我们创建了一个PersistentVolume,满足了我们在accessModes部分列出的要求,但它也支持ReadWriteOnce模式。虽然我们没有要求支持ReadWriteOnce的东西,但你可能会同意我的看法,内置支持这两种模式的存储完全满足了我们对支持ReadOnlyMany的东西的要求。所以基本上这就是它的工作方式。
您的PV由GCP自动配置以响应您的PVC支持这两个accessModes,如果您在PodDeployment定义中没有明确指定您要以只读模式挂载,则默认情况下,它将以**读写 * 模式挂载。
您可以通过连接到能够成功挂载PersistentVolumePod来轻松验证它:

kubectl exec -ti pod-name -- /bin/bash


并试图在挂载的文件系统上写些东西。
您收到的错误消息:

"Error 400: RESOURCE_IN_USE_BY_ANOTHER_RESOURCE"


特别关注GCE持久磁盘,它已经由一个GKEnodeReadWriteOnce模式下挂载,而它不能由另一个node挂载,而你的Pods的其余部分则被安排在该Pods上。
如果你想在ReadOnlyMany模式下挂载,你需要在Deployment定义中通过在Pod's模板规范下的volumes部分添加readOnly: true语句来明确指定它,如下所示:

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

但是请记住,为了能够在readOnly模式下挂载它,首先我们需要用数据预先填充这样的卷。否则,您将看到另一个错误消息,说未格式化的卷不能在只读模式下挂载。
最简单的方法是创建一个Pod,该Pod只用于将已经上传到我们的某个GKE节点的数据复制到目标PV
请注意,预先填充PersistentVolume的数据可以通过许多不同的方式完成。您可以在这样的Pod中装载您将在Deployment中使用的PersistentVolume,并使用curlwget从某个外部位置获取数据,将其直接保存在您的目标PV上。你决定吧
在我的示例中,我展示了如何使用额外的本地卷来实现这一点,该卷允许我们装载到我们的Poddirectorypartitiondisk(在我的示例中,我使用位于我的一个GKE节点上的目录/var/tmp/test),在我们的一个kubernetes节点上可用。这是比hostPath灵活得多的解决方案,因为我们不需要关心调度这样的Pod到特定的节点,其中包含数据。PersistentVolume中已经定义了特定的
节点亲和性**规则,Pod会自动调度到特定节点上。
要创建它,我们需要三件事:
StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer


PersistentVolume定义:

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>


最后是PersistentVolumeClaim

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-storage


然后我们可以创建临时Pod,它只用于将数据从我们的GKE节点复制到我们的GCE持久磁盘

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


上面看到的路径并不重要。此Pod的任务只是允许我们将数据复制到目标PV。最终,我们的PV将安装在完全不同的路径上。
创建Pod并成功装载了两个卷后,我们可以通过运行以下命令来附加到它:

kubectl exec -ti my-pod -- /bin/bash


使用Pod简单地运行:

cp /mnt/source/* /mnt/destination/


就这样了。现在我们可以删除exit并删除临时Pod

kubectl delete pod mypod


一旦它消失了,我们就可以应用我们的Deployment,我们的PersistentVolume最终可以被位于各个**GKE节点上的所有PodsreadOnly模式挂载:

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


顺便说一句,如果你对你的Pods将只在一个特定的节点上调度的事实没有意见,你可以完全给予使用GCE持久磁盘,切换到上面提到的本地卷。这样,所有的Pods不仅可以读取它,还可以同时写入它。唯一需要注意的是,所有这些Pods都将在单个节点上运行。

e3bfsja2

e3bfsja22#

您可以使用类似NFS的文件系统来实现这一点。在Google Cloud上,Filestore是合适的产品(NFS托管)。您有一个教程here来实现您的配置

wbgh16ku

wbgh16ku3#

如果您想在不同节点之间共享卷并提供高度可扩展的解决方案,则需要使用读写多(RWX)类型的共享卷声明。就像使用NFS服务器。
您可以在此处了解如何部署NFS服务器:
https://www.shebanglabs.io/run-nfs-server-on-ubuntu-20-04/
然后,您可以按如下方式挂载卷(NFS服务器中的目录):
https://www.shebanglabs.io/how-to-set-up-read-write-many-rwx-persistent-volumes-with-nfs-on-kubernetes/
我已经使用这种方式在+8 k8s部署(+200 pod)之间提供共享静态内容,每月通过Nginx服务10亿次请求。而且它确实在NFS设置下工作得很好:)

cidc1ykv

cidc1ykv4#

Google提供了类似NFS的文件系统,称为Google Cloud Filestore。您可以将其安装在多个Pod上。

66bbxpm5

66bbxpm55#

我通过将部署策略从“滚动”更改为“重新创建”解决了此问题。

相关问题