DEV Community

Cover image for Integrating Nano Banana Try‑On into a Polyglot Microservices Demo (gRPC, Skaffold, K8s)
Lahfir
Lahfir

Posted on

Integrating Nano Banana Try‑On into a Polyglot Microservices Demo (gRPC, Skaffold, K8s)

Adding a new AI feature to a polyglot microservices repo doesn’t have to be painful. In this post, I’ll show how I integrated the Nano Banana Try‑On service (tryonservice/) into the Google Online Boutique–style demo: containerizing a Python gRPC service, exposing it on Kubernetes, wiring the Go frontend, and enabling local/dev workflows with Skaffold and Docker Compose.

What we’ll cover:

  • Service design and gRPC surface
  • Containerization (multi‑stage Dockerfile)
  • Kubernetes Deployment/Service and env wiring
  • Frontend integration (Go) via env vars + gRPC client
  • Local dev with Skaffold and Docker Compose
  • Observability hooks with OpenTelemetry

Architecture at a glance

The new tryonservice is a Python gRPC service that generates a virtual try‑on image for a given user and product. The Go frontend exposes HTTP routes and calls tryonservice over gRPC. Everything is deployed with Kubernetes; images are built via Skaffold.

The Try‑On service (Python + gRPC)

Key entrypoint: src/tryonservice/tryon_server.py

  • gRPC server implements TryOnService with two RPCs: TryOnProduct and GetTryOnHistory.
  • Uses Google Gemini via google-genai when GEMINI_API_KEY is present; otherwise returns a mock image so dev flows are smooth.
  • Emits structured logs and optional OpenTelemetry traces.

Code highlights:

58:class TryOnService(demo_pb2_grpc.TryOnServiceServicer):
71:    def TryOnProduct(self, request, context):
299:def serve():
301:    port = os.environ.get('PORT', '5012')
324:    server.add_insecure_port(f'[::]:{port}')
327:    logger.info(f"TryOn service listening on port {port}")
Enter fullscreen mode Exit fullscreen mode

Observability is opt‑in via OTLP:

41:# Initialize OpenTelemetry
42:if os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT'):
43:    resource = Resource(attributes={"service.name": "tryonservice"})
45:    processor = BatchSpanProcessor(
46:        OTLPSpanExporter(endpoint=os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT'))
Enter fullscreen mode Exit fullscreen mode

Dependencies

Pinned in requirements.txt:

1:grpcio==1.68.1
4:google-genai==0.3.0
5:Pillow==11.0.0
8:opentelemetry-api==1.29.0
11:opentelemetry-exporter-otlp==1.29.0
Enter fullscreen mode Exit fullscreen mode

Containerization (multi‑stage Dockerfile)

The service uses a two‑stage build to keep the runtime image slim, exposes gRPC on 5012, and runs the Python server directly.

1:FROM python:3.12-slim AS base
9:COPY requirements.txt .
10:RUN pip install --prefix="/install" -r requirements.txt
14:WORKDIR /tryonservice
24:EXPOSE 5012
29:ENTRYPOINT ["python", "tryon_server.py"]
Enter fullscreen mode Exit fullscreen mode

Kubernetes: Deployment and Service

We declare the Deployment and ClusterIP Service, and wire in env vars for port, Gemini key (as a Secret), and OTEL exporter. Probes are simple TCP checks on the gRPC port.

1:apiVersion: apps/v1
4:  name: tryonservice
19:      containers:
21:        image: gcr.io/quiet-cider-472416-a5/tryonservice:v2
27:        - name: PORT
28:          value: "5012"
29:        - name: GEMINI_API_KEY
31:            secretKeyRef:
32:              name: gemini-api-key
35:        - name: OTEL_EXPORTER_OTLP_ENDPOINT
36:          value: "otel-collector:4317"
56:apiVersion: v1
59:  name: tryonservice
65:  - name: grpc
66:    port: 5012
Enter fullscreen mode Exit fullscreen mode

Skaffold: build and deploy

Skaffold tracks and builds the service image and deploys manifests via kustomize.

50:  - image: tryonservice
51:    context: src/tryonservice
60:  kustomize:
62:    - kubernetes-manifests
Enter fullscreen mode Exit fullscreen mode

This means skaffold dev or skaffold run will build src/tryonservice and roll it out alongside other services.

Frontend wiring (Go)

The Go frontend consumes TRYON_SERVICE_ADDR and opens a gRPC connection; routes /tryon and /tryon/history proxy user actions to the service.

84: tryOnSvcAddr string
145:    mustMapEnv(&svc.tryOnSvcAddr, "TRYON_SERVICE_ADDR")
155:    mustConnGRPC(ctx, &svc.tryOnSvcConn, svc.tryOnSvcAddr)
168:    r.HandleFunc(baseUrl + "/tryon", svc.tryOnHandler).Methods(http.MethodPost)
169:    r.HandleFunc(baseUrl + "/tryon/history", svc.tryOnHistoryHandler).Methods(http.MethodGet)
Enter fullscreen mode Exit fullscreen mode

In Kubernetes, the frontend gets that env var from its Deployment manifest:

86:          - name: TRYON_SERVICE_ADDR
87:            value: "tryonservice:5012"
Enter fullscreen mode Exit fullscreen mode

For local docker‑compose and the helper script, the variable is present too:

72:      - TRYON_SERVICE_ADDR=tryonservice:8080
Enter fullscreen mode Exit fullscreen mode
93:TRYON_SERVICE_ADDR=tryonservice:8080
136:      - TRYON_SERVICE_ADDR=tryonservice:8080
Enter fullscreen mode Exit fullscreen mode

Note: In Kubernetes we expose 5012 (native gRPC port). In some local setups we route through the frontend at 8080; adjust as needed for your environment.

Running locally with Skaffold

  • Prereqs: Docker, kubectl, a local or remote Kubernetes cluster, and Skaffold.
  • Optional: Create the Gemini API key Secret so the service can use real generations:
kubectl create secret generic gemini-api-key \
  --from-literal=key="$GEMINI_API_KEY"
Enter fullscreen mode Exit fullscreen mode
  • Launch dev loop:
skaffold dev
Enter fullscreen mode Exit fullscreen mode
  • Port‑forward the try‑on service for quick gRPC tests:
kubectl port-forward deployment/tryonservice 5012:5012
Enter fullscreen mode Exit fullscreen mode
  • Example gRPC smoke test with grpcurl:
grpcurl -plaintext localhost:5012 list
Enter fullscreen mode Exit fullscreen mode

Running with docker‑compose

If you prefer Compose for a quick end‑to‑end spin‑up:

docker compose up --build
Enter fullscreen mode Exit fullscreen mode

Ensure the frontend and try‑on env vars are aligned; in this repo they’re defined in docker-compose.yml.

Observability (optional)

Set OTEL_EXPORTER_OTLP_ENDPOINT in tryonservice to emit traces to your collector. The frontend is already OTel‑instrumented via otelgrpc and otelhttp.

Gotchas and tips

  • If GEMINI_API_KEY is not set, the service falls back to a mock image. This is great for dev environments and CI.
  • gRPC readiness/liveness probes use TCP checks on 5012. If you add TLS, switch probes accordingly.
  • Keep image sizes small by relying on the two‑stage Dockerfile and avoiding unnecessary build tools in the runtime image.

Wrap‑up

That’s the full integration path: Python gRPC microservice → container → Kubernetes → Go frontend → local/dev workflows with Skaffold and Compose → optional OTel. You can apply the same pattern to add more AI‑backed capabilities to the demo.

Top comments (0)