Initial commit
Some checks failed
Continuous Integration - Pull Request / code-tests (pull_request) Has been cancelled
Continuous Integration - Pull Request / deployment-tests (local-code) (pull_request) Has been cancelled
helm-chart-ci / helm-chart-ci (pull_request) Has been cancelled
kubevious-manifests-ci / kubevious-manifests-ci (pull_request) Has been cancelled
kustomize-build-ci / kustomize-build-ci (pull_request) Has been cancelled
terraform-validate-ci / terraform-validate-ci (pull_request) Has been cancelled
Clean up deployment / cleanup-namespace (pull_request) Has been cancelled
Continuous Integration - Main/Release / code-tests (push) Has been cancelled
Continuous Integration - Main/Release / deployment-tests (local-code) (push) Has been cancelled
helm-chart-ci / helm-chart-ci (push) Has been cancelled
kubevious-manifests-ci / kubevious-manifests-ci (push) Has been cancelled
kustomize-build-ci / kustomize-build-ci (push) Has been cancelled
terraform-validate-ci / terraform-validate-ci (push) Has been cancelled

This commit is contained in:
2026-02-04 20:47:56 +05:30
commit dafcd9777f
363 changed files with 52703 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
# Shopping Assistant with RAG & AlloyDB
This demo adds a new service to Online Boutique called `shoppingassistantservice` which, alongside an Alloy-DB backed products catalog, adds a RAG-featured AI assistant to the frontend experience, helping users suggest products matching their home decor.
## Setup instructions
**Note:** This demo requires a Google Cloud project where you to have the `owner` role, else you may be unable to enable APIs or modify VPC rules that are needed for this demo.
1. Set some environment variables.
```sh
export PROJECT_ID=<project_id>
export PROJECT_NUMBER=<project_number>
export PGPASSWORD=<pgpassword>
```
**Note**: The project ID and project number of your Google Cloud project can be found in the Console. The PostgreSQL password can be set to anything you want, but make sure to note it down.
1. Change your default Google Cloud project.
```sh
gcloud auth login
gcloud config set project $PROJECT_ID
```
1. Enable the Google Kubernetes Engine (GKE) and Artifact Registry (AR) APIs.
```sh
gcloud services enable container.googleapis.com
gcloud services enable artifactregistry.googleapis.com
```
1. Create a GKE Autopilot cluster. This may take a few minutes.
```sh
gcloud container clusters create-auto cymbal-shops \
--region=us-central1
```
1. Change your Kubernetes context to your newly created GKE cluster.
```sh
gcloud container clusters get-credentials cymbal-shops \
--region us-central1
```
1. Create an Artifact Registry container image repository.
```sh
gcloud artifacts repositories create images \
--repository-format=docker \
--location=us-central1
```
1. Clone the `microservices-demo` repository locally.
```sh
git clone https://github.com/GoogleCloudPlatform/microservices-demo \
&& cd microservices-demo/
```
1. Run script #1. If it asks about policy bindings, select the option `None`. This may take a few minutes.
```sh
./kustomize/components/shopping-assistant/scripts/1_deploy_alloydb_infra.sh
```
**Note**: If you are on macOS and use a non-GNU version of `sed`, you may have to tweak the script to use `gsed` instead.
1. Create a Linux VM in Compute Engine (GCE).
```sh
gcloud compute instances create gce-linux \
--zone=us-central1-a \
--machine-type=e2-micro \
--image-family=debian-12 \
--image-project=debian-cloud
```
1. SSH into the VM. From here until we exit, all steps happen in the VM.
```sh
gcloud compute ssh gce-linux \
--zone "us-central1-a"
```
1. Install the Postgres client and set your default Google Cloud project.
```sh
sudo apt-get install -y postgresql-client
gcloud auth login
gcloud config set project <PROJECT_ID>
```
1. Copy script #2, the python script, and the products.json to the VM. Make sure the scripts are executable.
```sh
nano 2_create_populate_alloydb_tables.sh # paste content
nano generate_sql_from_products.py # paste content
nano products.json # paste content
chmod +x 2_create_populate_alloydb_tables.sh
chmod +x generate_sql_from_products.py
```
**Note:** You can find the files at the following places:
- `kustomize/components/shopping-assistant/scripts/2_create_populate_alloydb_tables.sh`
- `kustomize/components/shopping-assistant/scripts/generate_sql_from_products.py`
- `src/productcatalogservice/products.json`
1. Run script #2 in the VM. If it asks for a postgres password, it should be the same that you set in script #1 earlier. This may take a few minutes.
```sh
./2_create_populate_alloydb_tables.sh
```
1. Exit SSH.
```sh
exit
```
1. Create an API key in the [Credentials page](https://pantheon.corp.google.com/apis/credentials) with permissions for "Generative Language API", and make note of the secret key.
1. Replace the Google API key placeholder in the shoppingassistant service.
```sh
export GOOGLE_API_KEY=<google_api_key>
sed -i "s/GOOGLE_API_KEY_VAL/${GOOGLE_API_KEY}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
```
1. Edit the root Kustomize file to enable the `alloydb` and `shopping-assistant` components.
```sh
nano kubernetes-manifests/kustomization.yaml # make the modifications below
```
```yaml
# ...head of the file
components: # remove this comment
# - ../kustomize/components/cymbal-branding
# - ../kustomize/components/google-cloud-operations
# - ../kustomize/components/memorystore
# - ../kustomize/components/network-policies
- ../kustomize/components/alloydb # remove this comment
- ../kustomize/components/shopping-assistant # remove this comment
# - ../kustomize/components/spanner
# - ../kustomize/components/container-images-tag
# - ../kustomize/components/container-images-tag-suffix
# - ../kustomize/components/container-images-registry
```
1. Deploy to the GKE cluster.
```sh
skaffold run --default-repo=us-central1-docker.pkg.dev/$PROJECT_ID/images
```
1. Wait for all the pods to be up and running. You can then find the external IP and navigate to it.
```sh
kubectl get pods
kubectl get services
```

View File

@@ -0,0 +1,32 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
- shoppingassistantservice.yaml
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
template:
spec:
containers:
- name: server
env:
- name: ENABLE_ASSISTANT
value: "true"

View File

@@ -0,0 +1,135 @@
#!/bin/sh
#
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
set -x
# Replace me
PROJECT_ID=$PROJECT_ID
PROJECT_NUMBER=$PROJECT_NUMBER
PGPASSWORD=$PGPASSWORD
# Set sensible defaults
REGION=us-central1
USE_GKE_GCLOUD_AUTH_PLUGIN=True
ALLOYDB_NETWORK=default
ALLOYDB_SERVICE_NAME=onlineboutique-network-range
ALLOYDB_CLUSTER_NAME=onlineboutique-cluster
ALLOYDB_INSTANCE_NAME=onlineboutique-instance
ALLOYDB_CARTS_DATABASE_NAME=carts
ALLOYDB_CARTS_TABLE_NAME=cart_items
ALLOYDB_PRODUCTS_DATABASE_NAME=products
ALLOYDB_PRODUCTS_TABLE_NAME=catalog_items
ALLOYDB_USER_GSA_NAME=alloydb-user-sa
ALLOYDB_USER_GSA_ID=${ALLOYDB_USER_GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
ALLOYDB_SECRET_NAME=alloydb-secret
# Enable services
gcloud services enable alloydb.googleapis.com
gcloud services enable servicenetworking.googleapis.com
gcloud services enable secretmanager.googleapis.com
gcloud services enable aiplatform.googleapis.com
gcloud services enable generativelanguage.googleapis.com
# Set our DB credentials behind the secret
echo $PGPASSWORD | gcloud secrets create ${ALLOYDB_SECRET_NAME} --data-file=-
# Set up needed service connection
gcloud compute addresses create ${ALLOYDB_SERVICE_NAME} \
--global \
--purpose=VPC_PEERING \
--prefix-length=16 \
--description="Online Boutique Private Services" \
--network=${ALLOYDB_NETWORK}
gcloud services vpc-peerings connect \
--service=servicenetworking.googleapis.com \
--ranges=${ALLOYDB_SERVICE_NAME} \
--network=${ALLOYDB_NETWORK}
gcloud alloydb clusters create ${ALLOYDB_CLUSTER_NAME} \
--region=${REGION} \
--password=${PGPASSWORD} \
--disable-automated-backup \
--network=${ALLOYDB_NETWORK}
gcloud alloydb instances create ${ALLOYDB_INSTANCE_NAME} \
--cluster=${ALLOYDB_CLUSTER_NAME} \
--region=${REGION} \
--cpu-count=4 \
--instance-type=PRIMARY
gcloud alloydb instances create ${ALLOYDB_INSTANCE_NAME}-replica \
--cluster=${ALLOYDB_CLUSTER_NAME} \
--region=${REGION} \
--cpu-count=4 \
--instance-type=READ_POOL \
--read-pool-node-count=2
gcloud beta alloydb instances update ${ALLOYDB_INSTANCE_NAME} \
--cluster=${ALLOYDB_CLUSTER_NAME} \
--region=${REGION} \
--assign-inbound-public-ip=ASSIGN_IPV4 \
--database-flags password.enforce_complexity=on
# Fetch the primary and read IPs
ALLOYDB_PRIMARY_IP=`gcloud alloydb instances list --region=${REGION} --cluster=${ALLOYDB_CLUSTER_NAME} --filter="INSTANCE_TYPE:PRIMARY" --format=flattened | sed -nE "s/ipAddress:\s*(.*)/\1/p"`
ALLOYDB_READ_IP=`gcloud alloydb instances list --region=${REGION} --cluster=${ALLOYDB_CLUSTER_NAME} --filter="INSTANCE_TYPE:READ_POOL" --format=flattened | sed -nE "s/ipAddress:\s*(.*)/\1/p"`
# Substitute environment values (alloydb/kustomization.yaml)
sed -i "s/PROJECT_ID_VAL/${PROJECT_ID}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/REGION_VAL/${REGION}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_PRIMARY_IP_VAL/${ALLOYDB_PRIMARY_IP}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_USER_GSA_ID/${ALLOYDB_USER_GSA_ID}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_CLUSTER_NAME_VAL/${ALLOYDB_CLUSTER_NAME}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_INSTANCE_NAME_VAL/${ALLOYDB_INSTANCE_NAME}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_CARTS_DATABASE_NAME_VAL/${ALLOYDB_CARTS_DATABASE_NAME}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_CARTS_TABLE_NAME_VAL/${ALLOYDB_CARTS_TABLE_NAME}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_PRODUCTS_DATABASE_NAME_VAL/${ALLOYDB_PRODUCTS_DATABASE_NAME}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_PRODUCTS_TABLE_NAME_VAL/${ALLOYDB_PRODUCTS_TABLE_NAME}/g" kustomize/components/alloydb/kustomization.yaml
sed -i "s/ALLOYDB_SECRET_NAME_VAL/${ALLOYDB_SECRET_NAME}/g" kustomize/components/alloydb/kustomization.yaml
# Substitute environment values (kustomize/components/shopping-assistant/shoppingassistantservice.yaml)
sed -i "s/PROJECT_ID_VAL/${PROJECT_ID}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
sed -i "s/REGION_VAL/${REGION}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
sed -i "s/ALLOYDB_CLUSTER_NAME_VAL/${ALLOYDB_CLUSTER_NAME}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
sed -i "s/ALLOYDB_INSTANCE_NAME_VAL/${ALLOYDB_INSTANCE_NAME}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
sed -i "s/ALLOYDB_DATABASE_NAME_VAL/${ALLOYDB_PRODUCTS_DATABASE_NAME}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
sed -i "s/ALLOYDB_TABLE_NAME_VAL/${ALLOYDB_PRODUCTS_TABLE_NAME}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
sed -i "s/ALLOYDB_SECRET_NAME_VAL/${ALLOYDB_SECRET_NAME}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
sed -i "s/ALLOYDB_USER_GSA_ID/${ALLOYDB_USER_GSA_ID}/g" kustomize/components/shopping-assistant/shoppingassistantservice.yaml
# Create service account for the cart and shopping assistant services
gcloud iam service-accounts create ${ALLOYDB_USER_GSA_NAME} \
--display-name=${ALLOYDB_USER_GSA_NAME}
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member=serviceAccount:${ALLOYDB_USER_GSA_ID} --role=roles/alloydb.client
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member=serviceAccount:${ALLOYDB_USER_GSA_ID} --role=roles/alloydb.databaseUser
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member=serviceAccount:${ALLOYDB_USER_GSA_ID} --role=roles/secretmanager.secretAccessor
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member=serviceAccount:${ALLOYDB_USER_GSA_ID} --role=roles/serviceusage.serviceUsageConsumer
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member=serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-alloydb.iam.gserviceaccount.com --role=roles/aiplatform.user
gcloud iam service-accounts add-iam-policy-binding ${ALLOYDB_USER_GSA_ID} \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/cartservice]" \
--role roles/iam.workloadIdentityUser
gcloud iam service-accounts add-iam-policy-binding ${ALLOYDB_USER_GSA_ID} \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/shoppingassistantservice]" \
--role roles/iam.workloadIdentityUser
gcloud iam service-accounts add-iam-policy-binding ${ALLOYDB_USER_GSA_ID} \
--member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/productcatalogservice]" \
--role roles/iam.workloadIdentityUser

View File

@@ -0,0 +1,49 @@
#!/bin/sh
#
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
set -x
# Set sensible defaults
REGION=us-central1
ALLOYDB_CLUSTER_NAME=onlineboutique-cluster
ALLOYDB_CARTS_DATABASE_NAME=carts
ALLOYDB_CARTS_TABLE_NAME=cart_items
ALLOYDB_PRODUCTS_DATABASE_NAME=products
ALLOYDB_PRODUCTS_TABLE_NAME=catalog_items
# Fetch the primary and read IPs
ALLOYDB_PRIMARY_IP=`gcloud alloydb instances list --region=${REGION} --cluster=${ALLOYDB_CLUSTER_NAME} --filter="INSTANCE_TYPE:PRIMARY" --format=flattened | sed -nE "s/ipAddress:\s*(.*)/\1/p"`
# Create carts database and table
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -c "CREATE DATABASE ${ALLOYDB_CARTS_DATABASE_NAME}"
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_CARTS_DATABASE_NAME} -c "CREATE TABLE ${ALLOYDB_CARTS_TABLE_NAME} (userId text, productId text, quantity int, PRIMARY KEY(userId, productId))"
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_CARTS_DATABASE_NAME} -c "CREATE INDEX cartItemsByUserId ON ${ALLOYDB_CARTS_TABLE_NAME}(userId)"
# Create products database, table, and extensions
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -c "CREATE DATABASE ${ALLOYDB_PRODUCTS_DATABASE_NAME}"
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_PRODUCTS_DATABASE_NAME} -c "CREATE EXTENSION IF NOT EXISTS vector"
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_PRODUCTS_DATABASE_NAME} -c "CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;"
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_PRODUCTS_DATABASE_NAME} -c "GRANT EXECUTE ON FUNCTION embedding TO postgres;"
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_PRODUCTS_DATABASE_NAME} -c "CREATE TABLE ${ALLOYDB_PRODUCTS_TABLE_NAME} (id TEXT PRIMARY KEY, name TEXT, description TEXT, picture TEXT, price_usd_currency_code TEXT, price_usd_units INTEGER, price_usd_nanos BIGINT, categories TEXT, product_embedding VECTOR(768), embed_model TEXT)"
# Generate and insert products table entries
python3 ./generate_sql_from_products.py > products.sql
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_PRODUCTS_DATABASE_NAME} -f products.sql
rm products.sql
# Generate vector embeddings
psql -h ${ALLOYDB_PRIMARY_IP} -U postgres -d ${ALLOYDB_PRODUCTS_DATABASE_NAME} -c "UPDATE ${ALLOYDB_PRODUCTS_TABLE_NAME} SET product_embedding = embedding('textembedding-gecko@003', description), embed_model='textembedding-gecko@003';"

View File

@@ -0,0 +1,50 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
table_name = "catalog_items"
fields = [
'id', 'name', 'description', 'picture',
'price_usd_currency_code', 'price_usd_units', 'price_usd_nanos',
'categories'
]
# Load the produts JSON
with open("products.json", 'r') as f:
data = json.load(f)
# Generate SQL INSERT statements
for product in data['products']:
columns = ', '.join(fields)
placeholders = ', '.join(['{}'] * len(fields))
sql = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders});"
# Escape single quotes within product data
product['name'] = product['name'].replace("'", "")
product['description'] = product['description'].replace("'", "")
escaped_values = (
f"'{product['id']}'",
f"'{product['name']}'",
f"'{product['description']}'",
f"'{product['picture']}'",
f"'{product['priceUsd']['currencyCode']}'",
product['priceUsd']['units'],
product['priceUsd']['nanos'],
f"'{','.join(product['categories'])}'"
)
# Render the formatted SQL query
print(sql.format(*escaped_values))

View File

@@ -0,0 +1,95 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: apps/v1
kind: Deployment
metadata:
name: shoppingassistantservice
labels:
app: shoppingassistantservice
spec:
selector:
matchLabels:
app: shoppingassistantservice
template:
metadata:
labels:
app: shoppingassistantservice
spec:
serviceAccountName: shoppingassistantservice
terminationGracePeriodSeconds: 5
securityContext:
fsGroup: 1000
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
containers:
- name: server
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
image: shoppingassistantservice
ports:
- name: http
containerPort: 8080
env:
- name: GOOGLE_API_KEY
value: GOOGLE_API_KEY_VAL
- name: ALLOYDB_CLUSTER_NAME
value: ALLOYDB_CLUSTER_NAME_VAL
- name: ALLOYDB_INSTANCE_NAME
value: ALLOYDB_INSTANCE_NAME_VAL
- name: ALLOYDB_DATABASE_NAME
value: ALLOYDB_DATABASE_NAME_VAL
- name: ALLOYDB_TABLE_NAME
value: ALLOYDB_TABLE_NAME_VAL
- name: ALLOYDB_SECRET_NAME
value: ALLOYDB_SECRET_NAME_VAL
- name: PROJECT_ID
value: PROJECT_ID_VAL
- name: REGION
value: REGION_VAL
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: shoppingassistantservice
labels:
app: shoppingassistantservice
spec:
type: ClusterIP
selector:
app: shoppingassistantservice
ports:
- name: http
port: 80
targetPort: 8080
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: shoppingassistantservice
annotations:
iam.gke.io/gcp-service-account: ALLOYDB_USER_GSA_ID