Skip to main content

Sample_GKE Module — Configuration Guide

Sample_GKE is a wrapper module that sits on top of App_GKE. It deploys a pre-configured reference Flask application (Python 3.11, PostgreSQL 15, optional Redis, optional NFS) on GKE Autopilot. Its purpose is to serve as a working example of how to build a custom application module on top of App_GKE.

Most configuration variables in Sample_GKE are passed through unchanged to App_GKE. For the meaning, options, validation steps, and gcloud CLI commands for any variable that appears in App_GKE, refer to the App_GKE Configuration Guide. This guide documents only what is unique to Sample_GKE: its layered architecture, the pre-configured application it ships, and the specific behaviours it imposes on top of App_GKE.

Note: Variables marked as platform-managed are set and maintained by the platform. You do not normally need to change them.


Module Architecture

Sample_GKE composes two internal layers:

Sample_GKE
├── Sample_Common (application layer — Flask app, secrets, db-init job, Redis service)
└── App_GKE (infrastructure layer — GKE Autopilot, Cloud SQL, NFS, networking, CI/CD)

Sample_Common is a shared internal module that produces the application-specific configuration object (application_config) consumed by App_GKE. It is responsible for:

  • Generating a random 32-character Flask SECRET_KEY and storing it in Secret Manager.
  • Defining a db-init Kubernetes Job (using postgres:15-alpine) that runs the bundled database initialisation script (scripts/db-init.sh) on first deployment.
  • Optionally defining an internal Redis additional service (using redis:alpine) when enable_redis = true.
  • Providing a custom Cloud Build configuration that builds the sample Flask application from the bundled Dockerfile in Sample_Common/scripts/.

App_GKE receives the merged configuration from Sample_Common and provisions all GCP and Kubernetes infrastructure: the GKE cluster, Cloud SQL instance, NFS PersistentVolume, Artifact Registry repository, Secret Manager secrets, IAM bindings, networking, CI/CD pipelines, and observability resources.

You do not interact with Sample_Common directly. All inputs are exposed as variables on Sample_GKE itself.


Pre-configured Application

When deployed with default settings, Sample_GKE provides:

ComponentDetails
Application frameworkFlask (Python 3.11-slim), listening on port 8080
Container image sourcecustom — built from the bundled Dockerfile via Cloud Build
DatabasePostgreSQL 15, with a db-init Kubernetes Job that runs on first deployment
SecretSECRET_KEY — auto-generated 32-character random string, stored in Secret Manager, injected as the SECRET_KEY environment variable
RedisOptional — when enable_redis = true, an internal Redis (redis:alpine) additional service is deployed alongside the application
NFSOptional — when enable_nfs = true, a shared NFS volume is mounted at nfs_mount_path

The db-init job and the SECRET_KEY secret are managed entirely by the module. You do not need to pre-create them.


Behaviours Unique to Sample_GKE

1. Minimum Instance Count Override

The min_instance_count variable is exposed so you can tune it, but Sample_GKE internally overrides the value passed to App_GKE to be at least 1:

# sample.tf
sample_module = merge(module.sample_app.config, {
min_instance_count = 1 # Always keep at least 1 pod warm
})

Why: Unlike Cloud Run, GKE Autopilot does not natively support true scale-to-zero for standard Deployments. Setting min_instance_count = 1 ensures at least one pod is always ready to serve traffic without a cold-start delay. If you set min_instance_count = 0 in your configuration, the module overrides it to 1 internally.

2. Redis Host Fallback to Sidecar (127.0.0.1)

When enable_redis = true and redis_host is not set (or is empty), Sample_GKE injects REDIS_HOST=127.0.0.1 into the application:

# sample.tf
REDIS_HOST = var.enable_redis ? (
var.redis_host != null && var.redis_host != "" ? var.redis_host : "127.0.0.1"
) : ""

Why: In GKE, the Redis additional service (a separate redis:alpine Deployment) runs within the cluster. Using 127.0.0.1 as the fallback host is appropriate for configurations where Redis runs locally alongside the application. For an external Redis instance (e.g. Cloud Memorystore), set redis_host explicitly to the instance's private IP address.

Contrast with Sample_CloudRun: Cloud Run does not support pod-level co-location, so Sample_CloudRun does not fall back to 127.0.0.1 — you must always provide an explicit redis_host when using Redis with Cloud Run.

3. Explicit Secret Value Injection (explicit_secret_values)

Sample_GKE passes the raw value of the Flask SECRET_KEY directly to App_GKE via the explicit_secret_values mechanism:

# main.tf
explicit_secret_values = {
SECRET_KEY = module.sample_app.secret_values["FLASK_SECRET_KEY"]
}

Why: When a Secret Manager secret is created and then immediately referenced by a Kubernetes workload in the same Terraform apply, there is a read-after-write consistency window during which the secret version may not yet be globally available. By passing the raw value directly, App_GKE can create the Kubernetes Secret object without needing to read back from Secret Manager on the first apply. On subsequent applies, the secret is already present and this mechanism has no visible effect.

This is a GKE-specific pattern. Sample_CloudRun does not use explicit_secret_values because Cloud Run fetches secrets from Secret Manager at instance startup (after propagation is complete), avoiding this timing issue.

4. Resource Naming (resource_prefix)

Sample_GKE computes a deterministic resource_prefix and passes it to Sample_Common so that the Flask SECRET_KEY secret name is aligned with the naming convention used by App_GKE for all other resources:

resource_prefix = "app${var.application_name}${var.tenant_deployment_id}${local.random_id}"

This ensures the Secret Manager secret created by Sample_Common is named consistently with the secrets, Cloud SQL instance, GCS buckets, and Kubernetes resources created by App_GKE. You do not need to set this — it is computed automatically.


Configuration Reference

All configuration variables in Sample_GKE are passed through to App_GKE. The table below maps each configuration group to the corresponding section of the App_GKE Configuration Guide, noting any Sample_GKE-specific defaults or overrides.

GroupDescriptionSample_GKE Defaults / OverridesReference
Group 0Module Metadata & Configurationmodule_description defaults to "Sample_GKE: A sample application module…". module_documentation points to the GKE App docs URL. module_services includes GKE Autopilot, Cloud Build, Artifact Registry, Cloud SQL, Filestore (NFS), Secret Manager, Workload Identity, Cloud Monitoring, and Uptime Checks. All other variables are identical to App_GKE.App_GKE Guide — Group 0
Group 1Project & IdentityNo changes. project_id, tenant_deployment_id, support_users, resource_labels, and resource_creator_identity behave identically to App_GKE.App_GKE Guide — Group 1
Group 2Application Identityapplication_name defaults to "sample". application_display_name defaults to "Sample Application". application_description defaults to "Sample application to showcase GKE Autopilot features". application_version defaults to "latest". All other behaviour is identical to App_GKE.App_GKE Guide — Group 2
Group 3Runtime & Scalingcontainer_image_source defaults to "custom" (the pre-built Flask image is built by Cloud Build). container_image defaults to the Cloud Run hello image as a placeholder. min_instance_count variable defaults to 0, but is overridden to 1 internally by the module (see Minimum Instance Count Override above). All other variables (max_instance_count, container_port, container_protocol, container_resources, timeout_seconds, enable_image_mirroring, enable_vertical_pod_autoscaling, enable_cloudsql_volume, cloudsql_volume_mount_path, service_annotations, service_labels) behave identically to App_GKE.App_GKE Guide — Group 3
Group 4Environment Variables & Secretsenvironment_variables and secret_environment_variables are passed through to App_GKE. Additionally, the module automatically injects ENABLE_REDIS, REDIS_HOST, and REDIS_PORT (see Redis Host Fallback above) and SECRET_KEY (sourced from the auto-generated Secret Manager secret). secret_rotation_period, secret_propagation_delay, and manage_storage_kms_iam behave identically to App_GKE. enable_secrets_store_csi_driver is not exposed in Sample_GKE (the module manages secret delivery internally).App_GKE Guide — Group 4
Group 5GKE Backend ConfigurationAll variables (gke_cluster_name, gke_cluster_selection_mode, namespace_name, workload_type, service_type, session_affinity, enable_multi_cluster_service, configure_service_mesh, enable_network_segmentation, termination_grace_period_seconds, deployment_timeout, prereq_gke_subnet_cidr) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 5
Group 6Jobs & Scheduled Tasksinitialization_jobs defaults to the pre-configured db-init job (using postgres:15-alpine and the bundled db-init.sh script). You may override this with a custom job list. When enable_redis = true, an internal Redis additional service is added automatically — you do not need to declare it in additional_services. cron_jobs and additional_services are passed through to App_GKE unchanged.App_GKE Guide — Group 6
Group 7CI/CD & GitHub IntegrationAll variables (enable_cicd_trigger, github_repository_url, github_token, github_app_installation_id, cicd_trigger_config, enable_cloud_deploy, cloud_deploy_stages, enable_binary_authorization) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 7
Group 8Storage & Filesystem — NFSAll variables (enable_nfs, nfs_mount_path, nfs_instance_name, nfs_instance_base_name) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 8
Group 9Storage & Filesystem — GCSAll variables (create_cloud_storage, storage_buckets, gcs_volumes) are passed through unchanged. Sample_Common does not define any additional GCS buckets beyond what you configure here. Refer to the base guide for full descriptions.App_GKE Guide — Group 9
Group 10Database ConfigurationAll variables (database_type, sql_instance_name, sql_instance_base_name, application_database_name, application_database_user, database_password_length, enable_postgres_extensions, postgres_extensions, enable_mysql_plugins, mysql_plugins, enable_auto_password_rotation, rotation_propagation_delay_sec) are passed through unchanged. application_database_name defaults to "cloudrunapp" in App_GKE but is overridden to "cloudrunapp" in the Sample_GKE variables. The database is automatically initialised by the db-init job. Refer to the base guide for full descriptions.App_GKE Guide — Group 10
Group 11Backup Schedule & RetentionAll variables (backup_schedule, backup_retention_days, enable_backup_import, backup_source, backup_file, backup_format) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 11
Group 12Custom SQL ScriptsAll variables (enable_custom_sql_scripts, custom_sql_scripts_bucket, custom_sql_scripts_path, custom_sql_scripts_use_root) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 12
Group 13Observability & Healthstartup_probe_config and health_check_config are passed through to App_GKE unchanged. They use the same variable names and object structure as in App_GKE. alert_policies and uptime_check_config are also passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 13
Group 14Reliability PoliciesAll variables related to pod disruption budgets, HPA behaviour, and reliability settings are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 14
Group 15Resource QuotaAll variables related to Kubernetes namespace resource quotas are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 15
Group 16Custom Domain, Static IP & Network ConfigurationAll variables (application_domains, enable_custom_domain, reserve_static_ip, static_ip_name, enable_cdn, admin_ip_ranges, network_tags, enable_vpc_sc) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 16
Group 17Identity-Aware ProxyAll variables (enable_iap, iap_authorized_users, iap_authorized_groups, iap_oauth_client_id, iap_oauth_client_secret, iap_support_email) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 17
Group 18Cloud ArmorAll variables (enable_cloud_armor, cloud_armor_policy_name) are passed through unchanged. Refer to the base guide for full descriptions.App_GKE Guide — Group 18

Redis Configuration Summary

The table below summarises the three Redis-related variables and how they interact with the module's behaviour:

VariableDefaultBehaviour when enable_redis = true
enable_redistrueDeploys an internal redis:alpine additional service. Injects ENABLE_REDIS=true, REDIS_HOST, and REDIS_PORT into the application container.
redis_host""If left empty, REDIS_HOST is set to 127.0.0.1 (local fallback). If set to an IP or hostname, that value is used instead (for external Redis such as Cloud Memorystore).
redis_port"6379"Injected as REDIS_PORT. Change only if your Redis instance uses a non-standard port.
redis_auth""If set, stored in Secret Manager and injected securely. Leave empty for unauthenticated Redis (acceptable for internal cluster services on a private network).

Validating a Sample_GKE Deployment

Because Sample_GKE delegates all infrastructure to App_GKE, validation follows the same procedures described in the App_GKE Configuration Guide. The additional resources managed by Sample_Common can be validated as follows:

Flask SECRET_KEY secret:

# Confirm the Flask SECRET_KEY secret exists
gcloud secrets list --project=PROJECT_ID \
--filter="name:secret-key" \
--format="table(name,createTime)"

# View the secret's replication and rotation config
gcloud secrets describe SECRET_NAME \
--project=PROJECT_ID \
--format="yaml(replication,rotation)"

DB-init Kubernetes Job:

# List all Kubernetes Jobs in the application namespace
kubectl get jobs -n NAMESPACE -o wide

# View the status of the db-init job
kubectl describe job db-init -n NAMESPACE

# View db-init job logs
kubectl logs -l job-name=db-init -n NAMESPACE

Redis additional service (when enable_redis = true):

# List all Deployments in the namespace (redis should appear alongside the app)
kubectl get deployments -n NAMESPACE -o wide

# Confirm the Redis Service exists and has a cluster IP
kubectl get service -n NAMESPACE -l app=APPLICATION_NAME-redis

# Confirm REDIS_HOST and REDIS_PORT are injected into the app container
kubectl exec -n NAMESPACE POD_NAME -- env | grep REDIS