如何预填充ReadOnlyMany持久卷

12

我正在尝试在GKE中创建一个使用多个副本的部署。我有一些静态数据,我希望每个Pod都可以访问这些数据。这些数据将不会被更新,不需要写入。

我决定使用具有ReadOnlyMany存储类别的PV和相应的PVC。问题是,我不知道如何将我的数据实际传输到该卷——因为它是只读的。我尝试使用

gcloud compute scp /local/path instance:/remote/path

但是,当然,我遇到了一个权限错误。然后我尝试通过控制台创建新的 PV。我将其附加到一个 VM 上,具体方法是

gcloud compute instances attach disk

挂载并格式化磁盘,传输我的数据,卸载磁盘,从VM中将其分离,最后按照文档创建一个PVC。我将存储类别更改为ReadOnlyMany,这是唯一的区别。

但是,当我尝试将我的部署扩展到多个副本时,我收到一个错误消息,说磁盘已经附加到另一个节点。

那么,我该如何创建一个只读用的卷,并填充数据呢?或者是否有更好的方法,因为不需要写入?

谢谢!


我通过使用一个 initContainer 解决了这个问题,它在创建部署 Pod 时运行脚本,将文件从源路径复制到卷中(如果文件不存在)。 - JustADev
我通过使用一个initContainer来解决这个问题,它运行脚本,如果文件在创建部署Pod时不存在,就会将文件从源复制到卷中。 - undefined
3个回答

3

3

我们可以简化整个过程。在GKE上,您实际上不需要基于GCE持久磁盘手动创建PV。您只需要定义适当的PVC,它可以如下所示:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: webserver-content-claim
spec:
  accessModes: [ReadOnlyMany]
  resources:
    requests:
      storage: 5Gi

请注意,在PVC中,您不能定义访问模式以限制特定的约束条件。您所做的基本上只是请求支持此特定访问模式的存储。请注意,它是以列表的形式呈现的,这意味着您可以提供许多不同的访问模式,以便您的PV支持。我在答案中详细解释了这一点。但关键点在于,通过在PVC定义中设置ReadOnlyMany访问模式,您仅请求支持此类型访问的卷,但这并不意味着它不支持其他模式。
如果您没有像@Ievgen Goichuk建议的那样在volumes部分的Pod模板中指定readOnly:true,则默认情况下会以rw模式挂载。由于GCE Persistent Disk不支持ReadWriteMany访问模式,因此其他Pods无法挂载已经由一个特定node上调度的一个Podrw模式挂载的这种卷。这个Pod可以以rw模式挂载,是因为GCE Persistent Disk还支持ReadWriteOnce访问模式,根据官方文档的说法,意味着“该卷可以被一个节点以读写方式挂载”。这就是为什么在其他节点上调度的Pods无法挂载它的原因。
但现在让我们转向实际解决方案。
一旦您创建了上述PVC,您将看到相应的PV也已经被创建(kubectl get pv),并且其STATUSBound
现在我们只需要在开始以ReadOnlyMany访问模式使用它之前以某种方式预先填充它。我将分享对我最有效的方法。
如果您已经在您的工作节点的node-pool中的其中之一上传了数据,则可以跳过下一步。
我假设您已在本地计算机上安装了gcloud
gcloud compute scp /local/path instance:/remote/path

这是实现目标的正确方式。如果您遇到“permission denied”错误,这可能意味着您定义的“/remote/path”是某个受限制的目录,作为非root用户,您无法访问该目录。如果您尝试将本地文件系统上的内容复制到远程机器上的“/etc”目录中,则会看到此错误。最安全的方法是将文件复制到您有权限访问的主目录中:

gcloud compute scp --recurse /home/<username>/data/* <instance-name>:~ --zone <zone-name>

如果您想要从源目录复制所有文件和目录及其内容,请使用--recurse选项。

一旦我们的数据上传到其中一个工作节点,我们需要将其复制到我们新创建的PersistentVolume中。有几种不同的方法可以实现这一点。

我决定使用一个临时的Pod,并使用本地卷来完成它。

为了使我们已经存在于一个GKE工作节点上的数据也可用于我们的临时Pod,让我们创建以下内容:

storage-class-local.yaml

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

pv-local.yaml:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /home/<username>
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - <gke-node-name>

以及pvc-local.yaml

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

在下一步中,让我们创建一个临时的Pod,它将使我们能够从node复制数据,并将其作为本地卷挂载到Pod中,然后基于GCE Persistent Disk创建PV。它的定义可能如下所示:
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/mnt/source"
        name: local-volume
      - mountPath: "/mnt/destination"
        name: gce-pd-volume
  volumes:
    - name: local-volume
      persistentVolumeClaim:
        claimName: myclaim
    - name: gce-pd-volume
      persistentVolumeClaim:
        claimName: webserver-content-claim

Pod 运行时,我们可以通过以下方式连接到它:

kubectl exec -ti mypod -- /bin/bash

复制我们的文件:

cp -a /mnt/source/* /mnt/destination/

现在我们可以删除临时的Pod、本地PV和PVC。我们的PersistentVolume已经预先填充了数据,并且可以以ro模式挂载。
为了测试它,我们可以运行以下Deployment:
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: webserver-content
      volumes:
      - name: webserver-content
        persistentVolumeClaim:
          claimName: webserver-content-claim
          readOnly: true ### don't forget about setting this option

很有趣,我不知道“你只请求支持此类型访问的卷,但这并不意味着它不支持其他模式。”。话虽如此,你的回答很有道理。尽管使用这样一个有用的功能相当耗时和复杂,但似乎还是有点奇怪。更奇怪的是,在官方文档中竟然没有任何示例。无论如何,谢谢你,最终我还是设置了自己的NFS服务器。 - Nikolaos Paschos

0

你好,Nikolaos:

你所采用的方法在很大程度上取决于你的使用情况。

当你使用分布式文件系统如CEPH、GlusterFS或GCP Cloud Filestore或远程文件系统如NFS时,这种方法非常常见。

使用分布式文件系统或远程文件系统的方法是:

1. 创建一个PV,将AccessMode设置为RWO,并将Reclaim Policy设置为RETAIN。

2. 创建PVC。

3. 将PVC附加到POD上。

4. 通过POD将数据传输到卷中。

5. 删除pod、pvc和pv。

6. 为每个要附加数据的Deployment或POD创建一个新的PV,将AccessMode设置为ROX,并将Reclaim Policy设置为RETAIN。如果POD副本数大于1,则不适用此项,因为POD将附加相同的PV。

7. 为每个PV创建一个PVC。PV和PVC的关系是1:1

8. 将PVC附加到要使用的每个POD或Deployment上。

你的问题似乎是你试图将同一个PV附加到多个PVC上,这是不允许的,PVC <--> PV的关系是一对一的。

关于你的另一个问题,是否有更好的方法,这取决于你的使用情况。Google Cloud Platform提供了很多存储选项[1]。例如,如果你正在使用对象,可以使用Google Cloud Storage [2]而不是Persistent Disks。

[1] https://cloud.google.com/kubernetes-engine/docs/concepts/storage-overview

[2] https://cloud.google.com/filestore/docs/accessing-fileshares


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接