Сначала я хотел бы указать на одно принципиальное несоответствие в вашей конфигурации. Обратите внимание, что когда вы используете свой 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 Persistent Disk будет использоваться в качестве вашего тома. Подробнее об этом можно узнать здесь:
Реализации томов, такие как gcePersistentDisk, настраиваются с помощью ресурсов StorageClass. GKE создает для вас StorageClass по умолчанию, который использует стандартный постоянный тип диска (ext4). StorageClass по умолчанию используется, когда PersistentVolumeClaim не указывает 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, только один модуль может монтировать том в любой момент времени.
Ну, на самом деле нет. Я знаю, что это может показаться немного сложным и несколько удивительным, но определение accessModes
работает не так. На самом деле это широко неправильно понимаемая концепция. Во-первых, вы не можете определять режимы доступа в PVC
в том смысле, что устанавливаете там нужные вам ограничения. Поддерживаемые режимы доступа присущи определенному типу хранилища. Они уже определены поставщиком хранилища.
Фактически в PVC
определении вы запрашиваете PV
, который поддерживает конкретный режим доступа или режимы доступа. Обратите внимание, что он представлен в виде списка, что означает, что вы можете предоставить множество различных режимов доступа, которые должен поддерживать ваш PV
.
По сути, это как сказать: Эй! Провайдер хранилища! Дайте мне том, который поддерживает режим ReadOnlyMany
. Вы запрашиваете таким образом хранилище, которое будет удовлетворять вашим требованиям. Однако имейте в виду, что вам могут дать больше, чем вы просите. И это также наш сценарий, когда мы запрашиваем PV
, который поддерживает ReadOnlyMany
режим в GCP. Он создает для нас PersistentVolume
, который соответствует нашим требованиям, перечисленным в разделе accessModes
, но также поддерживает режим ReadWriteOnce
. Хотя мы не просили что-то, что также поддерживает ReadWriteOnce
, вы, вероятно, согласитесь со мной, что хранилище, которое имеет встроенную поддержку этих двух режимов, полностью удовлетворяет наш запрос на что-то, что поддерживает ReadOnlyMany
. Так в основном это работает.
Ваш PV
, который был автоматически предоставлен GCP в ответ для вашего PVC
, поддерживает эти два accessModes
, и если вы явно не укажете в Pod
или Deployment
определении, что хотите подключить его в режиме только для чтения, по умолчанию он монтируется в режиме чтения-записи.
Вы можете легко проверить это, подключив Pod
, который смог успешно смонтировать PersistentVolume
:
kubectl exec -ti pod-name -- /bin/bash
и пытается что-то записать в смонтированной файловой системе.
Вы получаете сообщение об ошибке:
"Error 400: RESOURCE_IN_USE_BY_ANOTHER_RESOURCE"
касается, в частности, постоянного диска GCE, который уже смонтирован одним GKE node
в ReadWriteOnce
режиме и не может быть смонтирован другим node
, на котором были запланированы остальные Pods
.
Если вы хотите, чтобы он был смонтирован в режиме ReadOnlyMany
, вам необходимо явно указать это в своем Deployment
определении, добавив оператор readOnly: true
в раздел volumes
под спецификацией шаблона 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
, который будет служить только для копирования данных, которые уже были загружены на один из наших узлов GKE, в пункт назначения PV
.
Обратите внимание, что предварительное заполнение PersistentVolume
данными может выполняться разными способами. Вы можете смонтировать в таком Pod
только свой PersistentVolume
, который вы будете использовать в своем Deployment
, и получить свои данные с помощью curl
или wget
из некоторого внешнего местоположения, сохраняя их прямо в пункте назначения PV
. Тебе решать.
В моем примере я показываю, как это сделать, используя дополнительный локальный том, который позволяет нам подключаться к нашим Pod
a directory
, partition
или disk
(в моем примере я использую каталог /var/tmp/test
, расположенный на одном из моих узлов GKE), доступный на одном из наших узлов kubernetes. Это гораздо более гибкое решение, чем hostPath
, поскольку нам не нужно заботиться о планировании таких Pod
для конкретного узла, который содержит данные. Конкретное правило сходства узлов уже определено в PersistentVolume
, а Pod
автоматически назначается для конкретного узла.
Для его создания нам понадобятся 3 вещи:
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
, наконец, можно будет смонтировать в 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
Кстати. если вас устраивает тот факт, что ваш Pods
будет запланирован только на одном конкретном узле, вы можете вообще отказаться от использования постоянного диска GCE и переключиться на вышеупомянутый локальный том. Таким образом, весь ваш Pods
сможет не только читать с него, но и писать в него одновременно. Единственное предостережение - все эти Pods
будут работать на одном узле.
person
mario
schedule
23.06.2020