Déployer une base de données vectorielle PostgreSQL sur GKE


Ce tutoriel vous explique comment déployer un cluster de base de données vectorielle PostgreSQL sur Google Kubernetes Engine (GKE).

PostgreSQL est fourni avec un éventail de modules et d'extensions qui étendent les fonctionnalités de la base de données. Dans ce tutoriel, vous allez installer l'extension pgvector sur un cluster PostgreSQL existant déployé dans GKE. L'extension Pgvector vous permet de stocker des vecteurs dans les tables de base de données en ajoutant des types de vecteurs à PostgreSQL. Pgvector propose également des recherches de similarités en exécutant des requêtes SQL courantes.

Nous simplifions le déploiement de l'extension PGvector en déployant d'abord l'opérateur CloudnativePG, car l'opérateur fournit une version groupée de l'extension.

Ce tutoriel est destiné aux administrateurs et architectes de plate-forme cloud, aux ingénieurs en ML et aux professionnels du MLOps (DevOps) qui souhaitent déployer des clusters de base de données PostgreSQL sur GKE.

Objectifs

Dans ce tutoriel, vous allez apprendre à effectuer les opérations suivantes :

  • Déployer l'infrastructure GKE pour PostgreSQL.
  • Installer l'extension pgvector sur le cluster PostgreSQL déployé sur GKE.
  • Déployer et configurer l'opérateur CloudNativePG PostgreSQL avec Helm.
  • Importer un ensemble de données de démonstration et exécuter des requêtes de recherche avec un notebook Jupyter.

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

Dans ce tutoriel, vous utilisez Cloud Shell pour exécuter des commandes. Cloud Shell est un environnement shell permettant de gérer les ressources hébergées sur Google Cloud. Il est doté des outils de ligne de commande préinstallés suivants : Google Cloud CLI, kubectl, Helm et Terraform. Si vous n'utilisez pas Cloud Shell, vous devez installer la Google Cloud CLI.

  1. Connectez-vous à votre compte Google Cloud. Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $ de crédits gratuits pour exécuter, tester et déployer des charges de travail.
  2. Installez Google Cloud CLI.
  3. Pour initialiser gcloudCLI, exécutez la commande suivante :

    gcloud init
  4. Créez ou sélectionnez un projet Google Cloud.

    • Créez un projet Google Cloud :

      gcloud projects create PROJECT_ID

      Remplacez PROJECT_ID par le nom du projet Google Cloud que vous créez.

    • Sélectionnez le projet Google Cloud que vous avez créé :

      gcloud config set project PROJECT_ID

      Remplacez PROJECT_ID par le nom de votre projet Google Cloud.

  5. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  6. Activer les API Cloud Resource Manager, Compute Engine, GKE, and IAM Service Account Credentials :

    gcloud services enable cloudresourcemanager.googleapis.com compute.googleapis.com container.googleapis.com iamcredentials.googleapis.com
  7. Installez Google Cloud CLI.
  8. Pour initialiser gcloudCLI, exécutez la commande suivante :

    gcloud init
  9. Créez ou sélectionnez un projet Google Cloud.

    • Créez un projet Google Cloud :

      gcloud projects create PROJECT_ID

      Remplacez PROJECT_ID par le nom du projet Google Cloud que vous créez.

    • Sélectionnez le projet Google Cloud que vous avez créé :

      gcloud config set project PROJECT_ID

      Remplacez PROJECT_ID par le nom de votre projet Google Cloud.

  10. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  11. Activer les API Cloud Resource Manager, Compute Engine, GKE, and IAM Service Account Credentials :

    gcloud services enable cloudresourcemanager.googleapis.com compute.googleapis.com container.googleapis.com iamcredentials.googleapis.com
  12. Attribuez des rôles à votre compte Google. Exécutez la commande suivante une fois pour chacun des rôles IAM suivants : roles/compute.securityAdmin, roles/compute.viewer, roles/container.clusterAdmin, roles/container.admin, roles/iam.serviceAccountAdmin, roles/iam.serviceAccountUser

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:EMAIL_ADDRESS" --role=ROLE
    • en remplaçant PROJECT_ID par l'ID de votre projet :
    • Remplacez EMAIL_ADDRESS par votre adresse e-mail.
    • Remplacez ROLE par chaque rôle individuel.

Configurer votre environnement

Pour configurer votre environnement avec Cloud Shell, procédez comme suit :

  1. Définissez des variables d'environnement pour votre projet, une région et un préfixe de ressource de cluster Kubernetes :

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    
    • Remplacez PROJECT_ID par l'ID de votre projet Google Cloud.

    Ce tutoriel utilise la région us-central1.

  2. Clonez l'exemple de dépôt de code depuis GitHub :

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. Accédez au répertoire postgres-pgvector :

    cd kubernetes-engine-samples/databases/postgres-pgvector
    

Créer l'infrastructure de votre cluster

Dans cette section, vous allez exécuter un script Terraform pour créer un cluster GKE régional, privé et à disponibilité élevée pour déployer votre base de données PostgreSQL.

Vous pouvez choisir de déployer PostgreSQL à l'aide d'un cluster Standard ou Autopilot. Chacun présente ses propres avantages et différents modèles de tarification.

Autopilot

Pour déployer l'infrastructure du cluster Autopilot, exécutez les commandes suivantes dans Cloud Shell :

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=../postgresql-cloudnativepg/terraform/gke-autopilot init
terraform -chdir=../postgresql-cloudnativepg/terraform/gke-autopilot apply \
-var project_id=${PROJECT_ID} \
-var region=${REGION} \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

GKE remplace les variables suivantes lors de l'exécution :

  • GOOGLE_OAUTH_ACCESS_TOKEN utilise la commande gcloud auth print-access-token pour récupérer un jeton d'accès qui authentifie les interactions avec diverses API Google Cloud.
  • PROJECT_ID, REGION et KUBERNETES_CLUSTER_PREFIX sont les variables d'environnement définies dans la section Configurer votre environnement et attribuées aux nouvelles variables pertinentes pour le cluster Autopilot que vous êtes en train de créer.

Lorsque vous y êtes invité, saisissez yes.

Terraform crée les ressources suivantes :

  • Un réseau VPC personnalisé et un sous-réseau privé pour les nœuds Kubernetes
  • Un routeur cloud pour accéder à Internet via la traduction d'adresse réseau (NAT)
  • Un cluster GKE privé dans la région us-central1.
  • Un ServiceAccount avec les autorisations de journalisation et de surveillance pour le cluster
  • Configuration de Google Cloud Managed Service pour Prometheus pour la surveillance et les alertes relatives au cluster.

Le résultat ressemble à ce qui suit :

...
Apply complete! Resources: 11 added, 0 changed, 0 destroyed.
...

Standard

Pour déployer l'infrastructure du cluster standard, exécutez les commandes suivantes dans Cloud Shell :

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=../postgresql-cloudnativepg/terraform/gke-standard init
terraform -chdir=../postgresql-cloudnativepg/terraform/gke-standard apply \
-var project_id=${PROJECT_ID} \
-var region=${REGION} \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

GKE remplace les variables suivantes lors de l'exécution :

  • GOOGLE_OAUTH_ACCESS_TOKEN utilise la commande gcloud auth print-access-token pour récupérer un jeton d'accès qui authentifie les interactions avec diverses API Google Cloud.
  • PROJECT_ID, REGION et KUBERNETES_CLUSTER_PREFIX sont les variables d'environnement définies dans la section Configurer votre environnement et attribuées aux nouvelles variables pertinentes pour le cluster Standard que vous êtes en train de créer.

Lorsque vous y êtes invité, saisissez yes. L'exécution de ces commandes et le passage du cluster à l'état prêt peuvent prendre plusieurs minutes.

Terraform crée les ressources suivantes :

  • Un réseau VPC personnalisé et un sous-réseau privé pour les nœuds Kubernetes
  • Un routeur cloud pour accéder à Internet via la traduction d'adresse réseau (NAT)
  • Un cluster GKE privé dans la région us-central1 avec l'autoscaling activé (un à deux nœuds par zone)
  • Un ServiceAccount avec les autorisations de journalisation et de surveillance pour le cluster
  • Configuration de Google Cloud Managed Service pour Prometheus pour la surveillance et les alertes relatives au cluster.

Le résultat ressemble à ce qui suit :

...
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.
...

Se connecter au cluster

Configurez kubectl pour récupérer les identifiants et communiquer avec votre nouveau cluster GKE :

gcloud container clusters get-credentials \
    ${KUBERNETES_CLUSTER_PREFIX}-cluster --region ${REGION} --project ${PROJECT_ID}

Déployer l'opérateur CloudNativePG

Déployez CloudNativePG sur votre cluster Kubernetes à l'aide d'un chart Helm :

  1. Vérifiez la version de Helm :

    helm version
    

    Mettez à jour la version si elle est antérieure à la version 3.13 :

    curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
    
  2. Ajoutez le dépôt du chart Helm de l'opérateur CloudNativePG :

    helm repo add cnpg https://cloudnative-pg.github.io/charts
    
  3. Déployez l'opérateur CloudNativePG à l'aide de l'outil de ligne de commande Helm :

    helm upgrade --install cnpg \
        --namespace cnpg-system \
        --create-namespace \
        cnpg/cloudnative-pg
    

    Le résultat ressemble à ce qui suit :

    Release "cnpg" does not exist. Installing it now.
    NAME: cnpg
    LAST DEPLOYED: Fri Oct 13 13:52:36 2023
    NAMESPACE: cnpg-system
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    ...
    

Déployer la base de données vectorielle PostgreSQL

Dans cette section, vous allez déployer la base de données vectorielle PostgreSQL.

  1. Créez un espace de noms pg-ns pour la base de données :

    kubectl create ns pg-ns
    
  2. Appliquez le fichier manifeste pour déployer le cluster PostgreSQL. Le fichier manifeste du cluster active l'extension pgvector.

    kubectl apply -n pg-ns -f manifests/01-basic-cluster/postgreSQL_cluster.yaml
    

    Le fichier manifeste postgreSQL_cluster.yaml décrit le déploiement :

    apiVersion: postgresql.cnpg.io/v1
    kind: Cluster
    metadata:
      name: gke-pg-cluster
    spec:
      description: "Standard GKE PostgreSQL cluster"
      imageName: ghcr.io/cloudnative-pg/postgresql:16.2
      enableSuperuserAccess: true
      instances: 3
      startDelay: 300
      primaryUpdateStrategy: unsupervised
      postgresql:
        pg_hba:
          - host all all 10.48.0.0/20 md5
      bootstrap:
        initdb:
          postInitTemplateSQL:
            - CREATE EXTENSION IF NOT EXISTS vector;
          database: app
      storage:
        storageClass: premium-rwo
        size: 2Gi
      resources:
        requests:
          memory: "1Gi"
          cpu: "1000m"
        limits:
          memory: "1Gi"
          cpu: "1000m"
      affinity:
        enablePodAntiAffinity: true
        tolerations:
        - key: cnpg.io/cluster
          effect: NoSchedule
          value: gke-pg-cluster
          operator: Equal
        additionalPodAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app.component
                  operator: In
                  values:
                  - "pg-cluster"
              topologyKey: topology.kubernetes.io/zone
      monitoring:
        enablePodMonitor: true
  3. Vérifiez l'état du cluster :

    kubectl get cluster -n pg-ns --watch
    

    Attendez que le résultat affiche l'état Cluster in healthy state avant de passer à l'étape suivante.

Importer l'ensemble de données de démonstration et exécuter des requêtes de recherche avec un notebook Jupyter

Dans cette section, vous allez importer des vecteurs dans une table PostgreSQL et exécuter des requêtes de recherche sémantique à l'aide de la syntaxe SQL.

Dans cet exemple, vous utilisez un ensemble de données issu d'un fichier CSV contenant une liste de livres de différents genres. Pgvector sert de moteur de recherche et le pod que vous créez sert de client à interroger la base de données PostgreSQL.

  1. Attendez que le pod maître PostgreSQL soit créé et prêt :

    while [[ $(kubectl get pod -l cnpg.io/cluster=gke-pg-cluster,role=primary -n pg-ns -o 'jsonpath={..status.conditions[?(@.type=="Ready")].status}') != "True" ]]; do
    sleep 5
    done
    
  2. Créez le ConfigMap avec books-dataset et exécutez le pod Jupyter pour interagir avec votre cluster PostgreSQL :

    kubectl create -n pg-ns configmap books-dataset --from-file=manifests/02-notebook/dataset.csv
    kubectl create -n pg-ns configmap notebook --from-file=manifests/02-notebook/vector-database.ipynb
    kubectl apply -n pg-ns -f manifests/02-notebook/jupyter.yaml
    
    • Le secret nommé gke-pg-cluster-superuser créé par l'opérateur CloudNativePG est installé sur le pod client en tant que variables d'environnement nommées CLIENTUSERNAME et CLIENTPASSWORD..
    • Le fichier ConfigMap books-dataset contient un fichier csv avec des données de livre pour la base de données PostgreSQL.
    • Le fichier ConfigMap demo-app contient du code Python permettant de créer la table PostgreSQL à partir de books-dataset.

    Le fichier manifeste jupyter.yaml décrit le déploiement notebook et son service :

    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels: &labels
        app: jupyter-notebook
      name: notebook
    spec:
      ports:
      - port: 8888
      selector: *labels
      type: LoadBalancer
      # type: ClusterIP
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: notebook
      labels: &labels
        app: jupyter-notebook
    spec:
      selector:
        matchLabels: *labels
      template:
        metadata: 
          labels: *labels
        spec:
          containers:
          - name: jupyter
            image: tensorflow/tensorflow:2.15.0-jupyter
            resources:
              requests:
                memory: "4500Mi"
                cpu: "1"
              limits:
                memory: "4500Mi"
                cpu: "1"
            ports:
            - containerPort: 8888
            env:
            - name: CLIENTPASSWORD
              valueFrom:
                secretKeyRef:
                  name: gke-pg-cluster-superuser
                  key: password
            - name: CLIENTUSERNAME
              valueFrom:
                secretKeyRef:
                  name: gke-pg-cluster-superuser
                  key: username
            volumeMounts:
            - name: books-dataset
              mountPath: /usr/local/dataset
            - name: notebook
              mountPath: /tf
          volumes:
          - name: books-dataset
            configMap:
              name: books-dataset
          - name: notebook
            configMap:
              name: notebook
  3. Attendez que GKE démarre le pod Jupyter :

    kubectl wait pods -l app=jupyter-notebook --for condition=Ready --timeout=300s -n pg-ns
    
  4. Obtenez l'URL avec le jeton d'accès pour vous connecter à Jupyter :

    export EXTERNAL_IP=$(kubectl -n pg-ns get svc notebook --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
    kubectl logs deploy/notebook -n pg-ns| grep '^ .*http://127'|sed "s|127.0.0.1|${EXTERNAL_IP}|"
    

    Le résultat ressemble à ce qui suit :

    http://34.123.21.1:8888/tree?token=a1d48d3531c48328695d6901004c94060aa0aa3554ff7463
    
  5. Ouvrez cette URL et cliquez sur le fichier vector-database.ipynb.

  6. Cliquez sur Run > Run all Cells (Exécuter > Exécuter toutes les cellules). Jupyter exécute le code et effectue une requête de recherche sur le texte drama about people and unhappy love.

    Cette requête effectue une recherche sémantique sur la table documents dans PostgreSQL, récupérant au maximum deux résultats ayant le score de correspondance le plus élevé pertinent pour votre requête.

    Le résultat ressemble à ce qui suit :

    Title: Romeo and Juliet, Author: William Shakespeare, Paul Werstine (Editor),
    Barbara A. Mowat (Editor), Paavo Emil Cajander (Translator)
    In Romeo and Juliet, Shakespeare creates a violent world, in which two young
    people fall in love. It is not simply that their families disapprove; the Montagues
    and the Capulets are engaged in a blood feud.In this death-filled setting, the
    movement from love at first sight to the lovers' final union in death seems
    almost inevitable. And yet, this play set in an extraordinary world has become
    the quintessential story of young love. In part because of its exquisite language,
    it is easy to respond as if it were about all young lovers.
    ---------
    Title: A Midsummer Night's Dream, Author: William Shakespeare, Paul Werstine (Editor),
    Barbara A. Mowat (Editor), Catherine Belsey (Contributor)
    Shakespeare's intertwined love polygons begin to get complicated from the start--Demetrius
    and Lysander both want Hermia but she only has eyes for Lysander. Bad news is,
    Hermia's father wants Demetrius for a son-in-law. On the outside is Helena,
    whose unreturned love burns hot for Demetrius. Hermia and Lysander plan to flee
    from the city under cover of darkness but are pursued by an enraged Demetrius
    (who is himself pursued by an enraptured Helena). In the forest, unbeknownst
    to the mortals, Oberon and Titania (King and Queen of the faeries) are having
    a spat over a servant boy. The plot twists up when Oberon's head mischief-maker,
    Puck, runs loose with a flower which causes people to fall in love with the
    first thing they see upon waking. Throw in a group of labourers preparing a
    play for the Duke's wedding (one of whom is given a donkey's head and Titania
    for a lover by Puck) and the complications become fantastically funny.
    ---------
    

Nettoyer

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez les ressources individuelles.

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.

Supprimez un projet Google Cloud :

gcloud projects delete PROJECT_ID

Si vous avez supprimé le projet, le nettoyage est terminé. Si vous n'avez pas supprimé le projet, suivez les étapes ci-après afin de supprimer les ressources individuelles.

Supprimer des ressources individuelles

  1. Définissez les variables d'environnement.

    export PROJECT_ID=${PROJECT_ID}
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    
  2. Exécutez la commande terraform destroy :

    export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
    terraform  -chdir=../postgresql-cloudnativepg/terraform/FOLDER destroy \
    -var project_id=${PROJECT_ID} \
    -var region=${REGION} \
    -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}
    

    Remplacez FOLDER par gke-autopilot ou gke-standard, selon le type de cluster GKE que vous avez créé.

    Lorsque vous y êtes invité, saisissez yes.

Étapes suivantes