Voltar

Uso do OVHcloud File Storage com Manila CSI (RWX) em clusters Kubernetes (MKS)

Aurélie Vache12 minutos de leitura

Uso do OVHcloud File Storage com Manila CSI (RWX) em clusters Kubernetes (MKS)

Se executa aplicações stateful no Kubernetes, um desafio comum é o fornecimento de armazenamento persistente partilhado que possa ser acedido por várias cargas de trabalho.

Embora os volumes persistentes do Kubernetes costumem depender de armazenamento em bloco com acesso ReadWriteOnce, algumas aplicações requerem acesso a um sistema de ficheiros partilhado com capacidades ReadWriteMany (RWX).

O OVHcloud File Storage fornece partilhas NFS geridas que podem ser consumidas dinamicamente por cargas de trabalho Kubernetes através do controlador Manila CSI.

Neste artigo, veremos como integrar o OVHcloud File Storage com o OVHcloud Managed Kubernetes Service (MKS) usando o Manila CSI e como testar as capacidades de armazenamento RWX.

OVHcloud File Storage

Partilhas NFS geridas pelo OVHcloud File Storage para cargas de trabalho Kubernetes nativas da cloud.

O OVHcloud Public Cloud File Storage é um serviço de armazenamento de ficheiros partilhado totalmente gerido, pensado para cargas de trabalho nativas da cloud executadas em instâncias Public Cloud e clusters Kubernetes.

Baseado em OpenStack Manila, fornece volumes NFSv3 partilhados que podem ser montados simultaneamente por vários clientes, o que faz dele uma solução ideal para aplicações que exijam um acesso ReadWriteMany (RWX). Os volumes podem ser provisionados com capacidades entre 150 GiB e 10 TiB, com um desempenho linear e previsível que se adapta à capacidade atribuída.

Como o serviço é totalmente gerido, não precisa de implementar ou manter um servidor NFS próprio. O File Storage integra-se com a plataforma OVHcloud através da Área de Cliente, da API, da CLI, do fornecedor Terraform e do Kubernetes por meio do controlador Manila CSI, permitindo que volumes partilhados sejam provisionados de forma dinâmica diretamente a partir do seu cluster.

Porquê usar o Manila CSI?

A Container Storage Interface (CSI) do Kubernetes fornece um mecanismo padrão para expor sistemas de armazenamento externos ao Kubernetes.

Em vez de criar manualmente montagens NFS e PersistentVolumes, o controlador Manila CSI permite que o Kubernetes crie e faça a gestão dinâmica de partilhas de ficheiros utilizando recursos Kubernetes, tais como Storage Classes (SC), Persistent Volume Claims (PVC) e Persistent Volumes (PV).

Pré-requisitos

Antes de começar, precisa de:

  • Um projeto Public Cloud OVHcloud
  • Um cluster OVHcloud Managed Kubernetes Service (MKS) que esteja ligado a uma rede privada
  • CLI Terraform instalada
  • CLI kubectl instalada

Implementação do Manila CSI passo a passo: vamos a isso!

Já temos um cluster MKS, na região EU-WEST-PAR, em execução numa rede privada e numa sub-rede. Neste artigo vamos:

  • criar um utilizador Public Cloud para o controlador Manila CSI
  • instalar o controlador CSI NFS
  • instalar o controlador Manila CSI
  • implementar um segredo para o Manila CSI que permita ao controlador Manila CSI autenticar-se no OpenStack e gerir recursos Manila no seu cluster
  • criar uma rede partilhada de ficheiros
  • implementar um ConfigMap para configurar o controlador Manila CSI
  • implementar a StorageClass csi-manila-nfs para permitir que o controlador Manila CSI crie dinamicamente partilhas Manila e as utilize como volumes Kubernetes

Vamos usar o Terraform para implementar esta arquitetura de forma simples.

Crie um ficheiro provider.tf e preencha-o com as informações:

bash
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
}

Com as suas credenciais, defina as variáveis de ambiente para o fornecedor Terraform OVHcloud:

bash
# 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"

Crie um ficheiro variables.tf.template e preencha-o com estas informações:

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

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

⚠️ No ficheiro, substitua o ID do MKS pelas informações do ID do seu cluster MKS atual.

Substitua o valor da variável de ambiente OVH_CLOUD_PROJECT_SERVICE no ficheiro variables.tf:

bash
envsubst < variables.tf.template > variables.tf

Crie um ficheiro nfs_share.tf e preencha-o com estas informações:

markdown
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
}

💡Neste ficheiro Terraform estamos a usar um módulo Terraform csi_manila existente, alojado no repositório GitHub «OVHcloud Public Cloud Examples».

A configuração Terraform está pronta. Vamos inicializá-la:

bash
terraform init

O resultado deve ser o seguinte:

bash
$ 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.

Aplique-o:

bash
terraform apply

O resultado deve ser o seguinte:

bash
$ 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]
...

Vamos criar dinamicamente o File Storage no Kubernetes

Agora, no seu cluster Kubernetes, crie um ficheiro pvc.yaml com este conteúdo:

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

Aplique este Persistent Volume Claim (PVC); graças a ele, os utilizadores Kubernetes podem solicitar armazenamento partilhado:

bash
kubectl apply -f pvc.yaml

Verifique o estado do PVC:

bash
kubectl get pvc nfs-share-fs-pvc

Aguarde até que o estado do PVC mude para Bound.

Deverá obter um resultado como este:

bash
$ 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

Crie um ficheiro deploy.yaml e preencha-o com este conteúdo:

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

Aplique esta implementação com um pod que tenha um volume associado a nfs-share-fs-pvc:

bash
kubectl apply -f deploy.yaml

Verifique se o pod está em execução:

bash
kubectl get pod

Deverá obter um resultado como este:

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

Verifique se consegue dimensionar a implementação (multi-attach volume):

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

Verifique se outro pod está em execução:

bash
kubectl get pod

Deverá obter um resultado como este:

bash
$ 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

Para verificar a funcionalidade RWX, ligue-se a um pod e crie um ficheiro no diretório montado (por exemplo, /usr/share/nginx/html).

bash
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

Depois, ligue-se ao segundo pod e confirme se o ficheiro está visível:

bash
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/

Deverá obter um resultado como este:

bash
$ 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

Agora, a sua partilha Manila, exposta através de NFS, está a funcionar como pretende! 🎉

Conclusão

Neste artigo, mostrámos que o uso do Manila CSI com o OVHcloud File Storage permite fornecer às aplicações Kubernetes armazenamento partilhado provisionado dinamicamente.

Recomendamos que consulte também o nosso Cloud Roadmap & Changelog para obter uma visão geral de todas as funcionalidades futuras dos produtos OVHcloud Public Cloud.


Partilhar em: