Tour ou pas - telle est la question

Tour ou pas - telle est la question

Au début de ce mois, le 3 mai, une version majeure d'un « système de gestion du stockage de données distribué dans Kubernetes » a été annoncée - Tour 1.0.0. Il y a plus d'un an, nous publié aperçu général de Rook. Ensuite, on nous a demandé de parler de son expérience utilisation en pratique — et maintenant, juste à temps pour une étape aussi importante dans l'histoire du projet, nous sommes heureux de partager nos impressions accumulées.

Bref, Rook est un ensemble opérateurs pour Kubernetes, qui prennent le contrôle total du déploiement, de la gestion, de la récupération automatique des solutions de stockage de données telles que Ceph, EdgeFS, Minio, Cassandra, CockroachDB.

À l'heure actuelle, le plus développé (et le seul в stable étape), la solution est opérateur tour-ceph.

Noter: Parmi les changements significatifs de la version Rook 1.0.0 liés à Ceph, on peut noter le support de Ceph Nautilus et la possibilité d'utiliser NFS pour les buckets CephFS ou RGW. Ce qui ressort entre autres, c'est la maturation du support EdgeFS jusqu'au niveau bêta.

Ainsi, dans cet article, nous :

  • Répondons à la question des avantages que nous voyons à utiliser Rook pour déployer Ceph dans un cluster Kubernetes ;
  • Nous partagerons notre expérience et nos impressions sur l'utilisation de Rook en production ;
  • Voyons pourquoi nous disons « Oui ! » à Rook et quels sont nos projets pour lui.

Commençons par les concepts généraux et la théorie.

"J'ai l'avantage d'une Tour !" (joueur d'échecs inconnu)

Tour ou pas - telle est la question

L'un des principaux avantages de Rook est que l'interaction avec les magasins de données s'effectue via les mécanismes Kubernetes. Cela signifie que vous n'avez plus besoin de copier les commandes pour configurer Ceph depuis la feuille vers la console.

— Voulez-vous déployer CephFS dans un cluster ? Écrivez simplement un fichier YAML !
- Quoi? Souhaitez-vous également déployer un magasin d'objets avec l'API S3 ? Écrivez simplement un deuxième fichier YAML !

Rook est créé selon toutes les règles d'un opérateur typique. L'interaction avec lui se produit en utilisant CRD (définitions de ressources personnalisées), dans lequel nous décrivons les caractéristiques des entités Ceph dont nous avons besoin (puisqu'il s'agit de la seule implémentation stable, par défaut cet article parlera de Ceph, sauf indication contraire explicite). Selon les paramètres spécifiés, l'opérateur exécutera automatiquement les commandes nécessaires à la configuration.

Regardons les détails en utilisant l'exemple de création d'un Object Store, ou plutôt - CephObjectStoreUser.

apiVersion: ceph.rook.io/v1
kind: CephObjectStore
metadata:
  name: {{ .Values.s3.crdName }}
  namespace: kube-rook
spec:
  metadataPool:
    failureDomain: host
    replicated:
      size: 3
  dataPool:
    failureDomain: host
    erasureCoded:
      dataChunks: 2
      codingChunks: 1
  gateway:
    type: s3
    sslCertificateRef:
    port: 80
    securePort:
    instances: 1
    allNodes: false
---
apiVersion: ceph.rook.io/v1
kind: CephObjectStoreUser
metadata:
  name: {{ .Values.s3.crdName }}
  namespace: kube-rook
spec:
  store: {{ .Values.s3.crdName }}
  displayName: {{ .Values.s3.username }}

Les paramètres indiqués dans le listing sont assez standards et ne nécessitent guère de commentaires, mais il convient de prêter une attention particulière à ceux attribués aux variables du modèle.

Le schéma général de travail se résume au fait que nous « commandons » les ressources via un fichier YAML, pour lequel l'opérateur exécute les commandes nécessaires et nous renvoie un secret « pas si réel » avec lequel nous pouvons continuer à travailler. (voir ci-dessous). Et à partir des variables répertoriées ci-dessus, la commande et le nom du secret seront compilés.

De quel genre d'équipe s'agit-il ? Lors de la création d'un utilisateur pour le stockage d'objets, l'opérateur Rook à l'intérieur du pod effectuera les opérations suivantes :

radosgw-admin user create --uid="rook-user" --display-name="{{ .Values.s3.username }}"

Le résultat de l'exécution de cette commande sera une structure JSON :

{
    "user_id": "rook-user",
    "display_name": "{{ .Values.s3.username }}",
    "keys": [
        {
           "user": "rook-user",
           "access_key": "NRWGT19TWMYOB1YDBV1Y",
           "secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"
        }
    ],
    ...
}

Keys - de quelles futures applications auront besoin pour accéder au stockage objet via l'API S3. L'opérateur Rook les sélectionne gentiment et les met dans son espace de noms sous la forme d'un secret avec le nom rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}.

Pour utiliser les données de ce secret, ajoutez-les simplement au conteneur en tant que variables d'environnement. A titre d'exemple, je vais donner un modèle pour Job, dans lequel nous créons automatiquement des buckets pour chaque environnement utilisateur :

{{- range $bucket := $.Values.s3.bucketNames }}
apiVersion: batch/v1
kind: Job
metadata:
  name: create-{{ $bucket }}-bucket-job
  annotations:
    "helm.sh/hook": post-install
    "helm.sh/hook-weight": "2"
spec:
  template:
    metadata:
      name: create-{{ $bucket }}-bucket-job
    spec:
      restartPolicy: Never
      initContainers:
      - name: waitdns
        image: alpine:3.6
        command: ["/bin/sh", "-c", "while ! getent ahostsv4 rook-ceph-rgw-{{ $.Values.s3.crdName }}; do sleep 1; done" ]
      - name: config
        image: rook/ceph:v1.0.0
        command: ["/bin/sh", "-c"]
        args: ["s3cmd --configure --access_key=$(ACCESS-KEY) --secret_key=$(SECRET-KEY) -s --no-ssl --dump-config | tee /config/.s3cfg"]
        volumeMounts:
        - name: config
          mountPath: /config
        env:
        - name: ACCESS-KEY
          valueFrom:
            secretKeyRef:
              name: rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}
              key: AccessKey
        - name: SECRET-KEY
          valueFrom:
            secretKeyRef:
              name: rook-ceph-object-user-{{ $.Values.s3.crdName }}-{{ $.Values.s3.username }}
              key: SecretKey
      containers:
      - name: create-bucket
        image: rook/ceph:v1.0.0
        command: 
        - "s3cmd"
        - "mb"
        - "--host=rook-ceph-rgw-{{ $.Values.s3.crdName }}"
        - "--host-bucket= "
        - "s3://{{ $bucket }}"
        ports:
        - name: s3-no-sll
          containerPort: 80
        volumeMounts:
        - name: config
          mountPath: /root
      volumes:
      - name: config
        emptyDir: {}
---
{{- end }}

Toutes les actions répertoriées dans ce Job ont été réalisées dans le cadre de Kubernetes. Les structures décrites dans les fichiers YAML sont stockées dans un référentiel Git et réutilisées de nombreuses fois. Nous considérons cela comme un énorme avantage pour les ingénieurs DevOps et le processus CI/CD dans son ensemble.

Heureux avec Rook et Rados

L'utilisation de la combinaison Ceph + RBD impose certaines restrictions sur le montage de volumes sur les pods.

En particulier, l'espace de noms doit contenir un secret d'accès à Ceph pour que les applications avec état fonctionnent. Ce n'est pas grave si vous avez 2-3 environnements dans leurs espaces de noms : vous pouvez copier le secret manuellement. Mais que se passerait-il si, pour chaque fonctionnalité, un environnement distinct avec son propre espace de noms était créé pour les développeurs ?

Nous avons résolu ce problème nous-mêmes en utilisant opérateur shell, qui copiait automatiquement les secrets dans de nouveaux espaces de noms (un exemple d'un tel hook est décrit dans cet article).

#! /bin/bash

if [[ $1 == “--config” ]]; then
   cat <<EOF
{"onKubernetesEvent":[
 {"name": "OnNewNamespace",
  "kind": "namespace",
  "event": ["add"]
  }
]}
EOF
else
    NAMESPACE=$(kubectl get namespace -o json | jq '.items | max_by( .metadata.creationTimestamp ) | .metadata.name')
    kubectl -n ${CEPH_SECRET_NAMESPACE} get secret ${CEPH_SECRET_NAME} -o json | jq ".metadata.namespace="${NAMESPACE}"" | kubectl apply -f -
fi

Cependant, lorsque vous utilisez Rook, ce problème n'existe tout simplement pas. Le processus de montage s'effectue à l'aide de ses propres pilotes basés sur Flexvolume ou CSI (encore en phase bêta) et ne nécessite donc pas de secrets.

Rook résout automatiquement de nombreux problèmes, ce qui nous encourage à l'utiliser dans de nouveaux projets.

Siège de Rook

Terminons la partie pratique en déployant Rook et Ceph afin de pouvoir mener nos propres expériences. Pour faciliter la prise d'assaut de cette tour imprenable, les développeurs ont préparé un package Helm. Téléchargeons-le :

$ helm fetch rook-master/rook-ceph --untar --version 1.0.0

En fichier rook-ceph/values.yaml vous pouvez trouver de nombreux paramètres différents. Le plus important est de préciser les tolérances pour les agents et la recherche. Nous avons décrit en détail à quoi peut servir le mécanisme des contaminations/tolérances. cet article.

En bref, nous ne souhaitons pas que les pods des applications clientes soient situés sur les mêmes nœuds que les disques de stockage de données. La raison est simple : de cette façon, le travail des agents Rook n'affectera pas l'application elle-même.

Alors, ouvrez le fichier rook-ceph/values.yaml avec votre éditeur préféré et ajoutez le bloc suivant à la fin :

discover:
  toleration: NoExecute
  tolerationKey: node-role/storage
agent:
  toleration: NoExecute
  tolerationKey: node-role/storage
  mountSecurityMode: Any

Pour chaque nœud réservé au stockage des données, ajoutez la teinte correspondante :

$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecute

Installez ensuite la charte Helm avec la commande :

$ helm install --namespace ${ROOK_NAMESPACE} ./rook-ceph

Vous devez maintenant créer un cluster et spécifier l'emplacement Menu OSD:

apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  clusterName: "ceph"
  finalizers:
  - cephcluster.ceph.rook.io
  generation: 1
  name: rook-ceph
spec:
  cephVersion:
    image: ceph/ceph:v13
  dashboard:
    enabled: true
  dataDirHostPath: /var/lib/rook/osd
  mon:
    allowMultiplePerNode: false
    count: 3
  network:
    hostNetwork: true
  rbdMirroring:
    workers: 1
  placement:
    all:
      tolerations:
      - key: node-role/storage
        operator: Exists
  storage:
    useAllNodes: false
    useAllDevices: false
    config:
      osdsPerDevice: "1"
      storeType: filestore
    resources:
      limits:
        memory: "1024Mi"
      requests:
        memory: "1024Mi"
    nodes:
    - name: host-1
      directories:
      - path: "/mnt/osd"
    - name: host-2
      directories:
      - path: "/mnt/osd"
    - name: host-3
      directories:
      - path: "/mnt/osd"

Vérification de l'état de Ceph - attendez-vous à voir HEALTH_OK:

$ kubectl -n ${ROOK_NAMESPACE} exec $(kubectl -n ${ROOK_NAMESPACE} get pod -l app=rook-ceph-operator -o name -o jsonpath='{.items[0].metadata.name}') -- ceph -s

En parallèle, vérifions que les pods avec l'application client ne finissent pas sur des nœuds réservés à Ceph :

$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName

De plus, des composants supplémentaires peuvent être configurés à volonté. Plus de détails à leur sujet sont indiqués dans documentation. Pour l'administration, nous vous recommandons fortement d'installer le tableau de bord et la boîte à outils.

Rook and hooks : Rook est-il suffisant pour tout ?

Comme vous pouvez le constater, le développement de Rook bat son plein. Mais il reste encore des problèmes qui ne permettent pas d'abandonner complètement la configuration manuelle de Ceph :

  • Pas de pilote de tour ne peut pas exporter des métriques sur l'utilisation des blocs montés, ce qui nous prive de surveillance.
  • Flexvolume et CSI je ne sais pas comment modifiez la taille des volumes (par opposition au même RBD), de sorte que Rook est privé d'un outil utile (et parfois indispensable !).
  • Rook n'est toujours pas aussi flexible que Ceph ordinaire. Si nous souhaitons configurer le pool pour que les métadonnées CephFS soient stockées sur SSD et que les données elles-mêmes soient stockées sur le disque dur, nous devrons enregistrer manuellement des groupes distincts de périphériques dans les cartes CRUSH.
  • Malgré le fait que rook-ceph-operator soit considéré comme stable, il y a actuellement quelques problèmes lors de la mise à niveau de Ceph de la version 13 à la version 14.

résultats

"Pour l'instant, Rook est fermée au monde extérieur par des pions, mais nous pensons qu'un jour, elle jouera un rôle décisif dans le jeu !" (citation inventée spécifiquement pour cet article)

Le projet Rook a sans aucun doute conquis nos cœurs – nous pensons que [avec tous ses avantages et inconvénients] il mérite définitivement votre attention.

Nos projets futurs se résument à faire de rook-ceph un module pour opérateur complémentaire, ce qui rendra son utilisation dans nos nombreux clusters Kubernetes encore plus simple et pratique.

PS

A lire aussi sur notre blog :

Source: habr.com

Ajouter un commentaire