In my earlier blog post Remocal, I discussed the concept of Remocal, explained about mirrord, demonstrated its usage in a .NET Aspire based application.
In this blog post, I will be using Online Boutique, a sample cloud-first application with several microservices showcasing Kubernetes, Istio and gRPC. Online Boutique offers functionalities to browse items, add them to the cart, and purchase them.
Architecture
The diagram below illustrates the architecture of Online Boutique application, which comprises 11 microservices written in various programming languages. For more details, you can visit Online Boutique.
Service | Language | Description |
---|---|---|
frontend | Go | Exposes an HTTP server to serve the website. Does not require signup/login and generates session IDs for all users automatically. |
cartservice | C# | Stores the items in the user's shopping cart in Redis and retrieves it. |
productcatalogservice | Go | Provides the list of products from a JSON file and ability to search products and get individual products. |
currencyservice | Node.js | Converts one money amount to another currency. Uses real values fetched from European Central Bank. It's the highest QPS service. |
paymentservice | Node.js | Charges the given credit card info (mock) with the given amount and returns a transaction ID. |
shippingservice | Go | Gives shipping cost estimates based on the shopping cart. Ships items to the given address (mock) |
emailservice | Python | Sends users an order confirmation email (mock). |
checkoutservice | Go | Retrieves user cart, prepares order and orchestrates the payment, shipping and the email notification. |
recommendationservice | Python | Recommends other products based on what's given in the cart. |
adservice | Java | Provides text ads based on given context words. |
loadgenerator | Python / Locust | Continuously sends requests imitating realistic user shopping flows to the frontend. |
Prerequisites
- Skaffold
- You can refer my blog post Skaffold to know more about Skaffold e.g., What is Skaffold? How to install? How to use?
- Docker Desktop
- mirrord Visual Studio Code Extension
- You can refer my blog post Remocal to more about mirrord e.g., What is mirrord? How to install? How to use?
Get the code
# Clone code using this URL
https://github.com/GoogleCloudPlatform/microservices-demo.git
Docker Desktop
Install Docker Desktop and enable Kubernetes option to create a single-node Kubernetes cluster. Alternatively, you may choose from other Kubernetes cluster such as Minikube, k3d, Kind, MicroK8s, Rancher, GKE, AKS, IKS, OKE, AKC and EKS.
Run Skaffold
NOTE: Since, I have been using MacBook Air with the M3 Chip, I have made a few changes in docker files to support AMD and ensure successful builds. Also, it is important to note that before running Skaffold, you should ensure that service-specific dependencies, such as .NET 8 for cartservice are made available and configured properly.
# Updated cartservice dockerfile - linux-x64 to linux-arm64
FROM mcr.microsoft.com/dotnet/sdk:8.0.302-noble@sha256:bd836d1c4a19860ee61d1202b82561f0c750edb7a635443cb001042b71d79569 as builder
WORKDIR /app
COPY cartservice.csproj .
RUN dotnet restore cartservice.csproj \
-r linux-arm64
COPY . .
RUN dotnet publish cartservice.csproj \
-p:PublishSingleFile=true \
-r linux-arm64 \
--self-contained true \
-p:PublishTrimmed=true \
-p:TrimMode=full \
-c release \
-o /cartservice
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0.6-noble-chiseled@sha256:55d6e41f2e7687c597daa4fdca997b07beb3e23b6283729e19bb8ceb272def1a
WORKDIR /app
COPY --from=builder /cartservice .
EXPOSE 7070
ENV DOTNET_EnableDiagnostics=0 \
ASPNETCORE_HTTP_PORTS=7070
USER 1000
ENTRYPOINT ["/app/cartservice"]
# Updated loadgenerator dockerfile - python:3.12.4-slim@sha256:d3a32591680bdfd49da5773495730cf8afdb817e217435db66588b2c64db6d5e to python:3
FROM python:3 as base
FROM base as builder
COPY requirements.txt .
RUN pip install --prefix="/install" -r requirements.txt
FROM base
WORKDIR /loadgen
COPY --from=builder /install /usr/local
# Add application code.
COPY locustfile.py .
# enable gevent support in debugger
ENV GEVENT_SUPPORT=True
ENTRYPOINT locust --host="http://${FRONTEND_ADDR}" --headless -u "${USERS:-10}" 2>&1
# Changes in skaffold.yaml
# I have removed "loadgenerator" as it continuously sends requests imitating realistic user shopping flows to the frontend, which interrupts cartservice debugging.
apiVersion: skaffold/v3
kind: Config
metadata:
name: app
build:
artifacts:
# image tags are relative; to specify an image repo (e.g. GCR), you
# must provide a "default repo" using one of the methods described
# here:
# https://skaffold.dev/docs/concepts/#image-repository-handling
- image: emailservice
context: src/emailservice
- image: productcatalogservice
context: src/productcatalogservice
- image: recommendationservice
context: src/recommendationservice
- image: shoppingassistantservice
context: src/shoppingassistantservice
- image: shippingservice
context: src/shippingservice
- image: checkoutservice
context: src/checkoutservice
- image: paymentservice
context: src/paymentservice
- image: currencyservice
context: src/currencyservice
- image: cartservice
context: src/cartservice/src
docker:
dockerfile: Dockerfile
- image: frontend
context: src/frontend
- image: adservice
context: src/adservice
tagPolicy:
gitCommit: {}
local:
useBuildkit: false
manifests:
kustomize:
paths:
- kubernetes-manifests
deploy:
kubectl: {}
# "gcb" profile allows building and pushing the images
# on Google Container Builder without requiring docker
# installed on the developer machine. However, note that
# since GCB does not cache the builds, each build will
# start from scratch and therefore take a long time.
#
# This is not used by default. To use it, run:
# skaffold run -p gcb
profiles:
- name: gcb
build:
googleCloudBuild:
diskSizeGb: 300
machineType: N1_HIGHCPU_32
timeout: 4000s
# "debug" profile replaces the default Dockerfile in cartservice with Dockerfile.debug,
# which enables debugging via skaffold.
#
# This profile is used by default when running skaffold debug.
- name: debug
activation:
- command: debug
patches:
- op: replace
path: /build/artifacts/7/docker/dockerfile
value: Dockerfile.debug
# The "network-policies" profile is not used by default.
# You can use it in isolation or in combination with other profiles:
# skaffold run -p network-policies, debug
- name: network-policies
patches:
- op: add
path: /manifests/kustomize/paths/1
value: kustomize/components/network-policies
# Run this command to build images and deploy on the Kubernetes cluster
skaffold run
Generating tags...
- emailservice -> emailservice:latest
- productcatalogservice -> productcatalogservice:latest
- recommendationservice -> recommendationservice:latest
- shoppingassistantservice -> shoppingassistantservice:latest
- shippingservice -> shippingservice:latest
- checkoutservice -> checkoutservice:latest
- paymentservice -> paymentservice:latest
- currencyservice -> currencyservice:latest
- cartservice -> cartservice:latest
- frontend -> frontend:latest
- adservice -> adservice:latest
- loadgenerator -> loadgenerator:latest
Checking cache...
- emailservice: Not found. Building
- productcatalogservice: Not found. Building
- recommendationservice: Not found. Building
- shoppingassistantservice: Not found. Building
- shippingservice: Not found. Building
- checkoutservice: Not found. Building
- paymentservice: Not found. Building
- currencyservice: Not found. Building
- cartservice: Not found. Building
- frontend: Not found. Building
- adservice: Not found. Building
- loadgenerator: Not found. Building
Starting build...
Building [checkoutservice]...
NOTE: Please be aware that the initial setup make take a bit longer, approximately ~20 minutes, to complete the build and deploy activities.
After successful completion, you can run below command to check the status of pods.
kubectl get pods
NAME READY STATUS RESTARTS AGE
adservice-7cf748845b-dm87b 1/1 Running 2 (38h ago) 3d20h
cartservice-59cb4f6fdb-2fzcf 1/1 Running 2 (38h ago) 3d20h
checkoutservice-555c97df5f-wc6fk 1/1 Running 2 (38h ago) 3d20h
currencyservice-74fc67f7-5kzvd 1/1 Running 2 (61s ago) 3d20h
emailservice-78d55c667c-fdhdb 1/1 Running 2 (61s ago) 3d20h
frontend-b6747486c-d8css 1/1 Running 2 (38h ago) 3d20h
paymentservice-5588565ccc-xj77r 1/1 Running 2 (61s ago) 3d20h
productcatalogservice-86f5555f5f-pshkf 1/1 Running 2 (38h ago) 3d20h
recommendationservice-57f6f5bfc4-mqnd6 1/1 Running 2 (61s ago) 3d20h
redis-cart-84f5b8c59d-gllcq 1/1 Running 2 (38h ago) 3d20h
shippingservice-6db998f76f-spvf9 1/1 Running 2 (38h ago) 3d20h
Port-Forward
kubectl port-forward deployment/frontend 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
You can run following command to check the impact of port-forward.
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
adservice ClusterIP 10.111.88.96 <none> 9555/TCP 3d20h
cartservice ClusterIP 10.97.87.162 <none> 7070/TCP 3d20h
checkoutservice ClusterIP 10.97.43.177 <none> 5050/TCP 3d20h
currencyservice ClusterIP 10.106.117.61 <none> 7000/TCP 3d20h
emailservice ClusterIP 10.96.127.1 <none> 5000/TCP 3d20h
frontend ClusterIP 10.110.213.167 <none> 80/TCP 3d20h
frontend-external LoadBalancer 10.102.65.249 localhost 80:32688/TCP 3d20h
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d
paymentservice ClusterIP 10.96.13.60 <none> 50051/TCP 3d20h
productcatalogservice ClusterIP 10.104.237.105 <none> 3550/TCP 3d20h
recommendationservice ClusterIP 10.106.71.106 <none> 8080/TCP 3d20h
redis-cart ClusterIP 10.101.151.141 <none> 6379/TCP 3d20h
shippingservice ClusterIP 10.101.213.94 <none> 50051/TCP 3d20h
Now, you can run the application using http://localhost:8080 to the access the Online Boutique application. The images below provide a glimpse of Online Boutique UI.
mirrord in action
Let's consider a scenario where the Online Boutique has reached the staging (or QA) environment, and you would like to address an issue or reproduce it or implement a feature for a specific service, such as the cartservice. Now, you have a few options either replicate entire setup at locally or on the cloud to work on, which can be time-consuming, expensive or occasionally impractical. Moreover, ensuring parity between Dev/Staging/Prod environments can pose additional challenges. In such instances, mirrord offers distinct advantages.
In the context of the Online Boutique application, we will be working with cartservice. Let's open cartservice and put a breakpoint as shown below:
Generate launch.json and task.json using .NET: Generate Assets for Build and Debug from the Command Palette... and enable mirrord and update settings (mirrord.json) as shown below:
Now, we are ready to start the debugging cartservice by pressing F5, and you will be asked to select the pod. From dropdown, choose 'cartservice'.
After selecting the pod, debugger gets activated. (Refer. the below image)
If Online Boutique is not currently open, you can open it and add few items to the Cart. Next, click on the Empty Cart option to see the magic. This activity can also be performed with the other services such as frontend, productcatalogservice, currencyservice, paymentservice, shippingservice, emailservice, checkoutservice, recommendationservice and adservice.
NOTE: While I have utilized mirrord OSS version; however, the Team/Enterprise edition offers more advance capabilities.
Cleanup
Since we have deployed the application using skaffold run command, we can use skaffold delete to clean up the deployed resources.
# Run this command to clean-up the deployed resources
skaffold delete