我有一个三节点GCE集群和一个带有三个副本的单容器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内容?
答案 0 :(得分:3)
首先,我想指出您的配置中的一个基本差异。请注意,当您使用示例中定义的PersistentVolumeClaim
时,根本就不会使用nginx-content
PersistentVolume
。您可以通过运行以下命令轻松进行验证:
kubectl get pv
在您的 GKE集群上。您会注意到,除了手动创建的nginx-content
PV
之外,还有另一个是根据您应用的PVC
自动进行设置的。
请注意,在您的PersistentVolumeClaim
定义中,您明确引用了default
存储类,该类与您手动创建的PV
无关。实际上,即使您完全省略了注释,也可以:
annotations:
volume.alpha.kubernetes.io/storage-class: default
它的工作方式完全相同,即无论如何都将使用default
存储类。在 GKE 上使用默认存储类别意味着 GCE永久磁盘将用作您的卷配置程序。您可以阅读有关here的更多信息:
已配置诸如gcePersistentDisk之类的卷实现 通过StorageClass资源。 GKE为创建一个默认的StorageClass 您使用标准持久性磁盘类型(ext4)。默认值 当PersistentVolumeClaim未指定存储空间时使用StorageClass StorageClassName。您可以替换提供的默认StorageClass 与你自己的。
但是让我们继续解决您面临的问题。
首先,我想强调您不必使用任何类似NFS的文件系统即可达到目标。
如果您需要PersistentVolume
在ReadOnlyMany
模式下可用, GCE永久磁盘是一个完全满足您要求的完美解决方案。
许多ro
可以同时以Pods
模式安装,许多Pods
更为重要的是,它们安排在不同的 GKE nodes
。此外,它的配置非常简单,并且可以直接在 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
支持特定访问模式的访问。请注意,它采用的是列表的形式,这意味着您可以提供您希望PV
支持的许多不同的访问模式。
基本上,这就像说:“嘿!存储提供者!给我一个支持ReadOnlyMany
模式的卷。” 您正在用这种方式要求一种满足您要求的存储。但是请记住,您可以得到比要求更多的东西。这也是我们在 GCP 中请求支持PV
模式的ReadOnlyMany
时的情况。它为我们创建了一个PersistentVolume
,它符合我们在accessModes
部分中列出的要求,但它也支持ReadWriteOnce
模式。尽管我们没有要求也支持ReadWriteOnce
的功能,但您可能会同意我的观点,即对这两种模式具有内置支持的存储完全可以满足我们对支持ReadOnlyMany
的要求。所以基本上这就是它的工作方式。
由GCP为您的PV
自动提供的PVC
支持这两个accessModes
,如果您未在Pod
或{{1 }}的定义,您希望以只读模式挂载它,默认情况下,它以读写模式挂载。
您可以通过将其附加到能够成功装入Deployment
的{{1}}来轻松地进行验证:
Pod
并尝试在已挂载的文件系统上写一些东西。
您收到的错误消息:
PersistentVolume
特别关注 GCE永久磁盘,该磁盘已经由一个 GKE kubectl exec -ti pod-name -- /bin/bash
在"Error 400: RESOURCE_IN_USE_BY_ANOTHER_RESOURCE"
模式下安装,而不能由另一个{{ 1}},其余node
都安排在此位置。
如果您希望以ReadWriteOnce
模式挂载它,则需要在node
定义中明确指定它,方法是在Pods
部分的{{1} {1}}模板规范如下:
ReadOnlyMany
但是请记住,要能够以Deployment
模式挂载它,首先我们需要用数据预填充该卷。否则,您将看到另一条错误消息,提示未格式化的卷不能以只读模式挂载。
最简单的方法是创建一个readOnly: true
,该volumes
仅用于将已经上传到我们的 GKE节点之一的数据复制到目标{{1} }。
请注意,可以用许多不同的方式来预填充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
装入要在readOnly
中使用的Pod
中,并使用PV
或PersistentVolume
从外部位置保存中获取数据它直接在您的目的地Pod
上。由你决定。
在我的示例中,我演示了如何使用额外的local卷来执行此操作,该卷允许我们将PersistentVolume
,Deployment
或{{1 }}(在我的示例中,我使用位于我的一个GKE节点上的目录curl
)在我们的kubernetes节点之一上可用。它比wget
更加灵活,因为我们不必关心将PV
调度到包含数据的特定节点。已在Pod
中定义了特定的节点关联性规则,并且directory
已自动安排在特定节点上。
要创建它,我们需要三件事:
partition
:
disk
/var/tmp/test
定义:
hostPath
最后是Pod
:
PersistentVolume
然后,我们可以创建临时Pod
,该临时StorageClass
仅用于将数据从 GKE节点复制到我们的 GCE永久磁盘。
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
只需运行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
最后可以通过位于各种 GKE上的所有cp /mnt/source/* /mnt/destination/
以exit
方式安装节点:
Pod
顺便说一句。如果您确定kubectl delete pod mypod
仅安排在一个特定节点上,那么您完全可以放弃使用 GCE永久磁盘并切换到上述{{3 }}卷。这样,您所有的Deployment
不仅可以读取它,还可以同时对其进行写入。唯一的警告是所有这些PersistentVolume
将在单个节点上运行。
答案 1 :(得分:1)
您可以使用类似NFS的文件系统来实现。在Google Cloud上,Filestore是适合此产品的产品(由NFS管理)。您有教程here用于实现配置
答案 2 :(得分:0)
如果要跨不同节点共享卷并提供高度可扩展的解决方案,则需要使用ReadWriteMany(RWX)类型的共享卷声明。就像使用NFS服务器一样。
您可以在此处了解如何部署NFS服务器:
https://www.shebanglabs.io/run-nfs-server-on-ubuntu-20-04/
然后您可以按以下方式装载卷(来自NFS服务器的目录):
我已经使用这种方式在+8个k8s部署(+200个pod)之间交付共享的静态内容,通过Nginx每月服务10亿个请求。它确实与NFS设置完美配合:)
答案 3 :(得分:0)
Google提供了类似NFS的文件系统,称为Google Cloud Filestore。您可以将其安装在多个Pod上。