Below, you can find the infrastructure architecture:
Additionally, here's the architecture of the CI/CD pipeline:
First, I need to set up the GKE cluster that will later use to deploy the app. I am using a GKE standard cluster for this purpose.
gcloud beta container clusters create "k8s-pintu" \
--project "pintu-sre" \
--zone "asia-southeast2-a" \
--no-enable-basic-auth \
--cluster-version "1.27.3-gke.100" \
--release-channel "None" \
--machine-type "e2-highmem-2" \
--image-type "COS_CONTAINERD" \
--disk-type "pd-standard" \
--disk-size "50" \
--metadata disable-legacy-endpoints=true \
--spot \
--num-nodes "3" \
--enable-ip-alias \
--network "projects/pintu-sre/global/networks/default" \
--subnetwork "projects/pintu-sre/regions/asia-southeast2/subnetworks/default" \
--no-enable-intra-node-visibility \
--default-max-pods-per-node "110" \
--no-enable-master-authorized-networks \
--addons HorizontalPodAutoscaling,HttpLoadBalancing \
--enable-autoupgrade \
--enable-autorepair \
--max-surge-upgrade 1 \
--max-unavailable-upgrade 0 \
--no-enable-managed-prometheus \
--enable-shielded-nodes \
--node-locations "asia-southeast2-a"
After creating the cluster, I deployed both the GoLang and Node.js apps on it using a CI/CD script that had been set up earlier. All I had to do was configure the service account and trigger the pipeline.
Then, I proceeded to deploy the service and the ingress using Kubernetes manifests that had been created previously.
I've set up two services to handle the deployments:
- golang-service
- nodejs-service
I've also configured the ingress to route requests based on the host.
For the Google Kubernetes Engine (GKE) setup, I'm using Global Load Balancing (GLB) with Network Endpoint Groups (NEG) as the backend. I've also configured a static IP address (35.201.64.31) for the frontend.
Now, here's the tricky part. GLB automatically creates a health check to see if my pods are ready. However, my app doesn't have a route for the /
path, which causes an issue. To fix it, I manually changed the health check to request /healthz
instead of /
, ensuring that the pods are marked as ready.
After addressing the health check issue, I proceeded to create a DNS record to route traffic to the external IP address.
With the infrastructure in place, I tested access to the services through the internet.
The CI/CD setup also ready, and will automatically trigger of there is a commit in the repository.
The bese URI for Golang service
http://golang.lzy.engineer
The bese URI for Nodejs service
http://golang.lzy.engineer
- Endpoint: /tasks
- Method: GET
- Description: Retrieve a list of all tasks.
- Response: A JSON array containing task objects.
Example Request:
curl -X GET http://$BASE_URI/tasks
Example Response:
[
{
"title": "Task 1",
"description": "Description of Task 1"
},
{
"title": "Task 2",
"description": "Description of Task 2"
}
]
- Endpoint: /tasks
- Method: POST
- Description: Create a new task.
- Request Body: JSON object with title (required) and description (optional) fields.
- Response: A JSON object representing the created task.
Example Request:
curl -X POST http://$BASE_URI/tasks \
-H "Content-Type: application/json" \
-d '{
"title": "Dummy Task",
"description": "This is a dummy task description"
}'
Example Response:
{
"title": "New Task",
"description": "Description of the new task"
}
- Endpoint: /healthz
- Method: GET
- Description: Health check
- Response: a String "OK"
Example Request:
curl -X GET http://$BASE_URI/healtz
Example Response:
"OK"
Keep in mind that this service only uses in-memory storage and doesn't have a database. So, sometimes, the API's response might be different based on which of the three pods handles the request. Each pod has an equal chance, so it's a 1 in 3 probability.