Инфраструктура, VPS и облачный хостинг

Общее файловое хранилище OVHcloud в Kubernetes: настройка Manila CSI с поддержкой RWX

Поделиться:
Navigating OVHcloud  File Storage with Manila CSI (RWX) on Kubernetes clusters (MKS)

Если вы запускаете stateful-приложения на Kubernetes, одна из распространенных проблем — обеспечение общего постоянного хранилища, к которому могут обращаться несколько рабочих нагрузок.

В то время как Persistent Volumes в Kubernetes обычно полагаются на блочное хранилище с доступом ReadWriteOnce, некоторым приложениям требуется общий доступ к файловой системе с возможностями ReadWriteMany (RWX).

OVHcloud File Storage предоставляет управляемые NFS-ресурсы, которые могут динамически использоваться рабочими нагрузками Kubernetes через драйвер Manila CSI.

В этом посте мы рассмотрим, как интегрировать OVHcloud File Storage с управляемым Kubernetes-сервисом OVHcloud (MKS) с помощью Manila CSI и протестируем возможности RWX-хранилища.

OVHcloud File Storage

Navigating OVHcloud  File Storage with Manila CSI (RWX) on Kubernetes clusters (MKS)

OVHcloud Public Cloud File Storage — это полностью управляемый сервис общего файлового хранилища, предназначенный для облачных рабочих нагрузок, работающих на инстансах Public Cloud и кластерах Kubernetes.

Построенный на базе OpenStack Manila, он предоставляет общие NFSv3-разделы, которые могут быть одновременно смонтированы несколькими клиентами, что делает его идеальным решением для приложений, требующих доступа ReadWriteMany (RWX). Разделы можно выделить размером от 150 ГБ до 10 ТБ с предсказуемой линейной производительностью, масштабируемой пропорционально выделенной емкости.

Поскольку сервис полностью управляемый, вам не нужно развертывать или поддерживать собственный NFS-сервер. File Storage интегрируется с платформой OVHcloud через панель управления, API, CLI, Terraform-провайдер и Kubernetes через драйвер Manila CSI, что позволяет динамически подготавливать общие разделы непосредственно из вашего кластера.

Зачем использовать Manila CSI?

Интерфейс Container Storage Interface (CSI) в Kubernetes предоставляет стандартный механизм для подключения внешних систем хранения к Kubernetes.

Вместо ручного создания NFS-монтирований и PersistentVolumes драйвер Manila CSI позволяет Kubernetes динамически создавать и управлять файловыми ресурсами, используя такие ресурсы Kubernetes, как StorageClasses (SC), PersistentVolumeClaims (PVC) и PersistentVolumes (PV).

Предварительные требования

Перед началом вам потребуется:

  • Проект OVHcloud Public Cloud
  • Кластер управляемого Kubernetes-сервиса OVHcloud (MKS), подключенный к частной сети
  • Установленный Terraform CLI
  • Установленный kubectl CLI

Пошаговое развертывание Manila CSI: давайте сделаем это!

У нас уже есть кластер MKS в регионе EU-WEST-PAR, работающий внутри частной сети и подсети. В этом посте мы:

  • создадим пользователя Public Cloud для драйвера Manila CSI
  • установим драйвер CSI NFS
  • установим драйвер Manila CSI
  • развернем секрет (Secret) для Manila CSI, который позволит драйверу Manila CSI аутентифицироваться в OpenStack и управлять ресурсами Manila в вашем кластере
  • создадим файловую общую сеть
  • развернем ConfigMap для настройки драйвера Manila CSI
  • развернем StorageClass csi-manila-nfs для того, чтобы драйвер Manila CSI мог динамически создавать ресурсы Manila и использовать их как тома Kubernetes

Мы будем использовать Terraform для простоты развертывания этой архитектуры.

Создайте файл provider.tf и заполните его следующей информацией:

terraform {
required_providers {
helm = {
source = "hashicorp/helm"
}

kubectl = {
source = "alekc/kubectl"
version = "2.1.6"
}

ovh = {
source = "ovh/ovh"
}
}
}

provider "helm" {
kubernetes = {
host = data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].host
client_certificate = base64decode(data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].client_certificate)
client_key = base64decode(data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].client_key)
cluster_ca_certificate = base64decode(data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].cluster_ca_certificate)
}
}

provider "kubectl" {
host = data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].host
client_certificate = base64decode(data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].client_certificate)
client_key = base64decode(data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].client_key)
cluster_ca_certificate = base64decode(data.ovh_cloud_project_kube.mks_cluster.kubeconfig_attributes[0].cluster_ca_certificate)
load_config_file = false
}

Установите переменные окружения для Terraform-провайдера OVHcloud, используя свои учетные данные:

# OVHcloud provider needed keys
export OVH_ENDPOINT="ovh-eu"
export OVH_APPLICATION_KEY="xxx"
export OVH_APPLICATION_SECRET="xxx"
export OVH_CONSUMER_KEY="xxx"
export OVH_CLOUD_PROJECT_SERVICE="xxx"

Создайте файл variables.tf.template и заполните его этими данными:

variable "service_name" {
default = "$OVH_CLOUD_PROJECT_SERVICE"
}

variable "mks_cluster_id" {
default = "<your_mks_cluster_id>"
}

⚠️ В файле замените ID MKS на ID вашего существующего кластера MKS.

Замените значение переменной окружения OVH_CLOUD_PROJECT_SERVICE в файле variables.tf:

envsubst < variables.tf.template > variables.tf

Создайте файл nfs_share.tf и заполните его этими данными:

data "ovh_cloud_project_kube" "mks_cluster" {
service_name = var.service_name
kube_id = var.mks_cluster_id
}

data "ovh_cloud_network_private_vrack_subnet" "mks_cluster_subnet" {
service_name = var.service_name
network_id = data.ovh_cloud_project_kube.mks_cluster.private_network_id
id = data.ovh_cloud_project_kube.mks_cluster.nodes_subnet_id
}

# CSI Manila

module "csi_manila" {
source = "git::https://github.com/ovh/public-cloud-examples.git//containers-orchestration/managed-kubernetes/install-csi-manila/modules/ovhcloud/csi_manila?ref=v1.5.0"

service_name = var.service_name
region = data.ovh_cloud_project_kube.mks_cluster.region
share_network_name = "${data.ovh_cloud_project_kube.mks_cluster.name}-share-network"
network_id = data.ovh_cloud_project_kube.mks_cluster.private_network_id
subnet_id = data.ovh_cloud_project_kube.mks_cluster.nodes_subnet_id
subnet_cidr = data.ovh_cloud_network_private_vrack_subnet.mks_cluster_subnet.cidr
}

output "manila-user" {
value = module.csi_manila.manila-user
}

💡 В этом Terraform-файле мы используем существующий Terraform-модуль csi_manila, размещенный в GitHub-репозитории OVHcloud Public Cloud Examples.

Конфигурация Terraform готова. Давайте инициализируем её:

terraform init

Вывод должен быть примерно таким:

$ terraform init

Инициализация бэкенда...
Инициализация модулей...
Загрузка git::https://github.com/ovh/public-cloud-examples.git?ref=v1.5.0 для csi_manila...
- csi_manila в .terraform/modules/csi_manila/containers-orchestration/managed-kubernetes/install-csi-manila/modules/ovhcloud/csi_manila
Инициализация плагинов провайдера...
- Повторное использование предыдущей версии hashicorp/helm из файла блокировки зависимостей
- Повторное использование предыдущей версии alekc/kubectl из файла блокировки зависимостей
- Повторное использование предыдущей версии ovh/ovh из файла блокировки зависимостей
- Установка hashicorp/helm v3.2.0...
- Установлен hashicorp/helm v3.2.0 (подписано HashiCorp)
- Установка alekc/kubectl v2.1.6...
- Установлен alekc/kubectl v2.1.6 (самоподписанный, ID ключа 772FB27A86DAFCE7)
- Установка ovh/ovh v2.16.1...
- Установлен ovh/ovh v2.16.1 (подписано партнёром HashiCorp, ID ключа F56D1A6CBDAAADA5)
Провайдеры партнёров и сообщества подписаны их разработчиками.
Если вы хотите узнать больше о подписании провайдеров, вы можете прочитать об этом здесь:
https://developer.hashicorp.com/terraform/cli/plugins/signing

Terraform успешно инициализирован!

Теперь вы можете начать работу с Terraform. Попробуйте запустить "terraform plan", чтобы увидеть любые изменения, необходимые для вашей инфраструктуры. Все команды Terraform теперь должны работать.

Если вы когда-либо устанавливаете или изменяете модули или конфигурацию бэкенда для Terraform, повторно запустите эту команду для переинициализации вашего рабочего каталога. Если вы забудете, другие команды обнаружат это и напомнят вам сделать это при необходимости.

Примените:

terraform apply

Вывод должен быть таким:

$ terraform apply

data.ovh_cloud_project_kube.mks_cluster: Чтение...
data.ovh_cloud_project_kube.mks_cluster: Чтение завершено через 1с [id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx]
data.ovh_cloud_network_private_vrack_subnet.mks_cluster_subnet: Чтение...
data.ovh_cloud_network_private_vrack_subnet.mks_cluster_subnet: Чтение завершено через 1с [id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx]

Terraform использовал выбранные провайдеры для создания следующего плана выполнения. Действия с ресурсами обозначены следующими символами:
+ create (создать)

Terraform выполнит следующие действия:

# module.csi_manila.helm_release.csi-driver-nfs будет создан
+ resource "helm_release" "csi-driver-nfs" {
+ atomic = false
+ chart = "csi-driver-nfs"
+ cleanup_on_fail = false
+ create_namespace = false
+ dependency_update = false
+ disable_crd_hooks = false
+ disable_openapi_validation = false
+ disable_webhooks = false
+ force_update = false
+ id = (известно после применения)
+ lint = false
+ max_history = 0
+ metadata = (известно после применения)
+ name = "csi-driver-nfs"
+ namespace = "kube-system"
+ pass_credentials = false
+ recreate_pods = false
+ render_subchart_notes = true
+ replace = false
+ repository = "https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts"
+ reset_values = false
+ reuse_values = false
+ set_wo = (атрибут только для записи)
+ skip_crds = false
+ status = "deployed" (развернут)
+ take_ownership = false
+ timeout = 300
+ upgrade_install = false
+ verify = false
+ version = "4.13.4"
+ wait = true
+ wait_for_jobs = false
}

# module.csi_manila.helm_release.openstack-manila-csi будет создан
+ resource "helm_release" "openstack-manila-csi" {
+ atomic = false
+ chart = "openstack-manila-csi"
+ cleanup_on_fail = false
+ create_namespace = false
+ dependency_update = false
+ disable_crd_hooks = false
+ disable_openapi_validation = false
+ disable_webhooks = false
+ force_update = false
+ id = (известно после применения)
+ lint = false
+ max_history = 0
+ metadata = (известно после применения)
+ name = "openstack-manila-csi"
+ namespace = "kube-system"
+ pass_credentials = false
+ recreate_pods = false
+ render_subchart_notes = true
+ replace = false
+ repository = "https://kubernetes.github.io/cloud-provider-openstack"
+ reset_values = false
+ reuse_values = false
+ set_wo = (атрибут только для записи)
+ skip_crds = false
+ status = "deployed" (развернут)
+ take_ownership = false
+ timeout = 300
+ upgrade_install = false
+ verify = false
+ version = "2.36.0"
+ wait = true
+ wait_for_jobs = false
}

# module.csi_manila.kubectl_manifest.csi-manila-secrets будет создан
+ resource "kubectl_manifest" "csi-manila-secrets" {
+ api_version = (известно после применения)
+ apply_only = false
+ field_manager = "kubectl"
+ force_conflicts = false
+ force_new = false
+ id = (известно после применения)
+ kind = (известно после применения)
+ live_manifest_incluster = (чувствительное значение)
+ live_uid = (известно после применения)
+ name = (известно после применения)
+ namespace = (известно после применения)
+ server_side_apply = false
+ uid = (известно после применения)
+ validate_schema = true
+ wait_for_rollout = true
+ yaml_body = (чувствительное значение)
+ yaml_body_parsed = (известно после применения)
+ yaml_incluster = (чувствительное значение)
}

# module.csi_manila.kubectl_manifest.manila-runtime-configmap будет создан
+ resource "kubectl_manifest" "manila-runtime-configmap" {
+ api_version = "v1"
+ apply_only = false
+ field_manager = "kubectl"
+ force_conflicts = false
+ force_new = false
+ id = (известно после применения)
+ kind = "ConfigMap"
+ live_manifest_incluster = (чувствительное значение)
+ live_uid = (известно после применения)
+ name = "manila-csi-runtimeconf-cm"
+ namespace = "default"
+ server_side_apply = false
+ uid = (известно после применения)
+ validate_schema = true
+ wait_for_rollout = true
+ yaml_body = (чувствительное значение)
+ yaml_body_parsed = <<-EOT
apiVersion: v1
data:
runtimeconfig.json: |
{
"nfs": {
"matchExportLocationAddress": "10.1.0.0/16"
}
}
kind: ConfigMap
metadata:
annotations:
meta.helm.sh/release-name: manila-csi
meta.helm.sh/release-namespace: default
labels:
app.kubernetes.io/managed-by: Helm
name: manila-csi-runtimeconf-cm
namespace: default
EOT
+ yaml_incluster = (чувствительное значение)
}

# module.csi_manila.kubectl_manifest.storage-class будет создан
+ resource "kubectl_manifest" "storage-class" {
+ api_version = (известно после применения)
+ apply_only = false
+ field_manager = "kubectl"
+ force_conflicts = false
+ force_new = false
+ id = (известно после применения)
+ kind = (известно после применения)
+ live_manifest_incluster = (чувствительное значение)
+ live_uid = (известно после применения)
+ name = (известно после применения)
+ namespace = (известно после применения)
+ server_side_apply = false
+ uid = (известно после применения)
+ validate_schema = true
+ wait_for_rollout = true
+ yaml_body = (чувствительное значение)
+ yaml_body_parsed = (известно после применения)
+ yaml_incluster = (чувствительное значение)
}

# module.csi_manila.ovh_cloud_project_user.manila-user будет создан
+ resource "ovh_cloud_project_user" "manila-user" {
+ creation_date = (известно после применения)
+ description = "Пользователь для драйвера Manila CSI"
+ id = (известно после применения)
+ openstack_rc = (известно после применения)
+ password = (чувствительное значение)
+ role_name = "share_operator"
+ roles = (известно после применения)
+ service_name = "xxxxxxxxxxxxxxxxxxxxx"
+ status = (известно после применения)
+ username = (известно после применения)
}

# module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork будет создан
+ resource "ovh_cloud_storage_file_share_network" "sharenetwork" {
+ checksum = (известно после применения)
+ created_at = (известно после применения)
+ current_state = (известно после применения)
+ description = (известно после применения)
+ id = (известно после применения)
+ name = "mks_standard_3az-share-network"
+ network_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
+ region = "EU-WEST-PAR"
+ resource_status = (известно после применения)
+ service_name = "xxxxxxxxxxxxxxxxxxxxx"
+ subnet_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
+ updated_at = (известно после применения)
}

План: 7 к добавлению, 0 к изменению, 0 к уничтожению.

Изменения в выводах:
+ manila-user = (известно после применения)

Вы хотите выполнить эти действия?
Terraform выполнит действия, описанные выше.
Для подтверждения будет принято только 'yes'.

Введите значение: yes

module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork: Создание...
module.csi_manila.ovh_cloud_project_user.manila-user: Создание...
module.csi_manila.kubectl_manifest.manila-runtime-configmap: Создание...
module.csi_manila.kubectl_manifest.manila-runtime-configmap: Создание завершено за 0с [id=/api/v1/namespaces/default/configmaps/manila-csi-runtimeconf-cm]
module.csi_manila.helm_release.csi-driver-nfs: Создание...
module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork: Ещё создаётся... [00м10с прошло]
module.csi_manila.ovh_cloud_project_user.manila-user: Ещё создаётся... [00м10с прошло]
module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork: Создание завершено за 12с [id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx]
module.csi_manila.kubectl_manifest.storage-class: Создание...
module.csi_manila.ovh_cloud_project_user.manila-user: Создание завершено за 13с [id=718526]
module.csi_manila.kubectl_manifest.csi-manila-secrets: Создание...
module.csi_manila.kubectl_manifest.csi-manila-secrets: Создание завершено за 1с [id=/api/v1/namespaces/default/secrets/csi-manila-secrets]
...

Давайте динамически создадим File Storage в Kubernetes

Теперь в вашем кластере Kubernetes создайте файл pvc.yaml с следующим содержимым:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-share-fs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 150Gi
storageClassName: csi-manila-nfs

Примените этот Persistent Volume Claim (PVC), благодаря которому пользователи Kubernetes могут запрашивать общее хранилище:

kubectl apply -f pvc.yaml

Проверьте статус PVC:

kubectl get pvc nfs-share-fs-pvc

Дождитесь, пока статус PVC изменится на Bound.

Вы должны получить вывод, подобный этому:

$ kubectl get pvc nfs-share-fs-pvc

NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
nfs-share-fs-pvc Bound pvc-af1349b7-eb51-43ac-b0f0-94d0e8139c57 150Gi RWX csi-manila-nfs <unset> 3h48m

Создайте файл deploy.yaml и заполните его следующим содержимым:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
volumes:
- name: nfs-share-fs-pvc
persistentVolumeClaim:
claimName: nfs-share-fs-pvc
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nfs-share-fs-pvc

Примените этот Deployment с одним подом, к которому подключен том nfs-share-fs-pvc:

kubectl apply -f deploy.yaml

Убедитесь, что под запущен:

kubectl get pod

Вы должны получить вывод, подобный этому:

$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-6cbf9b898c-svpzf 1/1 Running 0 6m

Проверьте возможность масштабирования Deployment (том с множественным подключением):

kubectl scale deploy/nginx-deployment --replicas=2

Проверьте, что другой под запущен:

kubectl get pod

Вы должны получить вывод, подобный этому:

$ kubectl get pod

NAME READY STATUS RESTARTS AGE
nginx-deployment-6cbf9b898c-867bq 1/1 Running 0 3m
nginx-deployment-6cbf9b898c-svpzf 1/1 Running 0 9m

Чтобы проверить функциональность RWX, подключитесь к одному поду и создайте файл в смонтированном каталоге (например, /usr/share/nginx/html).

MY_POD=$(kubectl get po -o name | sed -n '1p')
echo $MY_POD

#Create a file in the pod number 1
kubectl exec $MY_POD -it -- touch /usr/share/nginx/html/index.html

Затем подключитесь ко второму поду и убедитесь, что файл видим:

MY_POD_2=$(kubectl get po -o name | sed -n '2p')
echo $MY_POD_2

#Display it in the pod number two
kubectl exec $MY_POD_2 -it -- ls -alrt /usr/share/nginx/html/

Вы должны получить вывод, подобный этому:

$ kubectl exec $MY_POD_2 -it -- ls -alrt /usr/share/nginx/html/

total 24
drwxr-xr-x 3 root root 4096 Jul 14 01:22 ..
drwx------ 2 root root 16384 Jul 15 09:18 lost+found
drwxrwxrwx 3 root root 4096 Jul 15 09:32 .
-rw-r--r-- 1 root root 0 Jul 15 13:09 index.html

Теперь ваша общая папка Manila, предоставленная через NFS, работает так, как вы хотели! 🎉

Заключение

В этом сообщении блога мы показали, что использование Manila CSI с OVHcloud File Storage позволяет предоставлять приложениям Kubernetes динамически подготавливаемое общее хранилище.

Navigating OVHcloud  File Storage with Manila CSI (RWX) on Kubernetes clusters (MKS)