Kubernetes의 AlloyDB Omni 및 로컬 AI 모델

1. 소개

이 Codelab에서는 GKE에 AlloyDB Omni를 배포하고 동일한 Kubernetes 클러스터에 배포된 개방형 삽입 모델과 함께 사용하는 방법을 알아봅니다. 동일한 GKE 클러스터의 데이터베이스 인스턴스 옆에 모델을 배포하면 지연 시간과 서드 파티 서비스에 대한 종속 항목이 줄어듭니다. 또한 데이터가 조직 외부로 유출되어서는 안 되고 서드 파티 서비스의 사용이 허용되지 않는 경우 보안 요구사항에 따라 필요할 수 있습니다.

391e4244b25a7db0.png

기본 요건

  • Google Cloud, 콘솔에 관한 기본적인 이해
  • 명령줄 인터페이스 및 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. 설정 및 요구사항

자습형 환경 설정

  1. Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
  • 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔은 고유한 문자열을 자동으로 생성합니다. 일반적으로는 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로 PROJECT_ID로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 다른 임의 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다.
  • 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
  1. 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스 또는 프로젝트를 삭제하면 됩니다. Google Cloud 신규 사용자는 300달러(USD) 상당의 무료 체험판 프로그램에 참여할 수 있습니다.

Cloud Shell 시작

Google Cloud를 노트북에서 원격으로 실행할 수 있지만, 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Google Cloud Shell을 사용합니다.

Google Cloud Console의 오른쪽 상단 툴바에 있는 Cloud Shell 아이콘을 클릭합니다.

55efc1aaa7a4d3ad.png

환경을 프로비저닝하고 연결하는 데 몇 분 정도 소요됩니다. 완료되면 다음과 같이 표시됩니다.

7ffe5cbb04455448.png

가상 머신에는 필요한 개발 도구가 모두 들어있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab의 모든 작업은 브라우저 내에서 수행할 수 있습니다. 아무것도 설치할 필요가 없습니다.

3. 시작하기 전에

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.

4. GKE에 AlloyDB Omni 배포

GKE에 AlloyDB Omni를 배포하려면 AlloyDB Omni 연산자 요구사항에 나열된 요구사항에 따라 Kubernetes 클러스터를 준비해야 합니다.

GKE 클러스터 만들기

AlloyDB Omni 인스턴스가 포함된 포드를 배포하기에 충분한 풀 구성으로 표준 GKE 클러스터를 배포해야 합니다. Omni를 사용하려면 최소 2개의 CPU와 8GB의 RAM이 필요하며 운영자 및 모니터링 서비스에 여유 공간이 있어야 합니다.

배포의 환경 변수를 설정합니다.

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 서비스와 같은 필수 구성요소를 설치해야 합니다. cert-manager 설치 문서의 단계를 따르세요.

Cloud Shell에 이미 설치된 Kubernetes 명령줄 도구인 kubectl을 사용합니다. 이 유틸리티를 사용하기 전에 클러스터의 사용자 인증 정보를 가져와야 합니다.

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.16.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 설치

AlloyDB Omni 설치 연산자는 helm 유틸리티를 사용하여 설치할 수 있습니다.

다음 명령어를 실행하여 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.7.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

비밀번호의 보안 비밀 값은 비밀번호 단어 'VeryStrongPassword'의 Base64 표현입니다. 더 안정적인 방법은 Google Secret Manager를 사용하여 비밀번호 값을 저장하는 것입니다. 자세한 내용은 문서를 참고하세요.

매니페스트를 my-omni.yaml로 저장하여 다음 단계에서 적용합니다. Cloud Shell에 있는 경우 터미널 오른쪽 상단의 '편집기 열기' 버튼을 눌러 편집기를 사용하여 이 작업을 수행할 수 있습니다.

47ab85dad9afdff7.png

my-omni.yaml이라는 이름으로 파일을 저장한 후 '터미널 열기' 버튼을 눌러 터미널로 돌아갑니다.

b9b7747b39dbe8c7.png

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 포드를 사용하여 연결

클러스터가 준비되면 AlloyDB Omni 인스턴스 팟에서 PostgreSQL 클라이언트 바이너리를 사용할 수 있습니다. 포드 ID를 찾은 다음 kubectl을 사용하여 포드에 직접 연결하고 클라이언트 소프트웨어를 실행합니다. 비밀번호는 my-omni.yaml의 해시를 통해 설정된 VeryStrongPassword입니다.

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와 로컬 모델의 통합을 테스트하려면 클러스터에 모델을 배포해야 합니다.

모델의 노드 풀 만들기

모델을 실행하려면 추론을 실행할 노드 풀을 준비해야 합니다. 성능 관점에서 가장 좋은 접근 방식은 L4 Nvidia 가속기가 있는 g2-standard-8과 같은 노드 구성을 사용하는 그래픽 가속기가 있는 풀입니다.

L4 가속기를 사용하여 노드 풀을 만듭니다.

export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container node-pools create gpupool \
  --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \
  --project=${PROJECT_ID} \
  --location=${LOCATION} \
  --node-locations=${LOCATION}-a \
  --cluster=${CLUSTER_NAME} \
  --machine-type=g2-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 gpupool \
>   --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \
>   --project=${PROJECT_ID} \
>   --location=${LOCATION} \
>   --node-locations=${LOCATION}-a \
>   --cluster=${CLUSTER_NAME} \
>   --machine-type=g2-standard-8 \
>   --num-nodes=1
Note: Machines with GPUs have certain limitations which may affect your workflow. Learn more at https://cloud.google.com/kubernetes-engine/docs/how-to/gpus
Note: Starting in GKE 1.30.1-gke.115600, if you don't specify a driver version, GKE installs the default GPU driver for your node's GKE version.
Creating node pool gpupool...done.
Created [https://container.googleapis.com/v1/projects/student-test-001/zones/us-central1/clusters/alloydb-ai-gke/nodePools/gpupool].
NAME     MACHINE_TYPE   DISK_SIZE_GB  NODE_VERSION
gpupool  g2-standard-8  100           1.31.4-gke.1183000

배포 매니페스트 준비

모델을 배포하려면 배포 매니페스트를 준비해야 합니다.

Hugging Face의 BGE Base v1.5 임베딩 모델을 사용합니다. 여기에서 모델 카드를 확인할 수 있습니다. 모델을 배포하려면 Hugging Face에서 이미 준비된 안내와 GitHub의 배포 패키지를 사용하면 됩니다.

패키지 클론

git clone https://github.com/huggingface/Google-Cloud-Containers

cloud.google.com/gke-accelerator 값을 nvidia-l4로 바꾸고 리소스에 제한을 추가하여 매니페스트를 수정합니다.

vi Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config/deployment.yaml

수정된 매니페스트는 다음과 같습니다.

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: Snowflake--snowflake-arctic-embed-m
        hf.co/task: text-embeddings
    spec:
      containers:
        - name: tei-container
          image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-embeddings-inference-cu122.1-4.ubuntu2204:latest
          resources:
            requests:
              nvidia.com/gpu: 1
            limits:
              nvidia.com/gpu: 1
          env:
            - name: MODEL_ID
              value: Snowflake/snowflake-arctic-embed-m
            - name: NUM_SHARD
              value: "1"
            - name: PORT
              value: "8080"
          volumeMounts:
            - mountPath: /dev/shm
              name: dshm
            - mountPath: /data
              name: data
      volumes:
        - name: dshm
          emptyDir:
            medium: Memory
            sizeLimit: 1Gi
        - name: data
          emptyDir: {}
      nodeSelector:
        cloud.google.com/gke-accelerator: nvidia-l4

모델 배포

배포를 위한 서비스 계정과 네임스페이스를 준비해야 합니다.

Kubernetes 네임스페이스 hf-gke-namespace를 만듭니다.

export NAMESPACE=hf-gke-namespace
kubectl create namespace $NAMESPACE

Kubernetes 서비스 계정 만들기

export SERVICE_ACCOUNT=hf-gke-service-account
kubectl create serviceaccount $SERVICE_ACCOUNT --namespace $NAMESPACE

모델 배포

kubectl apply -f Google-Cloud-Containers/examples/gke/tei-deployment/gpu-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 탭을 엽니다.

4ca978f5142bb6ce.png

새 셸 세션에서 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]]

6. AlloyDB Omni에 모델 등록

배포된 모델과 함께 AlloyDB Omni가 작동하는 방식을 테스트하려면 데이터베이스를 만들고 모델을 등록해야 합니다.

데이터베이스 만들기

GCE VM을 점프 박스로 만들고 클라이언트 VM에서 AlloyDB Omni에 연결한 후 데이터베이스를 만듭니다.

Omni용 GKE 외부 부하 분산기를 사용하면 비공개 IP 주소 지정을 통해 VPC에서 액세스할 수 있지만 VPC 외부에서 연결할 수는 없으므로 점프박스가 필요합니다. 일반적으로 더 안전하며 데이터베이스 인스턴스를 인터넷에 노출하지 않습니다. 다이어그램을 명확하게 확인하세요.

391e4244b25a7db0.png

Cloud Shell 세션에서 VM을 만들려면 다음을 실행합니다.

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를 기록합니다. 예를 들면 다음과 같습니다.

output:

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를 사용하여 VM에 연결합니다.

gcloud compute ssh instance-1 --zone=$ZONE 

SSH 키 생성 메시지가 표시되면 안내에 따라 진행합니다. 문서에서 SSH 연결에 관해 자세히 알아보세요.

VM의 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=#

모델 등록

이제 데이터베이스에 모델을 등록할 수 있습니다.

다음은 이름이 bge-base-1.5인 모델을 등록하는 절차 호출입니다. IP 34.118.233.48을 모델 서비스 IP 주소 (kubectl get service tei-service의 출력)로 바꿉니다.

CALL
  google_ml.create_model(
    model_id => 'bge-base-1.5',
    model_request_url => 'http://34.118.233.48: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 => 'bge-base-1.5',
    model_request_url => 'http://34.118.233.48: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('bge-base-1.5','What is AlloyDB Omni?');

7. AlloyDB Omni에서 모델 테스트

데이터 로드

AlloyDB Omni이 배포된 모델과 함께 작동하는 방식을 테스트하려면 데이터를 로드해야 합니다. AlloyDB에서 벡터 검색을 위해 다른 Codelab 중 하나와 동일한 데이터를 사용했습니다.

데이터를 로드하는 한 가지 방법은 Google Cloud SDK와 PostgreSQL 클라이언트 소프트웨어를 사용하는 것입니다. 데모 데이터베이스를 만드는 데 사용한 것과 동일한 클라이언트 VM을 사용할 수 있습니다. VM 이미지에 기본값을 사용한 경우 Google Cloud SDK가 이미 설치되어 있어야 합니다. 하지만 Google 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 세션을 종료하고 명령줄 세션에서 명령어를 실행하여 데이터를 데모 데이터베이스에 로드합니다.

테이블을 만듭니다.

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('bge-base-1.5',product_description)::vector FROM cymbal_products;

예상되는 콘솔 출력:

demo=#  INSERT INTO cymbal_embedding(uniq_id,embedding)  SELECT uniq_id, google_ml.embedding('bge-base-1.5',product_description)::vector FROM cymbal_products;
INSERT 0 941
Time: 11069.762 ms (00:11.070)
demo=#

이 예시에서는 941개의 레코드에 대한 임베딩을 빌드하는 데 약 11초가 걸렸습니다.

테스트 쿼리 실행

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('bge-base-1.5','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('bge-base-1.5','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
-----------------------+----------------------------------------------------------------------------------+------------+----------+---------------------
 California Sycamore   | This is a beautiful sycamore tree that can grow to be over 100 feet tall. It is  |     300.00 |    93230 | 0.22753925487632942
 Toyon                 | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e |      10.00 |    93230 | 0.23497374266229387
 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e |      25.00 |    93230 | 0.24215884459965364
 California Redwood    | This is a beautiful redwood tree that can grow to be over 300 feet tall. It is a |    1000.00 |    93230 | 0.24564130578287147
 Cherry Tree           | This is a beautiful cherry tree that will produce delicious cherries. It is an d |      75.00 |    93230 | 0.24846117929767153
(5 rows)

Time: 28.724 ms
demo=#

쿼리는 28ms 동안 실행되었으며 요청과 일치하고 매장에서 사용 가능한 인벤토리 번호가 1583인 cymbal_products 테이블의 나무 목록을 반환했습니다.

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('bge-base-1.5','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
-----------------------+----------------------------------------------------------------------------------+------------+----------+---------------------
 California Sycamore   | This is a beautiful sycamore tree that can grow to be over 100 feet tall. It is  |     300.00 |    93230 | 0.22753925487632942
 Toyon                 | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e |      10.00 |    93230 | 0.23497374266229387
 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e |      25.00 |    93230 | 0.24215884459965364
 California Redwood    | This is a beautiful redwood tree that can grow to be over 300 feet tall. It is a |    1000.00 |    93230 | 0.24564130578287147
 Fremont Cottonwood    | This is a beautiful cottonwood tree that can grow to be over 100 feet tall. It i |     200.00 |    93230 |  0.2533482837690365
(5 rows)

Time: 14.665 ms
demo=#

쿼리 실행 시간이 약간 줄었고 이 이점은 대규모 데이터 세트에서 더 두드러집니다. 결과는 매우 유사하며 Cherry만 Fremont Cottonwood로 대체되었습니다.

다른 쿼리를 시도하고 문서에서 벡터 색인 선택에 관해 자세히 알아보세요.

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

VM 삭제

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. 설문조사

결과:

본 튜토리얼을 어떻게 사용하실 계획인가요?

읽기만 할 계획입니다. 읽은 다음 연습 활동을 완료할 계획입니다.