1. 简介
在此 Codelab 中,您将了解如何在 GKE 上部署 AlloyDB Omni,并将其与部署在同一 Kubernetes 集群中的开放式嵌入模型结合使用。在同一 GKE 集群中将模型部署在数据库实例旁边,可减少延迟和对第三方服务的依赖。此外,如果数据不应离开组织,并且不允许使用第三方服务,那么安全和合规性要求可能会要求进行本地部署。

前提条件
- 对 Google Cloud 控制台有基本的了解
- 对 Kubernetes 和 GKE 有基本了解
- 具备命令行界面和 Cloud Shell 方面的基本技能
学习内容
- 如何在 Google Kubernetes 集群上部署 AlloyDB Omni
- 如何连接到 AlloyDB Omni
- 如何将数据加载到 AlloyDB Omni
- 如何将开放式嵌入模型部署到 GKE
- 如何在 AlloyDB Omni 中注册嵌入模型
- 如何为语义搜索生成嵌入
- 如何在 AlloyDB Omni 中使用生成的嵌入进行语义搜索
- 如何在 AlloyDB 中创建和使用向量索引
所需条件
- Google Cloud 账号和 Google Cloud 项目
- 支持 Google Cloud 控制台和 Cloud Shell 的网络浏览器,例如 Chrome
2. 设置和要求
项目设置
- 登录 Google Cloud 控制台。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。
请改用个人账号,而非工作账号或学校账号。
- 创建新项目或重复使用现有项目。如需在 Google Cloud 控制台中创建新项目,请在标题中点击“选择项目”按钮,系统随即会打开一个弹出式窗口。

在“选择项目”窗口中,按“新建项目”按钮,系统随即会打开一个用于创建新项目的对话框。

在对话框中,输入您偏好的项目名称,然后选择位置。

- 项目名称是此项目参与者的显示名称。Google API 不会使用项目名称,并且您可以随时更改项目名称。
- 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Google Cloud 控制台会自动生成一个唯一 ID,但您可以自定义该 ID。如果您不喜欢生成的 ID,可以生成另一个随机 ID,也可以提供自己的 ID 来检查其可用性。在大多数 Codelab 中,您都需要引用项目 ID,该 ID 通常用占位符 PROJECT_ID 标识。
- 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档。
启用结算功能
设置个人结算账号
如果您使用 Google Cloud 抵用金设置了结算,则可以跳过此步骤。
如需设置个人结算账号,请点击此处在 Cloud 控制台中启用结算功能。
注意事项:
- 完成本实验的 Cloud 资源费用应低于 3 美元。
- 您可以按照本实验末尾的步骤删除资源,以避免产生更多费用。
- 新用户符合参与 $300 USD 免费试用计划的条件。
启动 Cloud Shell
虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。
在 Google Cloud 控制台 中,点击右上角工具栏中的 Cloud Shell 图标:

或者,您也可以先按 G,然后按 S。如果您位于 Google Cloud 控制台中,或者使用此链接,此序列将激活 Cloud Shell。
预配和连接到环境应该只需要片刻时间。完成后,您应该会看到如下内容:

这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5 GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的所有工作都可以在浏览器中完成。您无需安装任何程序。
3. 准备工作
启用 API
输出如下:
如需使用 Google Kubernetes Engine (GKE) 部署 AlloyDB Omni 和开放模型,您需要在 Google Cloud 项目中启用它们各自的 API。
在 Cloud Shell 中,确保项目 ID 已设置:
PROJECT_ID=$(gcloud config get-value project)
echo $PROJECT_ID
如果未在 Cloud Shell 配置中定义,请使用以下命令进行设置
export PROJECT_ID=<your project>
gcloud config set project $PROJECT_ID
启用所有必要的服务:
gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com
预期输出
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=test-project-001-402417 student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417 Updated property [core/project]. student@cloudshell:~ (test-project-001-402417)$ gcloud services enable compute.googleapis.com gcloud services enable container.googleapis.com Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.
API 简介
- 借助 Kubernetes Engine API (
container.googleapis.com),您可以创建和管理 Google Kubernetes Engine (GKE) 集群。它提供了一个托管式环境,可供您使用 Google 的基础设施来部署、管理和扩缩容器化应用。 - 借助 Compute Engine API (
compute.googleapis.com),您可以创建和管理虚拟机 (VM)、永久性磁盘和网络设置。它提供运行工作负载所需的核心基础设施即服务 (IaaS) 基础,并为许多托管服务托管底层基础架构。
4. 在 GKE 上部署 AlloyDB Omni
如需在 GKE 上部署 AlloyDB Omni,我们需要按照 AlloyDB Omni 操作器要求中列出的要求准备 Kubernetes 集群。
创建 GKE 集群
我们需要部署一个标准 GKE 集群,其池配置足以部署包含 AlloyDB Omni 实例的 pod。对于 AlloyDB Omni,我们需要至少 2 个 CPU 和 8 GB RAM,并为操作器和监控服务容器预留一些空间。我们将使用 e2-standard-4 虚拟机类型。
为部署设置环境变量。
export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=e2-standard-4
然后,我们使用 gcloud 创建 GKE 标准集群。
gcloud container clusters create ${CLUSTER_NAME} \
--project=${PROJECT_ID} \
--region=${LOCATION} \
--workload-pool=${PROJECT_ID}.svc.id.goog \
--release-channel=rapid \
--machine-type=${MACHINE_TYPE} \
--num-nodes=1
预期的控制台输出:
student@cloudshell:~ (gleb-test-short-001-415614)$ export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=n2-highmem-2
Your active configuration is: [gleb-test-short-001-415614]
student@cloudshell:~ (gleb-test-short-001-415614)$ gcloud container clusters create ${CLUSTER_NAME} \
--project=${PROJECT_ID} \
--region=${LOCATION} \
--workload-pool=${PROJECT_ID}.svc.id.goog \
--release-channel=rapid \
--machine-type=${MACHINE_TYPE} \
--num-nodes=1
Note: The Kubelet readonly port (10255) is now deprecated. Please update your workloads to use the recommended alternatives. See https://cloud.google.com/kubernetes-engine/docs/how-to/disable-kubelet-readonly-port for ways to check usage and for migration instructions.
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s).
Creating cluster alloydb-ai-gke in us-central1..
NAME: omni01
ZONE: us-central1-a
MACHINE_TYPE: e2-standard-4
PREEMPTIBLE:
INTERNAL_IP: 10.128.0.3
EXTERNAL_IP: 35.232.157.123
STATUS: RUNNING
student@cloudshell:~ (gleb-test-short-001-415614)$
准备集群
我们需要安装必需的组件,例如 cert-manager 服务(Kubernetes 的原生证书管理器)。我们可以按照文档中的步骤安装 cert-manager
我们将使用 Kubernetes 命令行工具 kubectl,该工具默认已安装在 Cloud Shell 中。在使用该实用程序之前,我们需要获取集群的凭据。
gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${LOCATION}
现在,我们可以使用 kubectl 安装 cert-manager:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.2/cert-manager.yaml
预期的控制台输出(已隐去部分信息):
student@cloudshell:~$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml namespace/cert-manager created customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created ... validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created
安装 AlloyDB Omni
可以使用 Helm 实用程序安装 AlloyDB Omni 操作器。
运行以下命令以安装 AlloyDB Omni 操作器:
export GCS_BUCKET=alloydb-omni-operator
export HELM_PATH=$(gcloud storage cat gs://$GCS_BUCKET/latest)
export OPERATOR_VERSION="${HELM_PATH%%/*}"
gcloud storage cp gs://$GCS_BUCKET/$HELM_PATH ./ --recursive
helm install alloydbomni-operator alloydbomni-operator-${OPERATOR_VERSION}.tgz \
--create-namespace \
--namespace alloydb-omni-system \
--atomic \
--timeout 5m
预期的控制台输出(已隐去部分信息):
student@cloudshell:~$ gcloud storage cp gs://$GCS_BUCKET/$HELM_PATH ./ --recursive
Copying gs://alloydb-omni-operator/1.2.0/alloydbomni-operator-1.2.0.tgz to file://./alloydbomni-operator-1.2.0.tgz
Completed files 1/1 | 126.5kiB/126.5kiB
student@cloudshell:~$ helm install alloydbomni-operator alloydbomni-operator-${OPERATOR_VERSION}.tgz \
> --create-namespace \
> --namespace alloydb-omni-system \
> --atomic \
> --timeout 5m
NAME: alloydbomni-operator
LAST DEPLOYED: Mon Jan 20 13:13:20 2025
NAMESPACE: alloydb-omni-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
student@cloudshell:~$
安装 AlloyDB Omni 操作器后,我们可以继续部署数据库集群。
以下是启用了 googleMLExtension 参数和内部(专用)负载平衡器的部署清单示例:
apiVersion: v1
kind: Secret
metadata:
name: db-pw-my-omni
type: Opaque
data:
my-omni: "VmVyeVN0cm9uZ1Bhc3N3b3Jk"
---
apiVersion: alloydbomni.dbadmin.goog/v1
kind: DBCluster
metadata:
name: my-omni
spec:
databaseVersion: "15.13.0"
primarySpec:
adminUser:
passwordRef:
name: db-pw-my-omni
features:
googleMLExtension:
enabled: true
resources:
cpu: 1
memory: 8Gi
disks:
- name: DataDisk
size: 20Gi
storageClass: standard
dbLoadBalancerOptions:
annotations:
networking.gke.io/load-balancer-type: "internal"
allowExternalIncomingTraffic: true
密码的 Secret 值是密码字词“VeryStrongPassword”的 Base64 表示形式。更可靠的方法是使用 Google Secret Manager 存储密码值。您可以在文档中详细了解相关信息。
将清单另存为 my-omni.yaml,以便在下一步中应用。如果您位于 Cloud Shell 中,可以使用编辑器来执行此操作,只需按终端右上角的“打开编辑器”按钮即可。

将文件另存为 my-omni.yaml 后,按“打开终端”按钮返回终端。

使用 kubectl 实用程序将 my-omni.yaml 清单应用于集群:
kubectl apply -f my-omni.yaml
预期的控制台输出:
secret/db-pw-my-omni created dbcluster.alloydbomni.dbadmin.goog/my-omni created
使用 kubectl 实用程序检查 my-omni 集群的状态:
kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
在部署期间,集群会经历不同的阶段,最终应以 DBClusterReady 状态结束。
预期的控制台输出:
$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default NAME PRIMARYENDPOINT PRIMARYPHASE DBCLUSTERPHASE HAREADYSTATUS HAREADYREASON my-omni 10.131.0.33 Ready DBClusterReady
连接到 AlloyDB Omni
使用 Kubernetes Pod 进行连接
当集群准备就绪后,我们就可以在 AlloyDB Omni 实例 pod 上使用 PostgreSQL 客户端二进制文件了。我们找到 pod ID,然后使用 kubectl 直接连接到 pod 并运行客户端软件。密码为 VeryStrongPassword,这是通过 my-omni.yaml 清单中的 Kubernetes Secret 设置的:
DB_CLUSTER_NAME=my-omni
DB_CLUSTER_NAMESPACE=default
DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'`
kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres
控制台输出示例:
DB_CLUSTER_NAME=my-omni
DB_CLUSTER_NAMESPACE=default
DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'`
kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres
Password for user postgres:
psql (15.7)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_128_GCM_SHA256, compression: off)
Type "help" for help.
postgres=#
5. 在 GKE 上部署 AI 模型
为了测试 AlloyDB Omni AI 与本地模型的集成,我们需要将模型部署到集群。我们将使用 Google 的 EmbeddingGemma 模型。
为模型创建节点池
为了运行模型,我们需要准备一个节点池来运行推理。我们可以使用仅限 CPU 的池或包含 GPU 加速器的池来运行它。在某些地区,由于资源并发性高,仅使用 CPU 的方法可能更可行。在我们的实验中,我们将使用 CPU 方法,但从性能角度来看,最佳方法是使用图形加速器的池,并使用 g2-standard-8(带 L4 Nvidia 加速器)等节点配置。
基于 CPU 的节点池
创建具有 e2-standard-32 节点的节点池。我们将限制从一个节点拉取,以节省资源。
export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container node-pools create cpupool \
--project=${PROJECT_ID} \
--location=${LOCATION} \
--node-locations=${LOCATION}-a \
--cluster=${CLUSTER_NAME} \
--machine-type=c3-standard-8 \
--num-nodes=1
预期输出
student@cloudshell$ export PROJECT_ID=$(gcloud config get project)
Your active configuration is: [pant]
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
student@cloudshell$ gcloud container node-pools create cpupool \
> --project=${PROJECT_ID} \
> --location=${LOCATION} \
> --node-locations=${LOCATION}-a \
> --cluster=${CLUSTER_NAME} \
> --machine-type=c3-standard-8 \
> --num-nodes=1
Creating node pool cpupool...done.
Created [https://container.googleapis.com/v1/projects/gleb-test-short-003-483115/zones/us-central1/clusters/alloydb-ai-gke/nodePools/cpupool].
NAME MACHINE_TYPE DISK_SIZE_GB NODE_VERSION
cpupool c3-standard-8 100 1.34.1-gke.3355002
获取 Hugging Face 令牌
在此实验中,我们将与 Hugging Face 合作部署 EmbeddingGemma 模型,为此我们需要获取 Hugging Face 令牌。
如果您之前没有令牌,请按照以下步骤生成新令牌。
- 使用右上角的“Log In”(登录)或“Sign Up”(注册)链接,在 Hugging Face 网站上登录或注册。
- 依次点击“您的个人资料” ->“访问令牌”
- 确认您的身份
- 点击“创建新令牌”
- 为您的令牌选择一个名称
- 为令牌选择角色 - 您至少需要拥有读取权限
- 点击页面底部的“创建令牌”
- 复制生成的令牌并保存以备后用
您还需要在 https://huggingface.co/google/embeddinggemma-300m 页面上接受相关条件,才能访问与 EmbeddingGemma 相关的文件和内容
使用令牌创建 Kubernetes Secret
在 Cloud Shell 会话中执行(将 HF_TOKEN 的值替换为您的 HF 令牌)。
export HF_TOKEN=hf_QjgW...lfrXF
kubectl create secret generic hf-secret \
--from-literal=hf_api_token=$HF_TOKEN \
--dry-run=client -o yaml | kubectl apply -f -
准备部署清单
如需部署模型,我们需要准备部署清单。
我们使用的是 Hugging Face 中的 Google EmbeddingGemma 模型。您可以点击此处查看模特卡。为了部署模型,我们将使用一种基于 Hugging Face 的说明和 GitHub 的部署软件包的方法。
从 GitHub 克隆软件包
git clone https://github.com/huggingface/Google-Cloud-Containers
调整了 CPU 节点上 TEI(文本嵌入接口)的清单。我们需要替换多个参数,包括模型、图片、正确的资源分配,并将 Hugging Face 令牌密钥添加到配置中。
修改清单(使用任何可用的编辑器)
vi Google-Cloud-Containers/examples/gke/tei-deployment/cpu-config/deployment.yaml
以下是针对基于 CPU 的池进行部署的更正后的清单。
apiVersion: apps/v1
kind: Deployment
metadata:
name: tei-deployment
spec:
replicas: 1
selector:
matchLabels:
app: tei-server
template:
metadata:
labels:
app: tei-server
hf.co/model: Google--embeddinggemma-300m
hf.co/task: text-embeddings
spec:
containers:
- name: tei-container
image: ghcr.io/huggingface/text-embeddings-inference:cpu-latest
#image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-embeddings-inference-cpu.1-4:latest
resources:
requests:
cpu: "6"
memory: "24Gi"
limits:
cpu: "6"
memory: "24Gi"
env:
- name: MODEL_ID
value: google/embeddinggemma-300m
- name: NUM_SHARD
value: "1"
- name: PORT
value: "8080"
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-secret
key: hf_api_token
volumeMounts:
- mountPath: /tmp
name: tmp
volumes:
- name: tmp
emptyDir: {}
nodeSelector:
#cloud.google.com/compute-class: "Performance"
cloud.google.com/machine-family: "c3"
部署模型
应用修改后的 CPU 部署清单来部署模型。
kubectl apply -f Google-Cloud-Containers/examples/gke/tei-deployment/cpu-config
验证部署
kubectl get pods
验证模型服务
kubectl get service tei-service
它应该会显示正在运行的 ClusterIP 服务类型
示例输出:
student@cloudshell$ kubectl get service tei-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE tei-service ClusterIP 34.118.233.48 <none> 8080/TCP 10m
我们将使用该服务的 CLUSTER-IP 作为端点地址。模型嵌入可通过 URI http://34.118.233.48:8080/embed 进行响应。您稍后在 AlloyDB Omni 中注册模型时会用到它。
我们可以使用 kubectl port-forward 命令公开该服务,从而对其进行测试。
kubectl port-forward service/tei-service 8080:8080
如果您使用的是 Cloud Shell,则端口转发可以在一个 Cloud Shell 会话中运行,我们需要另一个会话来测试它。
使用顶部的“+”号打开另一个 Cloud Shell 标签页。

并在新的 shell 会话中运行 curl 命令。
curl http://localhost:8080/embed \
-X POST \
-d '{"inputs":"Test"}' \
-H 'Content-Type: application/json'
它应该会返回一个向量数组,如以下示例输出(已编辑)所示:
curl http://localhost:8080/embed \
> -X POST \
> -d '{"inputs":"Test"}' \
> -H 'Content-Type: application/json'
[[-0.018975832,0.0071419072,0.06347208,0.022992613,0.014205903
...
-0.03677433,0.01636146,0.06731572]]
如果我们看到这些数字,则可以确认我们已成功测试该模型,现在可以在 AlloyDB Omni 中注册该模型,以便直接从 SQL 中使用。
6. 在 AlloyDB Omni 中注册模型
为了测试 AlloyDB Omni 如何与已部署的模型搭配使用,我们需要创建一个数据库并注册该模型。
创建数据库
创建 GCE 虚拟机作为跳板,以便从客户端虚拟机连接到 AlloyDB Omni 并创建数据库。
我们需要跳板,因为 Omni 的 GKE 外部负载平衡器允许您使用专用 IP 地址从 VPC 进行访问,但不允许您从 VPC 外部进行连接。它通常更安全,不会将数据库实例暴露给互联网。请查看图表,以了解详情。

如需在 Cloud Shell 会话中创建虚拟机,请执行以下命令:
export ZONE=us-central1-a
gcloud compute instances create instance-1 \
--zone=$ZONE
在 Cloud Shell 中使用 kubectl 查找 AlloyDB Omni 端点 IP:
kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
记下 PRIMARYENDPOINT。
以下是示例输出:
student@cloudshell:~$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default NAME PRIMARYENDPOINT PRIMARYPHASE DBCLUSTERPHASE HAREADYSTATUS HAREADYREASON my-omni 10.131.0.33 Ready DBClusterReady student@cloudshell:~$
10.131.0.33 是我们将在示例中用于连接到 AlloyDB Omni 实例的 IP。
使用 gcloud 连接到虚拟机:
gcloud compute ssh instance-1 --zone=$ZONE
如果系统提示生成 SSH 密钥,请按照说明操作。如需详细了解 SSH 连接,请参阅文档。
在虚拟机的 SSH 会话中安装 PostgreSQL 客户端:
sudo apt-get update
sudo apt-get install --yes postgresql-client
使用以下示例导出 AlloyDB Omni 负载平衡器 IP 变量(将 IP 替换为您的负载平衡器 IP):
export INSTANCE_IP=10.131.0.33
连接到 AlloyDB Omni,密码为 my-omni.yaml 中通过哈希设置的 VeryStrongPassword:
psql "host=$INSTANCE_IP user=postgres sslmode=require"
在已建立的 psql 会话中,执行以下命令:
create database demo;
退出会话并连接到数据库演示(或者您也可以在同一会话中运行 \c demo)
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
创建转换函数
对于第三方嵌入模型,我们需要创建转换函数,将输入和输出格式化为模型和我们的内部函数所需的格式。这些函数充当翻译器的角色,可在不同接口之间进行格式转换。
以下是处理输入的转换函数:
-- Input Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT)
RETURNS JSON
LANGUAGE plpgsql
AS $$
DECLARE
transformed_input JSON;
model_qualified_name TEXT;
BEGIN
SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input;
RETURN transformed_input;
END;
$$;
在连接到演示数据库的情况下执行提供的代码,如示例输出中所示:
demo=# -- Input Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT)
RETURNS JSON
LANGUAGE plpgsql
AS $$
DECLARE
transformed_input JSON;
model_qualified_name TEXT;
BEGIN
SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input;
RETURN transformed_input;
END;
$$;
CREATE FUNCTION
demo=#
以下是输出函数,用于将模型返回的响应转换为实数数组:
-- Output Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON)
RETURNS REAL[]
LANGUAGE plpgsql
AS $$
DECLARE
transformed_output REAL[];
BEGIN
SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output;
RETURN transformed_output;
END;
$$;
在同一会话中执行该命令:
demo=# -- Output Transform Function corresponding to the custom model endpoint CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON) RETURNS REAL[] LANGUAGE plpgsql AS $$ DECLARE transformed_output REAL[]; BEGIN SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output; RETURN transformed_output; END; $$; CREATE FUNCTION demo=#
注册模型
现在,我们可以在数据库中注册模型。
以下是用于注册名称为 embeddinggemma 的模型的程序调用。注册模型时,我们在 model_request_url 参数中使用 tei-service 服务名称。这是内部 Kubernetes 集群服务名称,可转换为 GKE 集群中的内部 IP:
CALL
google_ml.create_model(
model_id => 'embeddinggemma',
model_request_url => 'http://tei-service:8080/embed',
model_provider => 'custom',
model_type => 'text_embedding',
model_in_transform_fn => 'tei_text_input_transform',
model_out_transform_fn => 'tei_text_output_transform');
在连接到演示数据库时执行提供的代码:
demo=# CALL
google_ml.create_model(
model_id => 'embeddinggemma',
model_request_url => 'http://tei-service:8080/embed',
model_provider => 'custom',
model_type => 'text_embedding',
model_in_transform_fn => 'tei_text_input_transform',
model_out_transform_fn => 'tei_text_output_transform');
CALL
demo=#
我们可以使用以下测试查询来测试注册模型,该查询应返回一个实数数组。
select google_ml.embedding('embeddinggemma','What is AlloyDB Omni?');
请不要对长时间延迟后才收到矢量数据感到意外。在此测试中,我们使用基于 CPU 的节点池来托管嵌入模型,该模型在配备 GPU 的节点上运行速度更快。
7. 在 AlloyDB Omni 中测试模型
加载数据
为了测试 AlloyDB Omni 如何与已部署的模型搭配使用,我们需要加载一些数据。我使用了与 AlloyDB 中的向量搜索相关的其他 Codelab 中相同的数据。
一种加载数据的方法是使用 Google Cloud SDK 和 PostgreSQL 客户端软件。我们可以使用相同的客户端虚拟机。如果您为虚拟机映像使用了默认设置,则 Google Cloud SDK 应该已安装在该处。不过,如果您使用的是没有 Google SDK 的自定义映像,可以按照文档添加该 SDK。
按以下示例所示导出 AlloyDB Omni 负载平衡器 IP(将 IP 替换为您的负载平衡器 IP):
export INSTANCE_IP=10.131.0.33
连接到数据库并启用 pgvector 扩展程序。
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
在 psql 会话中:
CREATE EXTENSION IF NOT EXISTS vector;
退出 psql 会话,然后在命令行会话中执行命令,将数据加载到演示数据库。
创建表格。以下命令将获取 cymbal_demo_schema.sql 文件,并执行包含演示数据库中所有表定义的 SQL:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"
预期的控制台输出:
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo" Password for user postgres: SET SET SET SET SET set_config ------------ (1 row) SET SET SET SET SET SET CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE CREATE TABLE ALTER TABLE CREATE SEQUENCE ALTER TABLE ALTER SEQUENCE ALTER TABLE ALTER TABLE ALTER TABLE student@cloudshell:~$
以下是已创建的表的列表:
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"
输出:
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"
Password for user postgres:
List of relations
Schema | Name | Type | Owner | Persistence | Access method | Size | Description
--------+------------------+-------+----------+-------------+---------------+------------+-------------
public | cymbal_embedding | table | postgres | permanent | heap | 8192 bytes |
public | cymbal_inventory | table | postgres | permanent | heap | 8192 bytes |
public | cymbal_products | table | postgres | permanent | heap | 8192 bytes |
public | cymbal_stores | table | postgres | permanent | heap | 8192 bytes |
(4 rows)
student@cloudshell:~$
将数据加载到 cymbal_products 表中:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header"
预期的控制台输出:
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header" COPY 941 student@cloudshell:~$
以下是 cymbal_products 表中几行的示例。
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3"
输出:
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3"
Password for user postgres:
uniq_id | left | left | sale_price
----------------------------------+--------------------------------+----------------------------------------------------+------------
a73d5f754f225ecb9fdc64232a57bc37 | Laundry Tub Strainer Cup | Laundry tub strainer cup Chrome For 1-.50, drain | 11.74
41b8993891aa7d39352f092ace8f3a86 | LED Starry Star Night Light La | LED Starry Star Night Light Laser Projector 3D Oc | 46.97
ed4a5c1b02990a1bebec908d416fe801 | Surya Horizon HRZ-1060 Area Ru | The 100% polypropylene construction of the Surya | 77.4
(3 rows)
student@cloudshell:~$
将数据加载到 cymbal_inventory 表中:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header"
预期的控制台输出:
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header" Password for user postgres: COPY 263861 student@cloudshell:~$
以下是 cymbal_inventory 表中几行的示例。
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"
输出:
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"
Password for user postgres:
store_id | uniq_id | inventory
----------+----------------------------------+-----------
1583 | adc4964a6138d1148b1d98c557546695 | 5
1490 | adc4964a6138d1148b1d98c557546695 | 4
1492 | adc4964a6138d1148b1d98c557546695 | 3
(3 rows)
student@cloudshell:~$
将数据加载到 cymbal_stores 表中:
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header"
预期的控制台输出:
student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header" Password for user postgres: COPY 4654 student@cloudshell:~$
以下是 cymbal_stores 表中几行的示例。
psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"
输出:
student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"
Password for user postgres:
store_id | name | zip_code
----------+-------------------+----------
1990 | Mayaguez Store | 680
2267 | Ware Supercenter | 1082
4359 | Ponce Supercenter | 780
(3 rows)
student@cloudshell:~$
构建嵌入
使用 psql 连接到演示数据库,并根据 cymbal_products 表中所述的产品说明为这些产品构建嵌入内容。
连接到演示数据库:
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
我们使用具有列嵌入的 cymbal_embedding 表来存储嵌入,并使用商品说明作为函数的文本输入。
为查询启用计时功能,以便稍后与远程模型进行比较。
\timing
运行查询以构建嵌入:
INSERT INTO cymbal_embedding(uniq_id,embedding) SELECT uniq_id, google_ml.embedding('embeddinggemma',product_description)::vector FROM cymbal_products;
预期的控制台输出:
demo=# INSERT INTO cymbal_embedding(uniq_id,embedding) SELECT uniq_id, google_ml.embedding('embeddinggemma',product_description)::vector FROM cymbal_products;
INSERT 0 941
Time: 497878.136 ms (08:17.878)
demo=#
在此示例中,构建嵌入大约花费了 8 分钟。对于基于 CPU 的节点池,这是预期行为。对于具有 GPU 加速器的池,速度可能会快得多,具体取决于 GPU 类型。
运行测试查询
使用 psql 连接到演示数据库,并启用计时功能,以便像构建嵌入时一样测量查询的执行时间。
假设我们使用余弦距离作为向量搜索的算法,现在要查找与“这里适合种植哪种果树?”这类请求最匹配的 5 种产品。
在 psql 会话中,执行以下命令:
SELECT
cp.product_name,
left(cp.product_description,80) as description,
cp.sale_price,
cs.zip_code,
(ce.embedding <=> google_ml.embedding('embeddinggemma','What kind of fruit trees grow well here?')::vector) as distance
FROM
cymbal_products cp
JOIN cymbal_embedding ce on
ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on
ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
cs.store_id=ci.store_id
AND ci.inventory>0
AND cs.store_id = 1583
ORDER BY
distance ASC
LIMIT 5;
预期的控制台输出:
demo=# SELECT
cp.product_name,
left(cp.product_description,80) as description,
cp.sale_price,
cs.zip_code,
(ce.embedding <=> google_ml.embedding('embeddinggemma','What kind of fruit trees grow well here?')::vector) as distance
FROM
cymbal_products cp
JOIN cymbal_embedding ce on
ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on
ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
cs.store_id=ci.store_id
AND ci.inventory>0
AND cs.store_id = 1583
ORDER BY
distance ASC
LIMIT 5;
product_name | description | sale_price | zip_code | distance
-----------------------+----------------------------------------------------------------------------------+------------+----------+--------------------
Cherry Tree | This is a beautiful cherry tree that will produce delicious cherries. It is an d | 75.00 | 93230 | 0.5210549378080666
California Lilac | This is a beautiful lilac tree that can grow to be over 10 feet tall. It is an d | 5.00 | 93230 | 0.5639421771781971
Toyon | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e | 10.00 | 93230 | 0.5670010914504852
Rose Bush | This is a beautiful rose bush that will produce fragrant roses. It is a perennia | 50.00 | 93230 | 0.5731542622882957
California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e | 25.00 | 93230 | 0.5750934653011995
(5 rows)
Time: 83.610 ms
demo=#
该查询运行了 83 毫秒,并返回了 cymbal_products 表中与请求匹配且在商店 1583 中有库存的树的列表。
构建 ANN 索引
如果我们只有少量数据集,则可以轻松使用精确搜索来扫描所有嵌入内容,但随着数据量的增加,加载时间和响应时间也会随之增加。为了提高性能,您可以为嵌入数据构建索引。下面是一个示例,展示了如何使用 Google ScaNN 索引处理向量数据。
如果您与演示数据库的连接断开,请重新连接:
psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"
启用 alloydb_scann 扩展程序:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
构建索引:
CREATE INDEX cymbal_embedding_scann ON cymbal_embedding USING scann (embedding cosine);
尝试与之前相同的查询,并比较结果:
demo=# SELECT
cp.product_name,
left(cp.product_description,80) as description,
cp.sale_price,
cs.zip_code,
(ce.embedding <=> google_ml.embedding('embeddinggemma','What kind of fruit trees grow well here?')::vector) as distance
FROM
cymbal_products cp
JOIN cymbal_embedding ce on
ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on
ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
cs.store_id=ci.store_id
AND ci.inventory>0
AND cs.store_id = 1583
ORDER BY
distance ASC
LIMIT 5;
product_name | description | sale_price | zip_code | distance
-----------------------+----------------------------------------------------------------------------------+------------+----------+--------------------
Cherry Tree | This is a beautiful cherry tree that will produce delicious cherries. It is an d | 75.00 | 93230 | 0.5210549378080666
California Lilac | This is a beautiful lilac tree that can grow to be over 10 feet tall. It is an d | 5.00 | 93230 | 0.5639421771781971
Toyon | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e | 10.00 | 93230 | 0.5670010914504852
Rose Bush | This is a beautiful rose bush that will produce fragrant roses. It is a perennia | 50.00 | 93230 | 0.5731542622882957
California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e | 25.00 | 93230 | 0.5750934653011995
(5 rows)
Time: 64.783 ms
查询执行时间略有减少,并且在处理较大数据集时,这种增益会更加明显。结果非常相似,并且我们得到了相同的前 5 棵树。
您可以尝试其他查询,并参阅相关文档,详细了解如何选择向量索引。
别忘了,AlloyDB Omni 还提供更多功能和实验。
8. 清理环境
现在,我们可以删除包含 AlloyDB Omni 和 AI 模型的 GKE 集群了
删除 GKE 集群
在 Cloud Shell 中,执行以下命令:
export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container clusters delete ${CLUSTER_NAME} \
--project=${PROJECT_ID} \
--region=${LOCATION}
预期的控制台输出:
student@cloudshell:~$ gcloud container clusters delete ${CLUSTER_NAME} \
> --project=${PROJECT_ID} \
> --region=${LOCATION}
The following clusters will be deleted.
- [alloydb-ai-gke] in [us-central1]
Do you want to continue (Y/n)? Y
Deleting cluster alloydb-ai-gke...done.
Deleted
删除虚拟机
在 Cloud Shell 中,执行以下命令:
export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
--project=${PROJECT_ID} \
--zone=${ZONE}
预期的控制台输出:
student@cloudshell:~$ export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
--project=${PROJECT_ID} \
--zone=${ZONE}
Your active configuration is: [cloudshell-5399]
The following instances will be deleted. Any attached disks configured to be auto-deleted will be deleted unless they are attached to any other instances or the `--keep-disks` flag is given and specifies them for keeping. Deleting a disk
is irreversible and any data on the disk will be lost.
- [instance-1] in [us-central1-a]
Do you want to continue (Y/n)? Y
Deleted
如果您为此 Codelab 创建了一个新项目,则可以改为删除整个项目:https://console.cloud.google.com/cloud-resource-manager
9. 恭喜
恭喜您完成此 Codelab。
所学内容
- 如何在 Google Kubernetes 集群上部署 AlloyDB Omni
- 如何连接到 AlloyDB Omni
- 如何将数据加载到 AlloyDB Omni
- 如何将开放式嵌入模型部署到 GKE
- 如何在 AlloyDB Omni 中注册嵌入模型
- 如何为语义搜索生成嵌入
- 如何在 AlloyDB Omni 中使用生成的嵌入进行语义搜索
- 如何在 AlloyDB 中创建和使用向量索引
如需详细了解如何在 AlloyDB Omni 中使用 AI,请参阅文档。
10. 调查问卷
输出如下: