cover operator kubernetes

Introduction

Kubernetes a révolutionné la façon dont nous déployons et gérons les applications dans l’ère cloud-native. Il fournit une plateforme puissante et flexible pour orchestrer efficacement les charges de travail conteneurisées. Cependant, pour étendre les capacités de Kubernetes au-delà de ses fonctionnalités natives, les Custom Resource Definitions (CRDs) et les Opérateurs entrent en jeu. Dans cet article, nous explorerons ce que sont les CRDs et les Opérateurs, comment ils fonctionnent ensemble, comment construire un opérateur et comment ils apportent une valeur immense au déploiement et à la gestion de l’infrastructure et des applications dans l’écosystème Kubernetes.

logo kubernetes

Custom Resource Definitions (CRDs)

À la base, Kubernetes s’appuie sur un ensemble de ressources intégrées comme les Pods, Services et Deployments pour gérer les conteneurs. Cependant, Kubernetes vous permet de définir vos ressources personnalisées via les CRDs, qui agissent essentiellement comme un mécanisme d’extension. Une CRD vous permet de définir un nouveau type de ressource avec ses propres propriétés et comportements.

Pensez aux CRDs comme des plans qui définissent à quoi ressembleront et comment se comporteront vos ressources personnalisées dans le cluster Kubernetes. Une fois que vous définissez une CRD, Kubernetes peut gérer le cycle de vie de ces ressources personnalisées, tout comme ses ressources natives.

Opérateurs

Les opérateurs sont un concept puissant introduit par CoreOS (maintenant Red Hat) qui exploitent les CRDs pour automatiser la gestion d’applications complexes dans Kubernetes. Un Opérateur est essentiellement un contrôleur Kubernetes spécifiquement conçu pour fonctionner avec les CRDs. Il utilise des contrôleurs personnalisés pour surveiller et réagir aux changements dans les ressources personnalisées, automatisant les opérations nécessaires pour maintenir l’état désiré des ressources.

Comment fonctionnent les Opérateurs

Les opérateurs sont adaptés pour gérer les subtilités d’une application particulière ou d’un composant d’infrastructure. Ils comprennent les rouages internes de l’application et agissent en conséquence pour maintenir sa santé et sa disponibilité.

Lorsque vous créez une ressource personnalisée, l’Opérateur surveille les changements de cette ressource. Lors de la détection d’un changement, l’Opérateur prend les mesures appropriées pour réconcilier l’état réel de la ressource avec son état désiré, tel que défini dans la CRD. Cela pourrait impliquer la mise à l’échelle de l’application, l’exécution de sauvegardes, la mise à jour des configurations, ou toute autre tâche spécifique à l’application.

Les opérateurs sont conçus pour être intelligents et autonomes, libérant les développeurs et les opérateurs des tâches manuelles fastidieuses et réduisant la probabilité d’erreurs humaines.

schéma de fonctionnement d'un opérateur kubernetes

Comment écrire un opérateur

Nous allons maintenant prendre quelques minutes pour écrire un opérateur qui génère un UUID comme exemple KISS. Nous utiliserons Kopf, un framework Python, pour créer un opérateur simple. Bien que la plupart des opérateurs sur le marché soient écrits en Golang, Python est également populaire dans le monde Ops. Le choix dépend des connaissances de votre équipe.

D’abord, nous créerons une ressource de type (kind) Uuid. La CRD est créée avec le YAML ci-dessous.

# Une CRD simple pour générer un UUID

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: uuid.demo.operator
spec:
  scope: Namespaced
  group: demo.operator
  names:
    kind: Uuid
    plural: uuid
    singular: uuid
    shortNames:
      - id
      - uuid
  versions:
    - name: v1
      served: true
      storage: true
      additionalPrinterColumns:
      - name: UUID
        type: string
        description: L'UUID généré
        jsonPath: .status.uuid
      - name: Status
        type: string
        description: Indique si le composant a été créé avec succès ou non.
        jsonPath: .status.state
      schema:
        openAPIV3Schema:
          type: object
          properties:
            status:
              properties:
                state:
                  type: string
                uuid: 
                  type: string
                message: 
                  type: string
              type: object

Pour plus d’informations (et il y en a beaucoup) sur les CRDs, consultez la documentation.

Notez que nous avons spécifié notre propre API et version dans la CRD (demo.operator/v1) et que nous avons défini le kind à Uuid. Dans les additionalPrinterColumns, nous avons défini certaines propriétés qui peuvent être définies dans le spec qui seront également affichées à l’écran.

Créer la CRD et les ressources personnalisées ne suffit pas. Pour générer réellement un uuid lorsque la ressource personnalisée est créée, nous devons écrire et exécuter l’opérateur.

Installez d’abord Kopf et le package Kubernetes :

kopf==1.36.0
kubernetes==18.20.0

Mon code pour cet exemple :

import kopf
import logging
import kubernetes
import uuid

@kopf.on.login()
def login_fn(**kwargs):
    return kopf.login_via_client(**kwargs)

@kopf.on.startup()
def configure(settings: kopf.OperatorSettings, **_):
    settings.posting.level = logging.WARNING

@kopf.on.create('uuid') #code logique quand vous créez un objet uuid sur le cluster
def create_fn(spec, name, namespace, **kwargs):
    myuuid = uuid.uuid4()
    try:
        logging.info(f"Demande de création pour la ressource [{name}] CRÉATION dans le namespace [{namespace}]")
        logging.info(f"UUID généré pour la ressource [{name}] UUID [{myuuid}]")
        State = "Ok"
        Message = "Création de la ressource réussie"
    except Exception as e:
        logging.error(f"Exception générée pour uuid [{name}] CRÉATION dans le namespace [{namespace}]: {str(e)}")
        Message = (str(e))
        State = "Erreur"
        myuuid = "NA"
    updateObjectStatus(namespace, name, Message, State, myuuid)

@kopf.on.update('uuid') #code logique quand vous mettez à jour un objet uuid sur le cluster
def update_fn(spec, name, namespace, **kwargs):
    pass

@kopf.on.delete('uuid') #code logique quand vous supprimez un objet uuid sur le cluster
def delete_fn(spec, name, namespace, **kwargs):
    pass

def updateObjectStatus(custom_object_namespace, custom_object_name, Message, State, myuuid):
        custom_object_status = buildObjectStatus(Message, State, myuuid)
        sendObjectStatusToKubernetes(custom_object_namespace, custom_object_name, custom_object_status)

def buildObjectStatus(Message, State, myuuid):
        return {
            'status': {
                'state': State,
                'message': Message,
                'uuid': str(myuuid)
            }
        }

def sendObjectStatusToKubernetes(custom_object_namespace, custom_object_name, custom_object_status):
        kubernetes.client.CustomObjectsApi().patch_namespaced_custom_object(
            namespace=custom_object_namespace,
            name=custom_object_name,
            body=custom_object_status,
            group="demo.operator",
            version="v1",
            plural="uuid",
        )

Exécution de l’opérateur localement, mais la deuxième étape sera de créer une image docker et d’exécuter cet opérateur dans le cluster.

kopf run --standalone --liveness=http://0.0.0.0:8080/healthz app.py

Maintenant vous avez une CRD uuid déployée sur le cluster et l’opérateur. Vous pouvez créer l’objet uuid sur le cluster.

apiVersion: demo.operator/v1
kind: Uuid
metadata:
  name: my-first-ressource

Exemple en direct.

démo d'utilisation d'un operateur kubernetes

Conclusion

Les CRDs et les Opérateurs sont des outils puissants qui étendent les capacités de Kubernetes, en faisant plus qu’une simple plateforme d’orchestration de conteneurs. Avec les CRDs, vous pouvez définir des ressources personnalisées adaptées à vos besoins spécifiques, favorisant la cohérence et la réutilisabilité. Les opérateurs, à leur tour, exploitent ces ressources personnalisées pour automatiser des tâches complexes, consommer des produits et des APIs, s’assurant que vos applications et votre infrastructure sont toujours dans l’état désiré.

En incorporant les CRDs et les Opérateurs dans votre écosystème Kubernetes, vous pouvez simplifier la gestion de l’infrastructure, étendre la façon de consommer les informations de votre système, et rationaliser le déploiement d’applications, résultant en un environnement cloud-native plus efficace et robuste.

Traduction de mon article Medium