
Beim Ausführen von Stateful-Anwendungen auf Kubernetes liegt eine häufige Herausforderung in der Bereitstellung gemeinsam genutzter, persistenter Speicher, auf die mehrere Workloads zugreifen können.
Während Kubernetes Persistent Volumes in der Regel auf Blockspeicher mit ReadWriteOnce-Zugriff basieren, erfordern einige Anwendungen einen gemeinsamen Dateisystemzugriff mit ReadWriteMany (RWX)-Fähigkeiten.
OVHcloud File Storage bietet verwaltete NFS-Freigaben, die von Kubernetes-Workloads über den Manila CSI-Treiber dynamisch genutzt werden können.
In diesem Blogbeitrag zeigen wir, wie Sie OVHcloud File Storage mit OVHcloud Managed Kubernetes Service (MKS) mit Manila CSI integrieren und RWX-Speicherfunktionen testen.
OVHcloud File Storage

OVHcloud Public Cloud File Storage ist ein vollständig verwalteter, gemeinsam genutzter Dateispeicher-Dienst, der für Cloud-native Workloads auf Public Cloud-Instanzen und Kubernetes-Clustern entwickelt wurde.
Er basiert auf OpenStack Manila und bietet gemeinsame NFSv3 Volumes, die gleichzeitig von mehreren Clients gemountet werden können. Dies macht ihn zu einer idealen Lösung für Anwendungen, die ReadWriteMany (RWX)-Zugriff erfordern. Es können Volumes von 150 GiB bis 10 TiB bereitgestellt werden, mit vorhersehbaren, linearen Leistung, die mit der zugewiesenen Kapazität skaliert wird.
Da der Service vollständig verwaltet wird, müssen Sie keinen eigenen NFS-Server bereitstellen oder warten. File Storage lässt sich über das Control Panel, die API, die CLI, den Terraform-Provider und Kubernetes über den Manila CSI-Treiber nahtlos mit der OVHcloud-Plattform integrieren, sodass gemeinsam genutzte Volumes direkt aus Ihrem Cluster dynamisch bereitgestellt werden können.
Warum Manila CSI?
Das Kubernetes Container Storage Interface (CSI) bietet einen standardisierten Mechanismus, um externe Storage-Systeme für Kubernetes zugänglich zu machen.
Statt NFS-Mounts und PersistentVolumes manuell zu erzeugen, kann der Manila CSI-Treiber Kubernetes, Dateifreigaben mit Kubernetes-Ressourcen wie StorageClasses (SC), PersistentVolumeClaims (PVC) und PersistentVolumes (PV) dynamisch erstellen und verwalten.
Voraussetzungen
Bevor es losgehen kann, benötigen Sie Folgendes:
- Ein aktives OVHcloud Public-Cloud-Projekt
- Ein OVHcloud Managed Kubernetes Service (MKS)-Cluster, das mit einem privaten Netzwerk verbunden ist
- Terraform CLI (bereits installiert)
- kubectl CLI (bereits installiert)
Manila CSI Schritt für Schritt einsetzen: Los geht’s!
Wir haben bereits einen MKS Cluster in der Region EU-WEST-PAR, der in einem privaten Netzwerk und Subnetz läuft. In diesem Blogbeitrag geht es darum, wie Sie:
- einen Public Cloud-User für den Manila CSI-Treiber erstellen
- den CSI NFS-Treiber installieren
- den Manila CSI-Treiber installieren
- ein Secret für Manila CSI bereitstellen, das es dem Manila CSI Treiber ermöglicht, sich bei OpenStack zu authentifizieren und Manila Ressourcen in Ihrem Cluster zu verwalten
- ein Netzwerk mit gemeinsam genutzten Dateien erstellen
- eine ConfigMap deployen, um den Manila CSI-Treiber zu konfigurieren
- csi-manila-nfs StorageClass bereitstellen, damit der Manila CSI-Treiber Manila-Freigaben dynamisch erstellen und sie als Kubernetes-Volumes verwenden kann
Wir werden Terraform verwenden, um diese Architektur auf einfache Weise bereitzustellen.
Erstellen Sie eine provider.tf-Datei, die folgende Informationen enthält:
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
}Richten Sie die Umgebungsvariablen für den OVHcloud Terraform Provider mit Ihren Anmeldedaten ein:
# 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"Erstellen Sie eine variables.tf.template-Datei, die folgende Informationen enthält:
variable "service_name" {
default = "$OVH_CLOUD_PROJECT_SERVICE"
}
variable "mks_cluster_id" {
default = "<your_mks_cluster_id>"
}⚠️ Ersetzen Sie in der Datei die MKS ID durch Ihre vorhandene MKS Cluster ID.
Ersetzen Sie den Wert der Umgebungsvariable OVH_CLOUD_PROJECT_SERVICE in der variables.tf-Datei:
envsubst < variables.tf.template > variables.tfErstellen Sie eine nfs_share.tf-Datei, die folgende Informationen enthält:
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
}💡In dieser Terraform-Datei verwenden wir ein vorhandenes csi_manila Terraform-Modul, das im OVHcloud Public Cloud Examples GitHub-Repository gehostet wird.
Die Terraform-Konfiguration ist fertig. Lassen Sie uns starten:
terraform initDie Ausgabe sollte wie folgt aussehen:
$ terraform init
Initializing the backend...
Initializing modules...
Downloading git::https://github.com/ovh/public-cloud-examples.git?ref=v1.5.0 for csi_manila...
- csi_manila in .terraform/modules/csi_manila/containers-orchestration/managed-kubernetes/install-csi-manila/modules/ovhcloud/csi_manila
Initializing provider plugins...
- Reusing previous version of hashicorp/helm from the dependency lock file
- Reusing previous version of alekc/kubectl from the dependency lock file
- Reusing previous version of ovh/ovh from the dependency lock file
- Installing hashicorp/helm v3.2.0...
- Installed hashicorp/helm v3.2.0 (signed by HashiCorp)
- Installing alekc/kubectl v2.1.6...
- Installed alekc/kubectl v2.1.6 (self-signed, key ID 772FB27A86DAFCE7)
- Installing ovh/ovh v2.16.1...
- Installed ovh/ovh v2.16.1 (signed by a HashiCorp partner, key ID F56D1A6CBDAAADA5)
Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://developer.hashicorp.com/terraform/cli/plugins/signing
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.Anwenden:
terraform applyDie Ausgabe sollte wie folgt aussehen:
$ terraform apply
data.ovh_cloud_project_kube.mks_cluster: Reading...
data.ovh_cloud_project_kube.mks_cluster: Read complete after 1s [id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx]
data.ovh_cloud_network_private_vrack_subnet.mks_cluster_subnet: Reading...
data.ovh_cloud_network_private_vrack_subnet.mks_cluster_subnet: Read complete after 1s [id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.csi_manila.helm_release.csi-driver-nfs will be created
+ 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 = (known after apply)
+ lint = false
+ max_history = 0
+ metadata = (known after apply)
+ 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 = (write-only attribute)
+ 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 will be created
+ 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 = (known after apply)
+ lint = false
+ max_history = 0
+ metadata = (known after apply)
+ 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 = (write-only attribute)
+ 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 will be created
+ resource "kubectl_manifest" "csi-manila-secrets" {
+ api_version = (known after apply)
+ apply_only = false
+ field_manager = "kubectl"
+ force_conflicts = false
+ force_new = false
+ id = (known after apply)
+ kind = (known after apply)
+ live_manifest_incluster = (sensitive value)
+ live_uid = (known after apply)
+ name = (known after apply)
+ namespace = (known after apply)
+ server_side_apply = false
+ uid = (known after apply)
+ validate_schema = true
+ wait_for_rollout = true
+ yaml_body = (sensitive value)
+ yaml_body_parsed = (known after apply)
+ yaml_incluster = (sensitive value)
}
# module.csi_manila.kubectl_manifest.manila-runtime-configmap will be created
+ resource "kubectl_manifest" "manila-runtime-configmap" {
+ api_version = "v1"
+ apply_only = false
+ field_manager = "kubectl"
+ force_conflicts = false
+ force_new = false
+ id = (known after apply)
+ kind = "ConfigMap"
+ live_manifest_incluster = (sensitive value)
+ live_uid = (known after apply)
+ name = "manila-csi-runtimeconf-cm"
+ namespace = "default"
+ server_side_apply = false
+ uid = (known after apply)
+ validate_schema = true
+ wait_for_rollout = true
+ yaml_body = (sensitive value)
+ 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 = (sensitive value)
}
# module.csi_manila.kubectl_manifest.storage-class will be created
+ resource "kubectl_manifest" "storage-class" {
+ api_version = (known after apply)
+ apply_only = false
+ field_manager = "kubectl"
+ force_conflicts = false
+ force_new = false
+ id = (known after apply)
+ kind = (known after apply)
+ live_manifest_incluster = (sensitive value)
+ live_uid = (known after apply)
+ name = (known after apply)
+ namespace = (known after apply)
+ server_side_apply = false
+ uid = (known after apply)
+ validate_schema = true
+ wait_for_rollout = true
+ yaml_body = (sensitive value)
+ yaml_body_parsed = (known after apply)
+ yaml_incluster = (sensitive value)
}
# module.csi_manila.ovh_cloud_project_user.manila-user will be created
+ resource "ovh_cloud_project_user" "manila-user" {
+ creation_date = (known after apply)
+ description = "User for the Manila CSI driver"
+ id = (known after apply)
+ openstack_rc = (known after apply)
+ password = (sensitive value)
+ role_name = "share_operator"
+ roles = (known after apply)
+ service_name = "xxxxxxxxxxxxxxxxxxxxx"
+ status = (known after apply)
+ username = (known after apply)
}
# module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork will be created
+ resource "ovh_cloud_storage_file_share_network" "sharenetwork" {
+ checksum = (known after apply)
+ created_at = (known after apply)
+ current_state = (known after apply)
+ description = (known after apply)
+ id = (known after apply)
+ name = "mks_standard_3az-share-network"
+ network_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
+ region = "EU-WEST-PAR"
+ resource_status = (known after apply)
+ service_name = "xxxxxxxxxxxxxxxxxxxxx"
+ subnet_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
+ updated_at = (known after apply)
}
Plan: 7 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ manila-user = (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork: Creating...
module.csi_manila.ovh_cloud_project_user.manila-user: Creating...
module.csi_manila.kubectl_manifest.manila-runtime-configmap: Creating...
module.csi_manila.kubectl_manifest.manila-runtime-configmap: Creation complete after 0s [id=/api/v1/namespaces/default/configmaps/manila-csi-runtimeconf-cm]
module.csi_manila.helm_release.csi-driver-nfs: Creating...
module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork: Still creating... [00m10s elapsed]
module.csi_manila.ovh_cloud_project_user.manila-user: Still creating... [00m10s elapsed]
module.csi_manila.ovh_cloud_storage_file_share_network.sharenetwork: Creation complete after 12s [id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx]
module.csi_manila.kubectl_manifest.storage-class: Creating...
module.csi_manila.ovh_cloud_project_user.manila-user: Creation complete after 13s [id=718526]
module.csi_manila.kubectl_manifest.csi-manila-secrets: Creating...
module.csi_manila.kubectl_manifest.csi-manila-secrets: Creation complete after 1s [id=/api/v1/namespaces/default/secrets/csi-manila-secrets]
...Dynamischen File Storage auf Kubernetes erstellen
Erstellen Sie nun in Ihrem Kubernetes Cluster eine pvc.yaml-Datei mit folgendem Inhalt:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-share-fs-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 150Gi
storageClassName: csi-manila-nfsWenden Sie diesen Persistent Volume Claim (PVC) an, damit Kubernetes-User gemeinsam genutzte Speicher anfordern können:
kubectl apply -f pvc.yamlÜberprüfen Sie den Status des PVC:
kubectl get pvc nfs-share-fs-pvcWarten Sie, bis der Status des PVC zu Bound wechselt.
Die Ausgabe sollte wie folgt aussehen:
$ 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> 3h48mErstellen Sie eine deploy.yaml-Datei, die folgende Inhalte enthält:
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-pvcWenden Sie diese Bereitstellung mit einem Pod an, der ein Volume mit nfs-share-fs-pvc hat:
kubectl apply -f deploy.yamlÜberprüfen Sie, ob der Pod läuft:
kubectl get podDie Ausgabe sollte wie folgt aussehen:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-6cbf9b898c-svpzf 1/1 Running 0 6mÜberprüfen Sie, ob Sie das Deployment skalieren können (Multi-Attach-Volume):
kubectl scale deploy/nginx-deployment --replicas=2Überprüfen Sie, ob ein anderer Pod läuft:
kubectl get podDie Ausgabe sollte wie folgt aussehen:
$ 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 9mUm die RWX-Funktionalität zu überprüfen, verbinden Sie sich mit einem Pod und erstellen Sie eine Datei im gemounteten Verzeichnis (z. B. /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.htmlVerbinden Sie sich dann mit dem zweiten Pod und bestätigen Sie, dass die Datei sichtbar ist:
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/Die Ausgabe sollte wie folgt aussehen:
$ 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.htmlJetzt funktioniert Ihr über NFS freigegebener Manila-Ordner genau so, wie Sie es sich vorgestellt haben! 🎉
Fazit
In diesem Blogbeitrag haben wir gezeigt, dass die Verwendung von Manila CSI mit OVHcloud File Storage es ermöglicht, Kubernetes-Anwendungen mit dynamisch bereitgestelltem, gemeinsam genutztem Storage zu versorgen.
Wir empfehlen Ihnen, auch unsere Cloud-Roadmap und Changelog zu lesen, um einen Überblick über alle kommenden Funktionen der OVHcloud Public Cloud-Produkte zu erhalten.