如何在多个 Pod 上安装相同的持久卷?

asd*_*gav 4 google-cloud-platform kubernetes 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:
   - 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
Run Code Online (Sandbox Code Playgroud)

它们在容器规范中被引用,如下所示:

    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
Run Code Online (Sandbox Code Playgroud)

尽管我将卷创建为 ReadOnlyMany,但在任何给定时间只有一个 pod 可以挂载该卷。其余的给出“错误 400:RESOURCE_IN_USE_BY_ANOTHER_RESOURCE”。我怎样才能让所有三个副本从同一卷读取相同的 Web 内容?

mar*_*rio 15

首先,我想指出您的配置中的一个基本差异。请注意,当您PersistentVolumeClaim在示例中使用定义时,您根本不使用您nginx-content PersistentVolume的。您可以通过运行轻松验证它:

kubectl get pv
Run Code Online (Sandbox Code Playgroud)

在您的GKE 集群上。您会注意到,除了您手动创建的nginx-content PV,还有另一个是根据PVC您应用的自动配置的。

请注意,在您的PersistentVolumeClaim定义中,您明确引用了default与您手动创建的PV. 实际上即使你完全省略注释:

annotations:
        volume.alpha.kubernetes.io/storage-class: default
Run Code Online (Sandbox Code Playgroud)

它将以完全相同的方式工作,即default无论如何都会使用存储类。在GKE上使用默认存储类意味着GCE Persistent Disk将用作您的卷配置器。您可以在此处阅读更多相关信息:

诸如 gcePersistentDisk 之类的卷实现是通过 StorageClass 资源配置的。GKE 会为您创建一个默认的 StorageClass,它使用标准的永久性磁盘类型 (ext4)。当 PersistentVolumeClaim 未指定 StorageClassName 时,将使用默认 StorageClass。您可以将提供的默认 StorageClass 替换为您自己的。

但是让我们继续解决您面临的问题。

解决方案:

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

如果您需要PersistentVolumeReadOnlyMany模式下可用,GCE Persistent Disk是完全满足您要求的完美解决方案。

它可以ro由许多人Pods同时以模式挂载,更重要的是许多人Pods在不同的GKE 上安排nodes。此外,它的配置非常简单,并且可以在GKE 上开箱即用。

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

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

我们需要从定义我们的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
Run Code Online (Sandbox Code Playgroud)

但是,我想对此添加一项重要评论。你说:

尽管我将卷创建为 ReadOnlyMany,但在任何给定时间只有一个 pod 可以挂载该卷。

好吧,实际上你没有。我知道这看起来有点棘手,有点令人惊讶,但这并不是定义accessModes真正起作用的方式。事实上,这是一个被广泛误解的概念。首先,您不能PVC在某种意义上定义访问模式,即在那里放置您想要的约束。支持的访问模式是特定存储类型的固有特性。它们已经由存储提供者定义。

您在PVC定义中实际做的是请求PV支持特定访问模式或访问模式的 。请注意,它采用列表形式,这意味着您可以提供许多您希望PV支持的不同访问模式。

基本上就像说:“嘿!存储提供商!给我一个支持ReadOnlyMany模式的卷。” 您要求以这种方式获得满足您要求的存储。但是请记住,您可以得到的比您要求的更多。这也是我们在GCP 中要求PV支持ReadOnlyMany模式时的场景。它为我们创建了一个满足我们在部分中列出的要求但它也支持模式。虽然我们没有要求支持. 所以基本上这就是它的工作方式。PersistentVolumeaccessModesReadWriteOnceReadWriteOnceReadOnlyMany

PV是由 GCP 自动提供的,以响应您的PVC支持这两个accessModes,如果您没有明确指定PodDeployment定义要以只读模式安装它,默认情况下它以读写模式安装。

您可以通过附加到Pod能够成功安装的来轻松验证它PersistentVolume

kubectl exec -ti pod-name -- /bin/bash
Run Code Online (Sandbox Code Playgroud)

并试图在挂载的文件系统上写一些东西。

你得到的错误信息:

"Error 400: RESOURCE_IN_USE_BY_ANOTHER_RESOURCE"
Run Code Online (Sandbox Code Playgroud)

关注具体GCE永久磁盘已经装入一个GKE nodeReadWriteOnce模式,它不能被其他安装node在其上的其余部分Pods进行调度。

如果您希望它以ReadOnlyMany模式安装,则需要Deployment通过在模板规范下readOnly: truevolumes部分中添加语句来在定义中明确指定它,Pod's如下所示:

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
Run Code Online (Sandbox Code Playgroud)

但是请记住,为了能够在readOnly模式下挂载它,首先我们需要用数据预先填充这样的卷。否则,您将看到另一条错误消息,指出无法以只读模式安装未格式化的卷。

最简单的方法是创建一个单一的Pod,它仅用于将已经上传到我们的GKE 节点之一的数据复制到我们的目的地PV

请注意,PersistentVolume可以通过许多不同的方式使用数据进行预填充。您可以Pod只安装在PersistentVolume您将在您的文件中使用的文件,Deployment并使用curlwget从某个外部位置获取您的数据,并将其直接保存在您的目的地PV。由你决定。

在我的例子我展示了如何使用额外做局部容积,使我们能够安装到我们的Pod一个directorypartition或者disk(在我的例子中,我使用目录/var/tmp/test位于我的GKE节点之一)我们kubernetes节点之一可用。它比hostPath我们不必关心将此类调度Pod到包含数据的特定节点更灵活的解决方案。特定节点关联规则已在特定节点上定义PersistentVolumePod自动调度。

要创建它,我们需要 3 件事:

StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

最后PersistentVolumeClaim

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-storage
Run Code Online (Sandbox Code Playgroud)

然后我们可以创建我们的临时文件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
Run Code Online (Sandbox Code Playgroud)

您可以在上面看到的路径并不重要。这样做的任务Pod只是让我们将数据复制到目的地PV。最终我们PV将安装在完全不同的路径上。

一旦Pod被创建和这两个卷都成功安装,我们可以通过运行附加到它:

kubectl exec -ti my-pod -- /bin/bash
Run Code Online (Sandbox Code Playgroud)

随着Pod简单的运行:

cp /mnt/source/* /mnt/destination/
Run Code Online (Sandbox Code Playgroud)

就这样。现在我们可以exit删除我们的临时文件Pod

kubectl delete pod mypod
Run Code Online (Sandbox Code Playgroud)

一旦它消失了,我们就可以应用我们的,Deployment并且我们PersistentVolume最终可以readOnly通过Pods位于各种GKE 节点上的所有节点以模式挂载:

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
Run Code Online (Sandbox Code Playgroud)

顺便提一句。如果您认为您Pods将只在一个特定节点上调度这一事实,那么您可以完全放弃使用GCE Persistent Disk并切换到上述本地卷。通过这种方式,您Pods不仅可以读取它,还可以同时写入它。唯一需要注意的是,所有这些Pods都将在单个节点上运行。


gui*_*ere 2

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