How to use this lesson
Senior engineers are expected to be cloud-literate across providers, to design for failure domains (zones and regions), and to own the cost. Keep the provider-specific details brief, and emphasise the transferable patterns.
Q1. How would you deploy a Spring Boot application to Google App Engine? What's the difference between App Engine and Cloud Run?
Short answer:
-
App Engine (standard, Java 17/21 runtimes): deploy with gcloud app deploy, with an app.yaml (runtime, instance class, entrypoint: java -jar app.jar, scaling settings, environment variables), or through the Maven or Gradle App Engine plugins. App Engine flexible runs containers on managed VMs.
-
Cloud Run: runs any container (build with buildpacks or Jib, push to Artifact Registry, gcloud run deploy). It's request-driven autoscaling (including scale to zero), with concurrency per instance (many requests per container, unlike the classic Lambda model), revisions with traffic splitting (canary or blue-green), and jobs for batch work.
-
The differences:
- Cloud Run is container-native and portable (Knative-based);
- it has flexible runtimes and languages, per-request billing, and easy canaries;
- App Engine is an older, opinionated PaaS, with runtime constraints but integrated services.
Cloud Run is the default choice today for containerised Spring Boot services on GCP.
-
For Spring Boot on scale-to-zero platforms, reduce cold-start time: CDS or AOT caches, native images, or CPU boost on startup, and set min instances for latency-critical services.
Q2. How do you manage environment variables in Azure Web Apps (App Service)?
Short answer: Use App Settings (in the portal, CLI or IaC: az webapp config appsettings set, Bicep, Terraform), which are exposed to the application as environment variables. Spring Boot binds them through relaxed binding (SPRING_DATASOURCE_URL), and connection strings have their own section. For secrets, use Key Vault references (@Microsoft.KeyVault(SecretUri=…) in the App Settings), resolved by the platform using the app's managed identity, so no secrets are stored in the configuration. Use deployment slots with slot-specific ("sticky") settings for staging and production swaps. Restart behaviour applies when settings change. Spring Cloud Azure integrates Key Vault and App Configuration as property sources.
Q3. What are the key differences between AWS, GCP and Azure for Java developers?
Short answer:
- The core services map closely:
- compute: EC2 / Compute Engine / Virtual Machines;
- managed Kubernetes: EKS / GKE / AKS;
- serverless containers: Fargate / Cloud Run / Container Apps;
- functions: Lambda / Cloud Functions / Azure Functions;
- managed SQL: RDS or Aurora / Cloud SQL or AlloyDB or Spanner / Azure SQL or Database for PostgreSQL;
- messaging: SQS, SNS, MSK, Kinesis / Pub/Sub / Service Bus, Event Hubs;
- secrets: Secrets Manager / Secret Manager / Key Vault;
- observability: CloudWatch / Cloud Operations / Azure Monitor.
- Their flavours:
- AWS: the broadest service catalogue, and the most mature ecosystem. Spring Cloud AWS, SnapStart.
- GCP: strong Kubernetes (GKE), data and AI (BigQuery, Vertex AI), a simple Cloud Run, a global VPC network, and Spring Cloud GCP.
- Azure: strong enterprise and Microsoft integration (Entra ID, .NET shops), and Spring Cloud Azure (co-developed with the Spring team). Azure Spring Apps is being retired, so migrate to Container Apps or AKS.
- For Java teams, all three support Java 21, containers, and managed Kubernetes. The decision usually rests on the organisation's existing cloud, data platform, identity, pricing and compliance. Use portable layers (containers, Kubernetes, OpenTelemetry, Terraform) to limit lock-in where it matters.
Q4. How do you monitor your application on GCP or Azure?
Short answer:
-
GCP (Cloud Operations, formerly Stackdriver):
- Cloud Logging (structured JSON logs from stdout are parsed automatically, and the trace field links them to Cloud Trace);
- Cloud Monitoring (metrics, dashboards, uptime checks, SLO monitoring and alerting);
- Error Reporting, Cloud Profiler;
- Managed Service for Prometheus.
Integrate with OpenTelemetry, or Spring Cloud GCP starters, and Micrometer exporters.
-
Azure Monitor:
- Application Insights (the Java agent auto-instruments Spring Boot: requests, dependencies, exceptions, distributed tracing, live metrics);
- Log Analytics (KQL queries);
- metrics and alerts, workbooks;
- Azure Managed Prometheus and Grafana.
-
Vendor-neutral approach: OpenTelemetry instrumentation, exporting to whichever backend (a cloud-native one, or Grafana, Datadog, New Relic), with the same dashboards and SLO alerts across clouds.
Q5. What's the role of Spring Cloud Bus in configuration refresh?
Short answer: Spring Cloud Bus links the application instances through a message broker (RabbitMQ or Kafka). With Config Server, when configuration changes in Git:
- a webhook calls the Config Server's
/monitor endpoint (spring-cloud-config-monitor), or someone calls /actuator/busrefresh;
- a
RefreshRemoteApplicationEvent is broadcast on the bus;
- every subscribed instance (or those matching a destination such as
orders:**) refreshes its @RefreshScope beans and @ConfigurationProperties.
So you don't have to call /actuator/refresh on each instance individually. The caveats: it's an extra broker dependency, it gives eventual consistency across instances, and not every property is safely refreshable. On Kubernetes, many teams replace this with ConfigMap reloads, or rolling restarts through GitOps.
Q6. How do you implement blue-green deployment with Spring Cloud and Gateway?
Short answer:
- Deploy green (the new version) alongside blue, both registered in discovery under distinguishable metadata (
eureka.instance.metadata-map.version=green), or as separate service IDs (orders-blue, orders-green).
- Gateway routing:
- switch the route's
uri from lb://orders-blue to lb://orders-green (a configuration change, refreshed through Config or the Bus, or a Git commit);
- or use the
Weight predicate for gradual canary shifting (Weight=orders, 90 to blue and 10 to green);
- or route by header or cookie for internal testers first.
- Verify green (smoke tests, metrics), then shift 100%, and keep blue running for fast rollback (flip the route back).
- Retire blue after a bake period.
Requirements: a backward-compatible database schema, shared stateless sessions, and consumers that handle both message versions. On Kubernetes, the same idea is usually done with Services, Argo Rollouts or a mesh, rather than the gateway.
Q7. How do you deploy multi-region, high-availability microservices?
Short answer:
- First, decide the objective: active-passive (disaster recovery: a warm standby region, with an RPO and RTO in minutes) or active-active (users served from several regions, low latency, and region-failure tolerance). Active-active is much harder, because of the data.
- Traffic: global DNS or Anycast load balancing (Route 53 latency or failover routing, Cloud Load Balancing, Azure Front Door), with health-based failover, and CDNs.
- Stateless compute: identical deployments per region (GitOps per region, and the same images).
- Data (the crux):
- replicate across regions: Aurora Global Database, Cloud Spanner, Cosmos DB, DynamoDB global tables, CockroachDB, Kafka MirrorMaker 2 or Cluster Linking;
- choose the consistency model per domain: a single-writer home region per entity or tenant (data partitioning by region) avoids write conflicts; multi-writer needs conflict resolution;
- understand the replication lag, and its impact on reads and writes.
- Stateful dependencies: caches are regional (warm them), and message consumers have regional ownership, or idempotent cross-region processing.
- Resilience: zonal redundancy within each region first. Regional circuit breakers. Regular failover drills (game days), and runbooks.
- Cost and complexity: double infrastructure, cross-region data transfer, and operational overhead. Only do active-active when the business case requires it.
Learn it in depth → Global Content Delivery
Q8. How do you control cost while scaling microservices in the cloud?
Short answer:
- Visibility: tagging and labels per team and service, cost allocation, dashboards (Cost Explorer, Kubecost or OpenCost), budgets and anomaly alerts, and unit economics (cost per order or request).
- Right-sizing: tune the requests and limits from actual usage (VPA recommendations), consolidate over-provisioned services, pick the right instance families, and use ARM (Graviton), which is often 20–40% cheaper for Java.
- Elasticity: autoscaling with sensible minimums, scale to zero for idle and non-production workloads, and schedule shutdowns of development and test environments.
- Purchasing: Savings Plans or reserved instances for the baseline, and Spot for fault-tolerant or batch capacity (Karpenter handles the mix).
- Architecture:
- caching (fewer database and compute calls);
- asynchronous batching;
- avoiding chatty cross-AZ or cross-region traffic;
- VPC endpoints instead of NAT for AWS services;
- storage lifecycle policies;
- log and metric retention and sampling (observability bills often surprise people);
- fewer, larger microservices where the split doesn't pay off.
- Governance: FinOps reviews, and making cost a non-functional requirement in design reviews.
Learn it in depth → Cost Awareness
Short answer: Go from the symptom to the cause, with data:
- Scope it: which endpoints, regions or tenants? Since when? Did it start with a deployment, a config change, or a traffic change (deployment markers on the dashboards)?
- Golden signals and SLOs: latency percentiles, errors, traffic and saturation (CPU, CPU throttling in containers, memory, GC, thread and connection pools, and database connections).
- Distributed traces for the slow requests: find the slow span (a database query, a downstream call, lock waits, retries).
- Dependencies: database metrics (slow queries, locks, replication lag), cache hit ratio, third-party latency, queue lag, and cloud service limits or throttling (API rate limits, IOPS or throughput credits exhausted, burstable instance credits, NAT gateway port exhaustion).
- JVM level: JFR and async-profiler on an affected instance (CPU hotspots, allocation, lock contention), GC logs, and thread dumps.
- Infrastructure: noisy neighbours, node pressure, pod evictions, network latency across availability zones, DNS resolution latency, and load balancer health or unhealthy targets.
- Fix, and verify with before and after metrics. Add alerts and regression tests.
Q10. What is the ELK stack?
Short answer: Elasticsearch + Logstash + Kibana (plus Beats or Elastic Agent), the Elastic Stack for centralised logging and search analytics:
- Beats/Filebeat (or Fluent Bit) ship the logs from hosts and containers;
- Logstash or Elasticsearch ingest pipelines parse and enrich them;
- Elasticsearch indexes and stores them (with ILM for hot, warm and cold retention);
- Kibana searches, visualises and alerts (Discover, dashboards, and APM through the Elastic APM agents).
The open-source fork is OpenSearch (with OpenSearch Dashboards). The alternatives: Grafana Loki (label-indexed logs, cheaper at scale), cloud-native logging, and Splunk. The operational cost: Elasticsearch clusters need capacity planning (shards, heap, storage), and retention policies control the spend.
Q11. What is Grafana?
Short answer: Grafana is an open-source visualisation and observability platform:
- dashboards over many data sources: Prometheus (metrics), Loki (logs), Tempo (traces), Mimir, Elasticsearch, CloudWatch, SQL databases;
- alerting (unified alert rules, contact points, routing);
- correlations (from metrics to exemplar traces to logs);
- SLO dashboards, annotations (deployments);
- a large library of community dashboards (JVM Micrometer, Spring Boot, Kafka).
The "LGTM" stack (Loki, Grafana, Tempo, Mimir) forms a complete open-source observability backend, and Grafana Cloud offers it managed.
Follow-up questions this topic invites — and their answers
Q: What are RPO and RTO?
A: Recovery Point Objective: the maximum acceptable data loss, measured in time (for example 5 minutes). Recovery Time Objective: the maximum acceptable downtime before service is restored (for example 30 minutes). They drive the backup, replication and failover design, and its cost.
Q: How do you avoid vendor lock-in without losing the benefits of managed services?
A: Use portable interfaces where they're cheap (containers, Kubernetes, OpenTelemetry, SQL, Terraform), and accept lock-in consciously for high-value managed services. Encapsulate provider SDK usage behind your own ports and adapters, so a migration is contained.
Q: Why does CPU throttling hurt Java services in containers?
A: CFS quotas pause the whole container once it uses up its CPU time in a period (100 ms by default). JIT and GC threads consume quota too, so latency spikes appear even at moderate average CPU. Set realistic requests, avoid overly tight CPU limits, and watch container_cpu_cfs_throttled_periods_total.
Q: What's a good first step when cloud bills spike?
A: Break the costs down by service, tag and usage type, to find the driver (data transfer, NAT, logs ingestion, oversized instances, forgotten resources). Then fix the biggest line item first, and add budgets and anomaly alerts.