Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
---
title: Build a mixed-placement AI shopping assistant on GKE with Axion

draft: true
cascade:
draft: true

minutes_to_complete: 120
description: Deploy an AI shopping assistant for an Online Boutique storefront on GKE and compare N4A and C4A placement for the assistant tier.
description: Deploy and validate an Online Boutique storefront on GKE, add an AI shopping assistant, and compare N4A and C4A placement for the assistant tier.
who_is_this_for: This is an advanced topic for cloud developers, platform engineers, and site reliability engineers who run applications on Google Kubernetes Engine and want to place application tiers on the Axion-based machine series that fits each workload.

learning_objectives:
- Explain why a storefront application and an AI assistant can have different workload profiles
- Build and push a `linux/arm64` container image for the shopping assistant service
- Create and validate an Online Boutique storefront on an N4A node pool
- Build and push a `linux/arm64` container image, then add the AI shopping assistant to the storefront
- Use Kustomize overlays to run the assistant on N4A first, then move it to C4A
- Capture and compare benchmark summaries for the same assistant workload on N4A and C4A

prerequisites:
- A [Google Cloud account](https://console.cloud.google.com/) with billing enabled
- Access to a [GKE Standard cluster with Arm node pools](https://cloud.google.com/kubernetes-engine/docs/how-to/create-arm-clusters-nodes), including an N4A node pool and a C4A node pool
- Permission to create or use an [Artifact Registry Docker repository](https://cloud.google.com/artifact-registry/docs/docker/store-docker-container-images) in the same project
- Access to Google Cloud Shell, or a local Linux or macOS terminal with `gcloud`, `kubectl`, `docker`, `git`, `jq`, and `python3`
- Access to a [GKE Standard cluster with Arm node pools](https://cloud.google.com/kubernetes-engine/docs/how-to/create-arm-clusters-nodes), including N4A and C4A node pools
- Basic familiarity with Docker, Kubernetes, Kustomize, and Google Kubernetes Engine

author:
Expand All @@ -31,7 +33,8 @@ subjects: Containers and Virtualization
cloud_service_providers:
- Google Cloud
armips:
- Neoverse
- Neoverse-N3
- Neoverse-V2
operatingsystems:
- Linux
tools_software_languages:
Expand All @@ -41,6 +44,7 @@ tools_software_languages:
- Kustomize
- Python
- Ollama
- Gemma

further_reading:
- resource:
Expand All @@ -54,14 +58,14 @@ further_reading:
- resource:
title: Google Axion processors
link: https://cloud.google.com/products/axion
type: documentation
type: website
- resource:
title: Kustomize documentation
link: https://kubectl.docs.kubernetes.io/references/kustomize/
type: documentation
- resource:
title: Ollama documentation
link: https://ollama.com/
link: https://docs.ollama.com/
type: documentation
- resource:
title: Migrate x86 workloads to Arm on Google Kubernetes Engine with Axion processors
Expand All @@ -74,7 +78,3 @@ weight: 1 # _index.md always has weight of 1 to order corr
layout: "learningpathall" # All files under learning paths have this same wrapper
learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content.
---

Deploy an Online Boutique storefront on Google Kubernetes Engine (GKE), add an AI shopping assistant, and compare the same assistant workload on N4A and C4A node pools powered by Google Axion processors.

You start with the storefront on N4A, build the missing `shoppingassistantservice` image for `linux/arm64`, validate the assistant on N4A, and then move only the assistant tier to C4A. You finish by running the same fixed benchmark in both placements so you can compare request success, latency, and Kubernetes CPU telemetry.
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ layout: "learningpathall"

## How the application is organized

Modern AI applications often contain more than one workload shape. A storefront tier is steady, service-oriented, and always on. An AI assistant tier is burstier and more latency-sensitive because it performs reasoning work only when users ask for help.
Modern AI applications often contain more than one workload shape. For example, a storefront is steady, service-oriented, and always on. An AI assistant is burstier and more latency-sensitive because it runs only when users ask for help.

This Learning Path uses that split in a live Online Boutique storefront on Google Kubernetes Engine (GKE). The storefront starts on N4A nodes powered by Google Axion processors. You add the missing `shoppingassistantservice`, run it on N4A first, and then move only that assistant tier to C4A.
This Learning Path uses that split in a live Online Boutique storefront on Google Kubernetes Engine (GKE). The storefront starts on N4A nodes powered by Google Axion processors and Arm Neoverse N3. You add the missing `shoppingassistantservice`, run it on N4A first, and then move only that assistant tier to C4A nodes powered by Google Axion and Arm Neoverse V2.

The goal isn't to prove that one Axion-based machine series replaces the other. You use N4A for the steady storefront tier and evaluate C4A for the AI reasoning tier so you can decide which machine series fits each workload.

![Architecture diagram showing Cloud Shell and Kustomize deploying Online Boutique to GKE on Axion. Storefront microservices run on N4A, and the shopping assistant runs on C4A with access to cart, catalog, and a local Ollama Gemma model.#center](images/mixed-placement-agentic-storefront-on-axion.png "Mixed-placement agentic storefront on Axion")
![Architecture diagram showing the existing multi-architecture storefront image workflow and the final GKE placement on Axion. Core storefront services run on N4A, while the shopping assistant, Ollama sidecar, and Gemma model run together on C4A and call the live cart and catalog services.#center](images/mixed-placement-agentic-storefront-on-axion.png "Mixed-placement agentic storefront on Axion")

The diagram shows the final mixed-placement pattern you build toward. The storefront images already run on Arm nodes, and the assistant image you build later is `linux/arm64` because both target node pools are Arm-based.
The diagram shows the final mixed-placement pattern you build toward. Its left side summarizes the multi-architecture image publishing process used for the existing storefront images; you don't repeat that process here. The assistant image you build later targets only `linux/arm64` because both destination node pools are Arm-based. The right side shows the final placement: core services remain on N4A while the assistant, its Ollama sidecar, and the Gemma model move together to C4A.

## How the storefront already runs on Arm

Expand All @@ -25,54 +25,23 @@ When Kubernetes schedules a pod that uses a multi-architecture image on an Arm n

This Learning Path starts with Arm-compatible storefront images already available. To learn the full build-and-publish workflow for multi-architecture images on GKE, see [Migrate x86 workloads to Arm on Google Kubernetes Engine with Axion processors](/learning-paths/servers-and-cloud-computing/gke-multi-arch-axion/).

Inspect a public multi-architecture image manifest:
One image reference can therefore contain an Arm-compatible variant. You confirm the storefront image reference from the source tree after you set up your tools and cluster access.

```bash
docker buildx imagetools inspect docker.io/library/python:3.12-slim
```
## How assistant requests flow

The output is similar to:

```output
Name: docker.io/library/python:3.12-slim
MediaType: application/vnd.oci.image.index.v1+json

Manifests:
Platform: linux/amd64
Platform: linux/arm64/v8
```

The exact digest values can differ. The important signal is that one image reference can include an Arm-compatible variant. You confirm the storefront image reference from the source tree in the setup step.

## How the assistant works

The `shoppingassistantservice` is the AI layer for the application. It runs as its own Kubernetes service and receives assistant requests from the storefront frontend.

The assistant uses live storefront services as tools:

- `search_catalog` queries `productcatalogservice`.
- `get_product_details` fetches a specific product from the live catalog.
- `get_cart` reads the current cart from `cartservice`.
- `add_to_cart` updates the cart only after user confirmation.

The assistant sends grounded context to a local Ollama sidecar in the same pod. The sidecar runs the `gemma3:1b-it-qat` model for the reasoning step.

This design keeps the comparison focused. You don't add a separate vector database, retrieval pipeline, or hosted large language model API. You move the assistant logic and local reasoning runtime together as one AI tier.

## How requests flow through the storefront

When a shopper uses the assistant, the request follows this path:
The `shoppingassistantservice` is the AI layer for the application. It runs as its own Kubernetes service and handles requests sent through the storefront frontend. When a shopper uses the assistant, the request follows this path:

1. The browser sends the request to `frontend`.
2. `frontend` forwards the request to `shoppingassistantservice`.
3. The assistant calls live services such as `productcatalogservice` and `cartservice`.
4. The assistant sends grounded context to the local Ollama sidecar.
3. The assistant calls `productcatalogservice` or `cartservice` through its application tools.
4. The assistant sends the live storefront context to an Ollama sidecar in the same pod. The sidecar runs the `gemma3:1b-it-qat` model for the reasoning step.
5. The assistant returns the response through `frontend`.

The assistant is agentic because it does more than generate text. It uses application tools, keeps short-lived session state, and asks for confirmation before it calls `cartservice` to update the cart.

## What you've learned and what's next
The `search_catalog` and `get_product_details` tools query the live catalog. The `get_cart` tool reads the current cart, while `add_to_cart` updates it only after user confirmation.

This design keeps the placement comparison focused. You don't add a separate vector database, retrieval pipeline, or hosted large language model API. When you move the assistant pod, you move the assistant logic and local reasoning runtime together as one AI tier.

You've learned why the storefront tier and AI assistant tier use different Axion placements. You also saw how Arm-compatible image variants make the existing storefront portable to Axion nodes.

Next, you'll set up environment variables, connect to the GKE cluster, and clone the source tree that contains the assistant implementation.
In the next section, you'll set up environment variables, connect to the GKE cluster, and clone the source tree that contains the assistant implementation.
Original file line number Diff line number Diff line change
Expand Up @@ -16,62 +16,18 @@ This is an Arm-targeted build for Axion, not a full multi-architecture build. A
If you opened a new terminal, return to the source tree and restore the required variables:

```bash
cd "${HOME}/n4a-c4a/microservices-demo"

export PROJECT_ID="$(gcloud config get-value project)"
export ARTIFACT_REGION="us-central1"
export ARTIFACT_REPO="axion-workshop"
source "${HOME}/.storefront-axion-env"
cd "${REPO}"
```

## Define the image name
## Confirm the image name

Create one reusable image path:
Confirm the reusable image path you configured during setup:

```bash
export ASSISTANT_IMAGE_REPO="${ARTIFACT_REGION}-docker.pkg.dev/${PROJECT_ID}/${ARTIFACT_REPO}/shoppingassistantservice"
export ASSISTANT_IMAGE_TAG="lab-v1"
export ASSISTANT_IMAGE="${ASSISTANT_IMAGE_REPO}:${ASSISTANT_IMAGE_TAG}"

echo "${ASSISTANT_IMAGE}"
```

## Verify Docker and Buildx

Confirm that Docker and Buildx are available:

```bash
docker version
docker buildx version
```

If Docker is not running in your environment, start it before you continue.

## Configure Artifact Registry authentication

Configure Docker authentication for Artifact Registry:

```bash
gcloud auth configure-docker "${ARTIFACT_REGION}-docker.pkg.dev" --quiet
```

Cloud Shell might print a warning about existing credential helpers. You can continue if the command finishes successfully.

## Create a Buildx builder

Create or reuse a Buildx builder:

```bash
if docker buildx inspect axion-builder >/dev/null 2>&1; then
docker buildx use axion-builder
else
docker buildx create --name axion-builder --use
fi

docker buildx inspect --bootstrap
```

The output should show a working builder. Confirm that the `Platforms` line includes `linux/arm64` before you continue.

## Build and push the assistant image

Build the assistant image for `linux/arm64` and push it to Artifact Registry:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,23 @@ weight: 7
layout: "learningpathall"
---

## Create the N4A overlay
## Add the assistant to the N4A overlay

If you opened a new terminal, return to the source tree and restore the required variables:

```bash
cd "${HOME}/n4a-c4a/microservices-demo"

export PROJECT_ID="$(gcloud config get-value project)"
export ARTIFACT_REGION="us-central1"
export ARTIFACT_REPO="axion-workshop"
export ASSISTANT_IMAGE_REPO="${ARTIFACT_REGION}-docker.pkg.dev/${PROJECT_ID}/${ARTIFACT_REPO}/shoppingassistantservice"
export ASSISTANT_IMAGE_TAG="lab-v1"
export N4A_NODE_POOL_NAME="arm64-pool-n4a2"
source "${HOME}/.storefront-axion-env"
cd "${REPO}"

export FRONTEND_IP="$(kubectl get service frontend-external \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
export APP_URL="http://${FRONTEND_IP}"
```

Create a Kustomize overlay that adds the assistant component and keeps the full storefront, including the assistant, on the N4A node pool:
The `storefront-n4a` overlay already places every deployment on N4A. Extend it with the assistant component and the image you pushed. The existing `node-selector.yaml` patch then applies the same placement to the assistant deployment.

```bash
mkdir -p kustomize/overlays/assistant-n4a

cat <<EOF > kustomize/overlays/assistant-n4a/kustomization.yaml
cat <<EOF > kustomize/overlays/storefront-n4a/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
Expand All @@ -44,30 +37,16 @@ patches:
target:
kind: Deployment
EOF

cat <<EOF > kustomize/overlays/assistant-n4a/node-selector.yaml
- op: add
path: /spec/template/spec/nodeSelector
value:
cloud.google.com/gke-nodepool: ${N4A_NODE_POOL_NAME}
- op: add
path: /spec/template/spec/tolerations
value:
- key: kubernetes.io/arch
operator: Equal
value: arm64
effect: NoSchedule
EOF
```

## Render the overlay

Review the overlay files and render the manifest:

```bash
sed -n '1,120p' kustomize/overlays/assistant-n4a/kustomization.yaml
sed -n '1,120p' kustomize/overlays/assistant-n4a/node-selector.yaml
kubectl kustomize kustomize/overlays/assistant-n4a | sed -n '1,240p'
sed -n '1,120p' kustomize/overlays/storefront-n4a/kustomization.yaml
sed -n '1,120p' kustomize/overlays/storefront-n4a/node-selector.yaml
kubectl kustomize kustomize/overlays/storefront-n4a | sed -n '1,240p'
```

Do not apply the overlay if the rendered manifest has an empty image name, image tag, or node-pool value.
Expand All @@ -77,7 +56,7 @@ Do not apply the overlay if the rendered manifest has an empty image name, image
Apply the overlay and wait for the frontend and assistant deployments:

```bash
kubectl apply -k kustomize/overlays/assistant-n4a
kubectl apply -k kustomize/overlays/storefront-n4a
kubectl rollout status deployment/frontend --timeout=600s
kubectl rollout status deployment/shoppingassistantservice --timeout=1200s
```
Expand Down Expand Up @@ -120,9 +99,11 @@ The `server` container serves the assistant API. The `ollama` container prepares
The first rollout can take a few minutes because the Ollama sidecar pulls the model when the pod starts. Early `/readyz` checks can return `503` until the model is available.
{{% /notice %}}

## Validate the assistant through the storefront
## Send a request in one assistant session

Open the assistant endpoint and save its HTTP session cookie in `/tmp/assistant.cookies`. The next request sends the same cookie back, just as a browser would. The assistant can then associate follow-up prompts and cart actions with the same short-lived session.

Create a session with cookies and send one assistant request through the storefront:
Send one assistant request through the storefront:

```bash
curl --max-time 30 -s -c /tmp/assistant.cookies "${APP_URL}/assistant" >/dev/null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,9 @@ layout: "learningpathall"
If you opened a new terminal, return to the source tree and restore the required variables:

```bash
cd "${HOME}/n4a-c4a/microservices-demo"

export PROJECT_ID="$(gcloud config get-value project)"
export ARTIFACT_REGION="us-central1"
export ARTIFACT_REPO="axion-workshop"
export ASSISTANT_IMAGE_REPO="${ARTIFACT_REGION}-docker.pkg.dev/${PROJECT_ID}/${ARTIFACT_REPO}/shoppingassistantservice"
export ASSISTANT_IMAGE_TAG="lab-v1"
export N4A_NODE_POOL_NAME="arm64-pool-n4a2"
export C4A_NODE_POOL_NAME="arm64-pool-c4a"
source "${HOME}/.storefront-axion-env"
cd "${REPO}"

export FRONTEND_IP="$(kubectl get service frontend-external \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
export APP_URL="http://${FRONTEND_IP}"
Expand Down Expand Up @@ -117,7 +111,7 @@ The frontend should remain on N4A, and `shoppingassistantservice` should now run

## Warm the assistant on C4A

Send one request through the storefront after the move:
Open the assistant endpoint to refresh the HTTP session cookie, then send one request through the storefront after the move. The cookie file preserves the same browser-like session across both calls:

```bash
curl --max-time 30 -s -c /tmp/assistant.cookies "${APP_URL}/assistant" >/dev/null
Expand Down Expand Up @@ -171,9 +165,14 @@ The summary should show successful requests and the assistant running on the C4A
Compare the N4A and C4A summaries:

```bash
python3 workshop/compare_summaries.py workshop/fixed-n4a-summary.log workshop/fixed-c4a-summary.log
python3 workshop/compare_summaries.py \
workshop/fixed-n4a-summary.log \
workshop/fixed-c4a-summary.log | \
sed '/^Interpretation$/,$d'
```

This displays the measured comparison table without adding a predetermined placement conclusion. Lower batch duration and request latency indicate the faster run for those measurements.

Focus on these signals:

- Successful request count
Expand All @@ -200,6 +199,8 @@ The application now has three states:

That sequence is the core architectural point. A modern application can contain a steady application tier and a bursty AI reasoning tier. N4A supports the steady storefront and core services, while C4A gives you a placement to evaluate for concentrated, latency-sensitive assistant work.

Both Axion machine series run `arm64`, so the same assistant image moves between the Neoverse N3-based N4A pool and the Neoverse V2-based C4A pool. You can evaluate placement without maintaining a second image or build pipeline.

For this application, the useful pattern is not replacing one machine series with another. It is placing each workload tier where its behavior fits best.

If your N4A and C4A results are close, or if N4A looks better for a particular run, that is still useful information. The decision should come from the workload behavior you observe, not from assuming one Axion-based machine series is always the right answer.
Expand Down
Loading
Loading