Skip to main content

Sample Application on GKE — Lab Guide

📖 Configuration Guide

Overview

Estimated time: 1.5–2 hours

This lab deploys the Sample reference application on GKE Autopilot. It is a simple Flask application that demonstrates the full App GKE module feature set: Cloud SQL (PostgreSQL), Filestore NFS, GCS Fuse volume mounts, Redis integration, Workload Identity, Secret Manager, and Cloud Monitoring with uptime checks.

Use this module to understand typical application module patterns before building or studying production modules like Django GKE.

What the Module Automates

  • Builds the sample Flask container image using Cloud Build and pushes it to Artifact Registry
  • Creates a Kubernetes namespace and Deployment for the Flask application
  • Provisions a Cloud SQL PostgreSQL database user and database
  • Stores the database password and Flask secret key in Secret Manager
  • Mounts the Cloud SQL Auth Proxy as a sidecar for Unix socket database connections
  • Provisions a Cloud Filestore NFS instance and mounts it into the pod
  • Creates a GCS bucket and mounts it via GCS Fuse CSI driver
  • Configures Workload Identity for the pod service account
  • Reserves a static external IP and creates a LoadBalancer Kubernetes Service
  • Enables Cloud Monitoring uptime checks and alerting
  • Applies a PodDisruptionBudget to protect availability

What You Do Manually

  • Note the deployment outputs (external IP, namespace, etc.) from the RAD UI deployment panel
  • Obtain GKE cluster credentials with gcloud
  • Verify the Flask application pod is running
  • Access the sample application via the external LoadBalancer IP
  • Explore application endpoints: GCP metadata, health check, database connectivity test
  • Examine Kubernetes resources: Deployment, Service, ServiceAccount, volume mounts
  • Inspect the Workload Identity binding and Secret Manager configuration
  • View application logs in Cloud Logging
  • Monitor pod metrics and uptime check status in Cloud Monitoring

CLI and REST API Overview

# GKE cluster access
gcloud container clusters get-credentials <cluster> --region <region> --project <project>

# Kubernetes workload inspection
kubectl get deployment -n <namespace>
kubectl describe deployment -n <namespace>
kubectl get pod -n <namespace>
kubectl logs -l app=<app> -n <namespace>

# Application access
curl http://<external-ip>/
curl http://<external-ip>/health
curl http://<external-ip>/db

# Secret Manager
gcloud secrets list --project <project>
gcloud secrets versions access latest --secret=<secret-name> --project <project>

Prerequisites

  • Services GCP deployed in the same GCP project (provides VPC, GKE Autopilot cluster, Cloud SQL, Filestore, and Artifact Registry)
  • gcloud CLI installed and authenticated (gcloud auth login)
  • kubectl installed
  • curl installed
  • Access to the RAD UI with permission to deploy modules in the target GCP project

Phase 1 — Deploy [AUTOMATED]

Variables

In the RAD UI, open the Sample GKE module and fill in the deployment form:

VariableRequiredDefaultDescription
project_idYesGCP project ID
deployment_idNoauto-generatedSuffix appended to resource names
tenant_deployment_idNodemoUnique identifier for this deployment
regionNous-central1GCP region
application_nameNosampleInternal identifier used in resource naming
application_versionNolatestContainer image version tag
deploy_applicationNotrueDeploy the GKE workload
min_instance_countNo0Minimum pod replicas (0 = scale to zero when idle)
max_instance_countNo3Maximum pod replicas for HPA scaling
container_resourcesNo{cpu_limit="1000m", memory_limit="512Mi"}Pod CPU and memory limits
application_database_nameNosampledbPostgreSQL database name
application_database_userNosampleuserPostgreSQL user name
enable_nfsNotrueMount a Cloud Filestore NFS volume
nfs_mount_pathNo/mnt/nfsContainer path for the NFS mount
enable_redisNofalseEnable Redis integration
redis_hostNo""Redis server hostname or IP
redis_portNo6379Redis server port
gke_cluster_nameNo""GKE cluster name; leave empty to auto-discover
namespace_nameNo""Kubernetes namespace; leave empty to auto-generate
service_typeNoLoadBalancerKubernetes Service type
reserve_static_ipNotrueReserve a static external IP
resource_labelsNo{}Labels applied to all resources

Deploy

Click Deploy in the RAD UI.

Estimated Deployment Duration

PhaseDuration
Cloud SQL database and user creation1–2 min
Secret Manager secrets< 1 min
Cloud Build image build and push2–4 min
Static IP reservation1–2 min
Kubernetes namespace, Deployment, Service2–4 min
NFS setup and GCS Fuse mount2–3 min
Total8–16 min

Key Outputs

After deployment completes, the following outputs are available in the RAD UI deployment panel:

OutputDescription
service_urlApplication URL (http://<external-ip>)
service_external_ipExternal IP of the LoadBalancer Service
service_nameKubernetes Service name
namespaceKubernetes namespace
database_instance_nameCloud SQL instance name
database_namePostgreSQL database name
database_userPostgreSQL user name
database_password_secretSecret Manager secret name for the DB password
nfs_mount_pathNFS volume mount path inside the container
storage_bucketsGCS bucket names created for the application
deployment_idGenerated deployment suffix
container_imageFull image URI used for the Deployment

Set shell variables for use in later steps:

export PROJECT="your-gcp-project-id"   # set this first — your GCP project ID
export REGION="us-central1" # the region you deployed into
export TOKEN=$(gcloud auth print-access-token)

# Discover the GKE cluster
export CLUSTER=$(gcloud container clusters list \
--project=${PROJECT} \
--format="value(name)" \
--limit=1)

# Configure kubectl
gcloud container clusters get-credentials ${CLUSTER} \
--region=${REGION} \
--project=${PROJECT}

# Discover the namespace (pattern: appsample<tenant><deploymentid>)
export NAMESPACE=$(kubectl get namespaces --no-headers \
-o custom-columns=":metadata.name" | grep "^appsample" | head -1)

# Discover the external IP
export EXTERNAL_IP=$(kubectl get svc -n ${NAMESPACE} \
-o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')

# Discover the database password secret
export DB_SECRET=$(gcloud secrets list \
--project=${PROJECT} \
--filter="name~sample" \
--format="value(name)" \
--limit=1)

Phase 2 — Configure kubectl Access [MANUAL]

Steps

  1. Obtain GKE cluster credentials:

    gcloud container clusters get-credentials <cluster-name> \
    --region <region> \
    --project <project-id>

    gcloud REST equivalent:

    gcloud container clusters list --project <project-id>
  2. Verify the sample application pod is running:

    kubectl get pods -n ${NAMESPACE}

    Expected result:

    NAME                               READY   STATUS    RESTARTS   AGE
    sample-<suffix>-xxxxxxx-xxxxx 2/2 Running 0 5m

    The pod shows 2/2 because the Cloud SQL Auth Proxy runs as a sidecar alongside the Flask application container.

  3. Retrieve the external IP of the LoadBalancer Service:

    kubectl get svc -n ${NAMESPACE}

    Expected result: The LoadBalancer Service shows an EXTERNAL-IP.

    gcloud REST equivalent:

    gcloud compute addresses list --project <project-id>

Phase 3 — Access the Sample Application [MANUAL]

Steps

  1. Access the application root to confirm it is running:

    curl "http://${EXTERNAL_IP}/"

    Expected result: The Flask application responds with a welcome page or JSON payload showing it is alive.

  2. Test the health endpoint:

    curl "http://${EXTERNAL_IP}/health"

    Expected result: JSON response {"status": "ok"} or similar. This endpoint is also used by the Kubernetes liveness probe.

  3. Test the database connectivity endpoint:

    curl "http://${EXTERNAL_IP}/db"

    Expected result: JSON response showing a successful PostgreSQL connection, the database name, and optionally a row count. This confirms the Cloud SQL Auth Proxy sidecar is working and the application can reach the database.

  4. Explore the GCP metadata endpoint (if the sample app exposes one):

    curl "http://${EXTERNAL_IP}/metadata"

    Expected result: JSON response showing instance metadata such as project ID, region, instance name, and service account email — retrieved from the GKE node metadata server.

  5. Open the URL in a browser to explore the application visually.


Phase 4 — Explore Module Patterns [MANUAL]

This phase examines the Kubernetes resources created by the App GKE Foundation Module to understand how application module patterns are implemented.

Steps

  1. Describe the Deployment to see environment variables, volume mounts, and resource limits:

    kubectl describe deployment -n ${NAMESPACE}

    Expected result: Deployment spec showing:

    • Two containers: the Flask app and the Cloud SQL Auth Proxy sidecar
    • Environment variables: DB_NAME, DB_USER, DB_HOST (via Cloud SQL socket path), SECRET_KEY (from Secret Manager), REDIS_HOST, REDIS_PORT
    • Volume mounts: /cloudsql (Cloud SQL socket), /mnt/nfs (Filestore NFS), and any GCS Fuse mounts
  2. Inspect the Kubernetes ServiceAccount and Workload Identity binding:

    kubectl get serviceaccount -n ${NAMESPACE}
    kubectl describe serviceaccount <sa-name> -n ${NAMESPACE}

    Expected result: The ServiceAccount has the annotation iam.gke.io/gcp-service-account: <gsa>@<project>.iam.gserviceaccount.com linking it to a GCP service account via Workload Identity.

    gcloud equivalent (verify IAM binding):

    gcloud iam service-accounts get-iam-policy <gsa>@<project>.iam.gserviceaccount.com \
    --project <project-id>
  3. Check the NFS PersistentVolumeClaim:

    kubectl get pvc -n ${NAMESPACE}

    Expected result: A PVC bound to a Filestore NFS share, using the standard-rwx or similar StorageClass with ReadWriteMany access mode.

  4. Inspect Secret Manager secrets created for the application:

    gcloud secrets list --project <project-id> --filter="name~<deployment-id>"

    Expected result: Secrets for the database password (DB_PASSWORD_*) and Flask secret key (SECRET_KEY_*).

  5. Verify the application can access the GCS bucket via GCS Fuse by checking the mounted path:

    kubectl exec -it <pod-name> -n ${NAMESPACE} -c <app-container> -- ls /mnt/gcs

    Expected result: The GCS Fuse mount is visible inside the container (directory listing may be empty on first access).

  6. View the Horizontal Pod Autoscaler configuration:

    kubectl get hpa -n ${NAMESPACE}

    Expected result: HPA targeting the Deployment with MINPODS and MAXPODS matching the min_instance_count and max_instance_count variables.


Phase 5 — Cloud Logging and Monitoring [MANUAL]

Steps

  1. View application logs using kubectl:

    kubectl logs -l app=<app-name> -n ${NAMESPACE} -c <app-container> --tail=100

    Expected result: Flask request logs showing HTTP requests from the uptime check probe and any requests you made in Phase 3.

  2. View Cloud SQL Auth Proxy sidecar logs:

    kubectl logs -l app=<app-name> -n ${NAMESPACE} -c cloud-sql-proxy --tail=50

    Expected result: Proxy startup messages and connection establishment logs showing the Cloud SQL instance connection string.

  3. In the Google Cloud Console, navigate to Logging > Log Explorer and filter by:

    resource.type="k8s_container"
    resource.labels.namespace_name="${NAMESPACE}"
  4. gcloud equivalent:

    gcloud logging read \
    'resource.type="k8s_container" AND resource.labels.namespace_name="'${NAMESPACE}'"' \
    --project=<project-id> \
    --limit=50
  5. In the Cloud Console, navigate to Monitoring > Uptime checks. Find the uptime check created by the module (named after the application and deployment ID) and verify it shows Passing.

    gcloud equivalent:

    gcloud monitoring uptime list-configs --project=<project-id>
  6. In the Cloud Console, navigate to Monitoring > Metrics Explorer and plot:

    • Pod CPU utilisation: kubernetes.io/container/cpu/request_utilization filtered by namespace_name = ${NAMESPACE}
    • Pod memory usage: kubernetes.io/container/memory/used_bytes filtered by namespace_name = ${NAMESPACE}
  7. Navigate to Kubernetes Engine > Workloads in the Cloud Console, select the sample Deployment, and explore the built-in Observability tab.


Phase 6 — Undeploy [AUTOMATED]

When you are finished, return to the RAD UI, navigate to your deployment, and click Undeploy (or Delete) to remove all resources provisioned by this module: the Kubernetes Deployment, Service, namespace, Cloud SQL database and user, Secret Manager secrets, Filestore NFS instance, GCS bucket, and static IP.

Note: The Cloud SQL instance, GKE cluster, Filestore instance, and VPC are managed by Services GCP and are not affected.

Expected duration: 5–10 minutes.

Resources provisioned by the Services GCP module (VPC, Cloud SQL instance, GKE cluster) are managed separately and must be undeployed via their own RAD UI deployment entry.


Summary

PhaseTypeKey Action
Phase 1 — DeployAUTOMATEDRAD UI deployment builds image, provisions DB, NFS, GCS, and deploys GKE workload
Phase 2 — kubectl AccessMANUALgcloud container clusters get-credentials, verify pod and external IP
Phase 3 — Access ApplicationMANUALcurl the root, /health, and /db endpoints
Phase 4 — Explore Module PatternsMANUALInspect Deployment, Workload Identity, PVC, and Secret Manager integration
Phase 5 — Logging and MonitoringMANUALView pod logs, uptime check status, and CPU/memory metrics
Phase 6 — UndeployAUTOMATEDRAD UI removes all module resources