在 GKE 上部署 PostgreSQL 矢量数据库


本教程介绍如何在 Google Kubernetes Engine (GKE) 上部署 PostgreSQL 矢量数据库集群。

PostgreSQL 附带一系列模块和扩展程序,可扩展数据库的功能。在本教程中,您将在部署到 GKE 的现有 PostgreSQL 集群上安装 pgvector 扩展程序。借助 Pgvector 扩展程序,您可以将矢量类型添加到 PostgreSQL,从而将矢量存储在数据库表中。Pgvector 还可以通过运行常见 SQL 查询来提供相似性搜索。

我们首先部署 CloudnativePG 运算符以简化 PGvector 扩展程序部署,因为该运算符提供该扩展程序的捆绑版本。

本教程适用于想要在 GKE 上部署 PostgreSQL 数据库集群的云平台管理员和架构师机器学习工程师以及 MLOps (DevOps) 专业人员。

目标

在本教程中,您将学习如何:

  • 为 PostgreSQL 部署 GKE 基础架构。
  • 在部署到 GKE 的 PostgreSQL 集群上安装 pgvector 扩展程序。
  • 使用 Helm 部署和配置 CloudNativePG PostgreSQL 运算符。
  • 使用 Jupyter 笔记本上传演示数据集并运行搜索查询。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

在本教程中,您将使用 Cloud Shell 运行命令。Cloud Shell 是一种 shell 环境,用于管理托管在 Google Cloud 上的资源。它预装了 Google Cloud CLIkubectlHelmTerraform 命令行工具。如果您不使用 Cloud Shell,则必须安装 Google Cloud CLI。

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Enable the Cloud Resource Manager, Compute Engine, GKE, and IAM Service Account Credentials APIs:

    gcloud services enable cloudresourcemanager.googleapis.com compute.googleapis.com container.googleapis.com iamcredentials.googleapis.com
  7. Install the Google Cloud CLI.
  8. To initialize the gcloud CLI, run the following command:

    gcloud init
  9. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  10. Make sure that billing is enabled for your Google Cloud project.

  11. Enable the Cloud Resource Manager, Compute Engine, GKE, and IAM Service Account Credentials APIs:

    gcloud services enable cloudresourcemanager.googleapis.com compute.googleapis.com container.googleapis.com iamcredentials.googleapis.com
  12. Grant roles to your user account. Run the following command once for each of the following IAM roles: 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_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:myemail@example.com.

    • Replace ROLE with each individual role.

设置您的环境

如需使用 Cloud Shell 设置您的环境,请按照以下步骤操作:

  1. 为您的项目、区域和 Kubernetes 集群资源前缀设置环境变量:

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    
    • PROJECT_ID 替换为您的 Google Cloud 项目 ID。

    本教程使用 us-central1 区域。

  2. 从 GitHub 克隆示例代码库:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. 导航到 postgres-pgvector 目录:

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

创建集群基础架构

在本部分中,您将运行 Terraform 脚本来创建高可用性专用区域级 GKE 集群,以部署 PostgreSQL 数据库。

您可以选择使用 Standard 集群或 Autopilot 集群部署 PostgreSQL。每种类型都有自己的优势和不同的价格模式。

Autopilot

若要部署 Autopilot 集群基础架构,请在 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 会在运行时替换以下变量:

  • GOOGLE_OAUTH_ACCESS_TOKEN 使用 gcloud auth print-access-token 命令检索访问令牌,以对与各种 Google Cloud API 的交互进行身份验证
  • PROJECT_IDREGIONKUBERNETES_CLUSTER_PREFIX 是在设置环境部分中定义的环境变量,分配给您要创建的 Autopilot 集群的新相关变量。

出现提示时,请输入 yes

Terraform 会创建以下资源:

  • Kubernetes 节点的自定义 VPC 网络和专用子网。
  • 用于通过网络地址转换 (NAT) 访问互联网的 Cloud Router 路由器。
  • 专用 GKE 集群(在 us-central1 区域中)。
  • 具有集群的日志记录和监控权限的 ServiceAccount
  • 用于集群监控和提醒的 Google Cloud Managed Service for Prometheus 配置。

输出类似于以下内容:

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

标准

若要部署 Standard 集群基础架构,请在 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 会在运行时替换以下变量:

  • GOOGLE_OAUTH_ACCESS_TOKEN 使用 gcloud auth print-access-token 命令检索访问令牌,以对与各种 Google Cloud API 的交互进行身份验证。
  • PROJECT_IDREGIONKUBERNETES_CLUSTER_PREFIX 是在设置环境部分中定义的环境变量,分配给您要创建的标准集群的新相关变量。

出现提示时,请输入 yes。完成这些命令并使集群显示就绪状态可能需要几分钟时间。

Terraform 会创建以下资源:

  • Kubernetes 节点的自定义 VPC 网络和专用子网。
  • 用于通过网络地址转换 (NAT) 访问互联网的 Cloud Router 路由器。
  • 位于 us-central1 区域并且启用了自动扩缩功能的专用 GKE 集群(每个可用区一个到两个节点)。
  • 具有集群的日志记录和监控权限的 ServiceAccount
  • 用于集群监控和提醒的 Google Cloud Managed Service for Prometheus 配置。

输出类似于以下内容:

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

连接到集群

配置 kubectl 以提取凭据并与新的 GKE 集群通信:

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

部署 CloudNativePG 运算符

使用 Helm 图表将 CloudNativePG 部署到 Kubernetes 集群:

  1. 检查 Helm 的版本:

    helm version
    

    更新版本(如果低于 3.13):

    curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
    
  2. 添加 CloudNativePG 运算符 Helm 图表代码库:

    helm repo add cnpg https://cloudnative-pg.github.io/charts
    
  3. 使用 Helm 命令行工具部署 CloudNativePG 运算符:

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

    输出类似于以下内容:

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

部署 PostgreSQL 矢量数据库

在本部分中,您将部署 PostgreSQL 矢量数据库。

  1. 为数据库创建命名空间 pg-ns

    kubectl create ns pg-ns
    
  2. 应用清单以部署 PostgreSQL 集群。集群清单会启用 pgvector 扩展程序。

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

    postgreSQL_cluster.yaml 清单描述了 Deployment:

    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. 检查集群状态:

    kubectl get cluster -n pg-ns --watch
    

    等待输出显示 Cluster in healthy state 状态,然后再转到下一步。

使用 Jupyter 笔记本上传演示数据集并运行搜索查询

在本部分中,您需要将矢量上传到 PostgreSQL 表,并使用 SQL 语法运行语义搜索查询。

在以下示例中,您将使用包含不同类型图书列表的 CSV 文件中的数据集。Pgvector 充当搜索引擎,您创建的 Pod 充当查询 PostgreSQL 数据库的客户端。

  1. 等待 PostgreSQL 主要 Pod 创建完成并准备就绪:

    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. 使用 books-dataset 创建 Configmap 并运行 Jupyter Pod 以与 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
    
    • 由 CloudNativePG 运算符创建的名为 gke-pg-cluster-superuser 的 Secret 将作为名为 CLIENTUSERNAMECLIENTPASSWORD. 的环境变量装载到客户端 Pod 上
    • books-dataset ConfigMap 包含一个 csv 文件,其中包含 PostgreSQL 数据库的图书数据。
    • demo-app ConfigMap 包含用于从 books-dataset 创建 PostgreSQL 表的 Python 代码。

    jupyter.yaml 清单描述了 notebook Deployment 及其 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. 等待 GKE 启动 Jupyter Pod:

    kubectl wait pods -l app=jupyter-notebook --for condition=Ready --timeout=300s -n pg-ns
    
  4. 获取包含用于连接到 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}|"
    

    输出类似于以下内容:

    http://34.123.21.1:8888/tree?token=a1d48d3531c48328695d6901004c94060aa0aa3554ff7463
    
  5. 打开此网址,然后点击 vector-database.ipynb 文件。

  6. 点击运行 > Run all cells。Jupyter 会执行代码并对文本 drama about people and unhappy love 执行搜索查询。

    此查询对 PostgreSQL 中的 documents 表执行语义搜索,检索最多两个具有与查询相关的最高匹配得分的结果。

    输出类似于以下内容:

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

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

删除项目

为了避免产生费用,最简单的方法是删除您为本教程创建的项目。

Delete a Google Cloud project:

gcloud projects delete PROJECT_ID

如果您删除了项目,则表示您的清理已完成。如果您没有删除项目,请继续删除各个资源。

删除各个资源

  1. 设置环境变量。

    export PROJECT_ID=${PROJECT_ID}
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    
  2. 运行 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}
    

    FOLDER 替换为 gke-autopilotgke-standard,具体取决于您创建的 GKE 集群的类型

    出现提示时,请输入 yes

后续步骤