DevOps NetworkDevOpsNetwork
DevOps NetworkDevOpsNetwork

Menu

DashboardDaily ChallengePlannerLeaderboardRoadmapHubsInterview ExperiencesModulesCheatsheetsTech BlogQuizzesInterview PrepProjectsResourcesReport Bug

More

TopicsConceptsGlossaryCommunity
Join Free
DevOps NetworkDevOpsNetwork
Dashboard
Daily Challenge
Planner
Leaderboard
Roadmap
Interview Experiences
ResourcesReport Bug

GCP Interview Questions

50 real GCP interview questions with detailed answers on IAM, VPC networking, GKE, BigQuery and Cloud Run — grouped by difficulty.

50questions with answers
Questions
EASY (13)
Question 1What is a Google Cloud project, and why does every resource belong to one?Question 2What is the difference between Compute Engine and App Engine?Question 3What is a bucket in Google Cloud Storage, and what determines where its data physically lives?Question 4What is BigQuery?Question 5What is a VPC in GCP, and how is it different from a VPC in AWS?Question 6What are IAM's basic roles (Owner, Editor, Viewer), and why do interviewers ask about avoiding them?Question 7What is Google Kubernetes Engine (GKE)?Question 8What is Cloud Run?Question 9What is a service account, and how is it different from a regular user account?Question 10What is the difference between a region and a zone in GCP?Question 11What is Cloud SQL?Question 12What is the difference between Cloud CDN and a load balancer in GCP?Question 13What's the difference between Cloud Storage and Persistent Disk?
MEDIUM (25)
Question 14When would you choose Cloud Run over Cloud Functions, and vice versa?Question 15How do you choose between Compute Engine, App Engine, GKE, Cloud Run, and Cloud Functions for a new service?Question 16What's the difference between predefined and custom IAM roles, and when would you write a custom role?Question 17Explain the GCP resource hierarchy (organization, folder, project) and how IAM policies inherit down it.Question 18What's the difference between Shared VPC and VPC Peering, and when would you use each?Question 19What types of load balancing does GCP offer, and how do you pick one?Question 20How do BigQuery and Cloud SQL differ, and when would you use each?Question 21Explain object versioning and lifecycle management in Cloud Storage.Question 22What are the Cloud Storage storage classes, and how do you decide which one to use?Question 23What is Workload Identity in GKE, and why is it preferred over service account keys?Question 24What are Spot (formerly preemptible) VMs, and when are they an appropriate choice?Question 25In GCP VPC firewall rules, what's the difference between ingress and egress, and what's the default behavior?Question 26What are IAM Conditions, and what problem do they solve that roles alone can't?Question 27How does GKE Autopilot differ from Standard mode?Question 28What is Cloud Build, and what does a typical CI/CD pipeline look like on GCP?Question 29What are BigQuery partitioning and clustering, and why do they matter for cost?Question 30Explain how autoscaling works for a Managed Instance Group (MIG).Question 31What's the difference between Cloud Spanner and Cloud SQL?Question 32What is Private Google Access, and when do you need it?Question 33Explain the difference between synchronous request/response and asynchronous processing using Pub/Sub.Question 34How do you monitor and log an application running on GCP?Question 35What is Binary Authorization, and what does it protect against?Question 36What's the difference between an internal and external load balancer in GCP?Question 37What is the principle of least privilege, and how does GCP's IAM Recommender help enforce it in practice?Question 38How does GCP's Cloud NAT work, and why would a VM with no external IP still need it?
HARD (12)
Question 39A GKE cluster's node pool isn't scaling down even though traffic and load have clearly dropped. How do you debug it?Question 40Design a multi-region, highly available architecture on GCP for a stateless web service with a relational backend.Question 41Your BigQuery costs spiked roughly 10x overnight with no application code change. How do you find and fix the cause?Question 42A service account used by a CI/CD pipeline can suddenly access a Cloud Storage bucket it could reach yesterday, but not today. How do you debug the permission failure?Question 43You're deciding whether to migrate a legacy VM-based monolith to GKE or to Cloud Run. What trade-offs actually drive that decision?Question 44Design a disaster recovery strategy for a Cloud SQL database with a Recovery Point Objective (RPO) of 5 minutes.Question 45Multiple teams' projects need to share networking, but each team must not be able to see or modify another team's firewall rules. How do you design this with Shared VPC and IAM?Question 46How do Kubernetes NetworkPolicies interact with GCP VPC firewall rules in a GKE cluster, and when do you need both?Question 47A Cloud Run service intermittently returns 503 errors under moderate, not extreme, load. What's your debugging approach?Question 48Design a data pipeline that migrates a legacy on-premises relational database into BigQuery with minimal downtime.Question 49How would you structure IAM and Organization Policies to enforce least privilege across dev, staging, and prod environments within a single GCP organization?Question 50Two microservices in the same GKE cluster need to communicate, but a security review requires that all traffic between them be encrypted in transit, with mutual authentication, and audited — without changing application code. How do you achieve this on GCP?

A Google Cloud project is the container that every resource — VMs, buckets, databases, service accounts, APIs — lives inside; nothing in GCP exists outside of a project.

This matters because the project is GCP's real security and billing boundary, not the organization or a folder. IAM policies, enabled APIs, and quotas are all attached at the project level by default, so "which project is this resource in" is usually the first question worth asking when something is unreachable or a bill looks wrong. A project has three identifiers: a project ID (globally unique, immutable, used in gcloud commands and URLs), a project number (auto-assigned), and a display name (editable, not unique).

Reference: Creating and managing projects

Compute Engine gives you raw virtual machines you configure and manage yourself; App Engine is a fully managed platform where you deploy application code and Google handles the servers, scaling, and patching.

Choose Compute Engine when you need control over the OS, custom drivers, specific kernel versions, or licensing — basically anything that requires you to touch the machine. Choose App Engine when you have a standard web app or API in a supported language runtime and don't want to think about infrastructure at all; it handles traffic splitting, versioning, and automatic scaling for you. The trade-off is flexibility versus operational overhead — App Engine Standard in particular has real constraints (limited runtimes, no arbitrary binaries, sandboxed execution) that push complex workloads toward Compute Engine, containers, or GKE instead.

Reference: Compute Engine documentation

A bucket is the top-level container you create in Cloud Storage to hold objects (files); every object must belong to exactly one bucket, and bucket names are globally unique across all of GCP, not just your project.

The location type you pick at creation time decides where the data lives: a region stores data in a single geographic area for the lowest latency to nearby compute, a dual-region replicates across two specific regions for resilience with still-decent latency, and a multi-region spreads data across a large geographic area (like all of the US) for maximum availability. Location can't be changed after creation — moving a bucket's data to a different location means creating a new bucket and copying objects over — so this is a decision worth getting right up front, usually by putting the bucket in the same region as the compute that reads from it most.

Reference: Bucket locations

BigQuery is Google Cloud's fully managed, serverless data warehouse built for running fast SQL analytics over very large datasets without provisioning any servers.

Its defining architectural choice is that storage and compute are separated: your data sits in Google's distributed columnar storage, and queries spin up compute ("slots") only for the duration of the query. That's why it scales to scanning terabytes in seconds and why cost is usually driven by how much data a query scans, not by how much hardware you rented. It's the tool teams reach for over a traditional relational database when the workload is analytical (aggregating over huge tables) rather than transactional (many small reads/writes with strict consistency needs).

Reference: BigQuery overview

A VPC (Virtual Private Cloud) in GCP is a private, software-defined network that gives your resources isolated IP space, routing, and firewall rules — the same basic idea as any cloud VPC.

The detail that trips people up coming from AWS is scope: a GCP VPC is a global resource, and its subnets are regional, not zonal. That means one VPC can have subnets in multiple regions worldwide with no VPN or peering required to route between them privately — in AWS, by contrast, a VPC is confined to a single region and connecting regions needs peering or Transit Gateway. This global-by-default model is why GCP networking questions often hinge on "which region is the subnet in," not "which VPC."

Reference: VPC network overview

Owner, Editor, and Viewer are IAM's original "basic roles," granting broad permissions across nearly every service in a project — Viewer can read everything, Editor can read and modify everything, Owner can additionally manage IAM and billing.

They get asked about because they're a well-known anti-pattern: granting Editor to a person or service account gives it write access to dozens of services it will never touch, which fails the principle of least privilege and makes audits meaningless. The expected answer is that production access should use predefined roles (Google-curated, scoped to one service, like roles/bigquery.dataViewer) or custom roles (a hand-picked set of permissions for a specific job), with basic roles reserved for personal sandbox projects or genuine full-project ownership.

Reference: Understanding roles

GKE is Google's managed Kubernetes service — Google runs and patches the control plane (the API server, etcd, scheduler) for you, and you're responsible for the worker nodes (in Standard mode) or not even that (in Autopilot mode).

It exists because running Kubernetes yourself means operating a distributed system on top of a distributed system: upgrading the control plane, securing etcd, and handling node failures are all non-trivial. GKE takes the control plane off your plate entirely and, in Autopilot, takes node management off your plate too, billing per pod resource request instead of per VM. It's the default choice when a team already thinks in Kubernetes primitives — Deployments, Services, CRDs — rather than wanting to adopt Kubernetes for its own sake.

Reference: GKE overview

Cloud Run is Google's fully managed serverless platform for running containers — you give it a container image, and it handles scaling, load balancing, and infrastructure, including scaling all the way down to zero instances when there's no traffic.

Unlike Cloud Functions, which deploys a single function from source, Cloud Run deploys a whole container, so it can run any language, any framework, and any number of routes inside one service. It sits between Cloud Functions (simpler, event-driven, one entry point) and GKE (full Kubernetes control) on the abstraction spectrum: you get container flexibility without cluster management.

Reference: Cloud Run overview

A service account is an identity meant for a workload rather than a human — an application, a VM, or a CI/CD pipeline authenticates as a service account to call GCP APIs, instead of a person typing in a password.

It's a hybrid of two things: it's both an identity (something IAM roles can be granted to) and a resource (something IAM roles can be granted on, controlling who can impersonate it). Every service account has an email-like identifier and can be attached to compute resources or used to generate short-lived credentials. Attaching a service account with only the permissions a workload actually needs — instead of using personal credentials or long-lived keys — is the standard way automation gets access in GCP.

Reference: Service accounts overview

A region is a specific geographic area (like us-central1), and a zone is an isolated deployment area within that region (like us-central1-a) with its own power, cooling, and networking.

Every region has at least three zones, and zones within a region are connected by low-latency links but are engineered to fail independently — a power outage in one zone shouldn't take down another zone in the same region. This is why high availability in GCP usually means spreading resources across multiple zones within a region at minimum, and across multiple regions for disaster recovery from a region-level event (which is rare, but not impossible).

Reference: Regions and zones

Cloud SQL is GCP's fully managed relational database service, supporting MySQL, PostgreSQL, and SQL Server, where Google handles patching, backups, replication, and failover.

It's the go-to choice when an application needs a traditional relational database and the team doesn't want to run and patch database servers by hand. It supports read replicas for scaling reads and high-availability configurations that fail over to a standby in a different zone automatically. The main thing to know for an interview is what it isn't: it doesn't scale writes horizontally the way Cloud Spanner does, so past a certain single-instance write throughput, Spanner (or a re-architected sharding strategy) becomes the answer instead.

Reference: Cloud SQL overview

Cloud CDN caches content at Google's edge locations close to users, while a load balancer distributes incoming requests across your backend instances — they solve different problems and are normally used together, not as alternatives.

A load balancer's job is routing and health checking: deciding which healthy backend gets a given request. Cloud CDN's job is avoiding the backend entirely for content that hasn't changed, by serving a cached copy from an edge point of presence near the user. In GCP, Cloud CDN is actually enabled as a feature on a global external Application Load Balancer's backend service, not run as a separate standalone product, which is why the two show up together in architecture diagrams. The practical distinction worth naming: CDN helps with static or slow-changing content (images, JS bundles, cacheable API responses), while the load balancer is doing work on every single request regardless of whether CDN serves it, including all dynamic, uncacheable traffic.

Reference: Cloud CDN overview

Cloud Storage is object storage accessed over HTTP(S) APIs for storing and retrieving whole files (objects) with no filesystem semantics; Persistent Disk is block storage that attaches to a Compute Engine VM or GKE node and behaves like a regular disk the OS can format and mount.

The distinction that actually matters in an interview is access pattern and durability model. Cloud Storage objects are immutable once written (you replace the whole object, you don't edit part of it in place), can be accessed from anywhere with the right credentials, and scale to essentially unlimited size with 11 nines of durability built in. Persistent Disk behaves like a local drive — the OS can write to arbitrary byte offsets, mount a filesystem on it, and it's tied to a specific VM (or, for regional PDs, replicated synchronously across two zones) rather than being globally reachable. A common mistake is reaching for Persistent Disk to store files an application should be treating as objects (uploads, generated reports, backups) purely out of familiarity with "a disk," when Cloud Storage is both cheaper and the architecturally correct choice for that access pattern.

Reference: Persistent Disk overview · Cloud Storage overview

Choose Cloud Functions for small, single-purpose, event-triggered code — a webhook handler, an image-resize job, a Pub/Sub message processor — and Cloud Run when you have (or want to package) a full containerized application or API with multiple routes and custom dependencies.

The mechanical difference is what you deploy: Cloud Functions takes source code plus a dependency file and Google builds the container for you behind the scenes, with one function as the single entry point. Cloud Run takes a container image you build yourself, which can run any language or framework and expose as many routes as you like. Both scale to zero and both can respond to HTTP requests or Eventarc/Pub/Sub events, so the deciding factor in practice is usually team habits: if your team already thinks in Docker, Cloud Run fits the workflow better; if you want to skip containers entirely for a small piece of glue code, Cloud Functions removes that step. Cold starts also tend to be a bit faster on Cloud Functions because the runtime is lighter weight, though this gap has narrowed.

Reference: Cloud Run vs Cloud Functions

It comes down to how much infrastructure control you need versus how much you want Google to manage for you — the five options sit on a spectrum from "you manage everything" to "you manage nothing."

Option Choose it when...
Compute Engine You need OS-level control, custom drivers, or specific licensing/performance tuning.
GKE You're already using Kubernetes, need multi-container pods, a service mesh, or complex orchestration across many services.
Cloud Run You have a stateless containerized service or API and want serverless scaling, including scale-to-zero, without running a cluster.
App Engine You want a batteries-included PaaS with versioning and traffic splitting and don't want to build a container at all.
Cloud Functions You need a small piece of code that reacts to one kind of event and doesn't hold state between calls.

The common trap answer is picking GKE by default because it's the most "serious" option — in practice, most new stateless services should start on Cloud Run, since it's the simplest path that still gives container flexibility, and teams only move to GKE once they hit a concrete Kubernetes-specific need (StatefulSets, DaemonSets, custom schedulers, a service mesh).

Reference: Choosing a compute option

Predefined roles are curated by Google for a specific service — like roles/pubsub.publisher or roles/storage.objectViewer — bundling exactly the permissions that job needs, and they're updated automatically as Google adds new permissions to that role's purpose. Custom roles are ones you build yourself from a list of individual permissions, giving you exact control but making you responsible for maintaining them.

The usual guidance is to reach for a predefined role first, because it's maintained for you and covers the common case. You write a custom role when the closest predefined role is either too broad (it includes a permission you don't want to grant) or too narrow (it's missing one permission you need, and the next role up grants ten more you don't want). A common example: a predefined "viewer" role for a service might include the ability to list resources across the whole project, when you only want to view resources tagged for one team — a custom role solves that gap, at the cost of you now owning its upkeep as the underlying API surface evolves.

Reference: Creating and managing custom roles

GCP's resource hierarchy is Organization → Folders (optional, nestable) → Projects → individual resources, and it exists so that access and policy can be set once near the top and apply everywhere below, instead of being configured resource by resource.

An IAM policy set at the organization level applies to every folder and project underneath it; a policy set on a folder applies to every project inside that folder; a policy set on a project applies to every resource inside it. Crucially, this inheritance is additive only — a more specific level can grant additional access, but it cannot revoke a permission granted higher up. So if someone has Viewer at the org level, you cannot take that away at the project level; you can only add more roles at the project level, not subtract. This is a favorite interview trap because people assume IAM works like a firewall with allow/deny at each layer, when in practice the fix for "this user has too much access at a lower level" is almost always to narrow the grant at the higher level, or restructure the hierarchy — org policies (a separate mechanism) can enforce constraints, but IAM role bindings themselves only add up.

Reference: Resource hierarchy

Shared VPC lets multiple projects share a single VPC network owned by a designated host project, so resources in service projects deploy into subnets they don't own but can use as their own; VPC Peering instead connects two separate, independently-owned VPCs so they can talk over internal IPs while staying fully separate networks.

Shared VPC is the better fit when a central network or platform team wants to manage IP ranges, firewall rules, and routes once for many teams' projects — it gives centralized control and transitive connectivity (if A and B are both service projects on the same host, they can reach each other). It requires all participating projects to be in the same organization. VPC Peering is the better fit for connecting independently-managed networks, including across organizations — for instance, connecting your VPC to a SaaS vendor's VPC, or to a VPC in a company you're integrating with after an acquisition. The trade-off to know cold: peering is not transitive (if A peers with B and B peers with C, A cannot reach C through B), while Shared VPC's single flat network doesn't have that limitation.

Reference: Shared VPC overview

GCP's load balancers split along two axes: global vs. regional and external vs. internal, and the right one depends on where your traffic originates and what layer you need to balance at.

  • Global external Application Load Balancer — Layer 7 (HTTP/S), anycast IP, routes to the closest healthy backend worldwide. Use it for internet-facing web apps that need a single IP with global reach and features like URL-based routing or Cloud CDN integration.
  • Regional external Application/Network Load Balancer — same idea but scoped to one region, useful when compliance requires traffic to stay within a region.
  • Internal Application/Network Load Balancer — Layer 7 or Layer 4, private IP only, for service-to-service traffic inside a VPC that should never touch the public internet.
  • Network Load Balancer (passthrough) — Layer 4, preserves the client's source IP end to end, used when the backend needs to see the real client IP or handle non-HTTP protocols.

The question an interviewer is really testing is whether you know it's not "one load balancer" like a traditional on-prem appliance — you pick based on protocol (HTTP vs. arbitrary TCP/UDP), scope (does traffic need to span regions), and whether traffic is public or internal-only.

Reference: Choosing a load balancer

BigQuery is a serverless analytical data warehouse built for scanning huge datasets with SQL; Cloud SQL is a managed transactional relational database built for an application's day-to-day reads and writes.

The underlying difference is workload shape. BigQuery uses columnar storage and a massively parallel query engine, which makes it excellent at "aggregate this column across a billion rows" but a poor fit for "update this one row and read it back immediately" — it isn't designed for the frequent small transactions an application backend generates, and per-query costs make many tiny queries expensive. Cloud SQL is row-oriented, supports ACID transactions, indexes, and foreign keys the way a normal application database does, but doesn't scale to BigQuery's analytical throughput. In practice, teams use Cloud SQL as the system of record for an app and periodically export or stream that data into BigQuery (often via Datastream or Dataflow) to run analytics without hammering the production database.

Reference: When to use BigQuery

Object Versioning keeps prior versions of an object every time it's overwritten or deleted, instead of the change being destructive, and lifecycle management lets you set rules that automatically transition or delete objects based on age or other conditions.

With versioning enabled on a bucket, deleting an object just marks the current version as "noncurrent" rather than removing the data, so you can recover from an accidental overwrite or delete. That protection has a cost, though: every version is billed as its own object, so buckets with versioning left on indefinitely can silently accumulate storage cost. Lifecycle rules are the usual fix — for example, a rule that moves objects to Nearline storage after 30 days, Coldline after 90, and permanently deletes noncurrent versions after 365 days. Getting this pairing right (versioning for safety, lifecycle rules to bound the cost) is a common practical question because teams often turn on versioning and forget the second half.

YAML
# Example lifecycle rule: delete noncurrent versions after 30 days
rule:
- action:
type: Delete
condition:
isLive: false
daysSinceNoncurrentTime: 30

Reference: Object Versioning · Lifecycle management

Cloud Storage has four storage classes — Standard, Nearline, Coldline, and Archive — that trade a lower storage price for a higher retrieval cost and a longer minimum storage duration, and the right one depends on how often you expect to read the data.

Standard is for data accessed frequently (hot data, active websites, data being actively processed). Nearline suits data accessed less than once a month, like monthly backups. Coldline fits data accessed roughly once a quarter or less, such as disaster-recovery data. Archive is for data you almost never touch — long-term compliance retention — where retrieval can take longer and cost more, but storage is cheapest. All four classes have the same latency and durability; the only differences are price and minimum storage duration. The practical mistake to call out is picking a class based on how important the data is rather than how often it's read — Archive is fine for critical data you'll never read, and Standard can be wasteful for unimportant data you read constantly. Bucket-level or per-object lifecycle rules can automate the downgrade over time instead of requiring a manual choice up front.

Reference: Storage classes

Workload Identity lets a Kubernetes pod authenticate to Google Cloud APIs as a Google service account without ever holding a downloadable key file — it binds a Kubernetes Service Account (KSA) to a Google Service Account (GSA), and the pod's requests are transparently exchanged for short-lived GSA credentials.

The alternative it replaces — mounting a downloaded JSON key file into a pod, or attaching a service account key as a Kubernetes secret — creates a long-lived credential that can leak through logs, container images, or a compromised pod, and has no automatic rotation. Workload Identity's credentials are short-lived, scoped to the specific KSA-to-GSA binding, and never touch disk as a static file, which removes an entire class of key-leak incidents. This is one of the most common "why does this matter" security questions in a GKE interview: the honest trap answer is "just mount a key file, it's simpler," and the strong answer is naming the specific risk (long-lived, unrotated, exfiltratable credentials) that Workload Identity is designed to close.

Reference: Workload Identity

Spot VMs are spare Compute Engine capacity sold at a steep discount (up to ~60-91% off on-demand pricing) that Google can reclaim with very short notice whenever it needs that capacity back for on-demand customers.

They're appropriate for workloads that are stateless, fault-tolerant, and cheap to restart or reschedule — batch jobs, CI/CD runners, rendering pipelines, and fault-tolerant parts of a GKE cluster (like a node pool running non-critical or replicated services). They are not appropriate for anything that can't tolerate an abrupt interruption without data loss — a single-replica database, a long-running job with no checkpointing, or anything holding user-facing sessions with no failover. In GKE, the usual pattern is a dedicated Spot node pool with a taint, and only workloads with a matching toleration (ones you've explicitly decided can handle preemption) get scheduled there, so a stateful workload never accidentally lands on ephemeral capacity.

Reference: Spot VMs

Ingress rules control traffic coming into a resource, egress rules control traffic going out of it, and by default every GCP VPC has an implied rule that allows all egress and denies all ingress from outside the network.

That default-deny-ingress posture is intentional — nothing is reachable from outside until you explicitly write an allow rule for it, which is the opposite of some on-prem setups where "allow everything, then lock down" is the norm. Rules are evaluated by priority (lower number wins), and you can target them at specific instances by network tag or service account rather than applying them network-wide, which is the recommended way to scope access tightly (for example, an allow rule that only applies to instances tagged web-server, rather than every VM in the VPC). A common interview trap: people assume you also need to explicitly allow egress for outbound-only workloads, when in fact you'd only add an egress deny rule if you specifically want to restrict outbound traffic, since it's open by default.

Reference: VPC firewall rules overview

IAM Conditions let you attach a conditional expression to a role binding, so access is only granted when that condition is true — most commonly based on time, resource name/type, or request attributes — instead of a role being either fully granted or not granted at all.

Roles and permissions answer "what can this identity do," but they have no built-in concept of "only during this window" or "only for resources matching this pattern." Conditions fill that gap: a classic example is granting temporary access that expires automatically (request.time < timestamp("2026-12-31T00:00:00Z")), or scoping a role so it only applies to Cloud Storage objects with a particular prefix, rather than the whole bucket. This is worth mentioning in an interview specifically because it shows you know IAM isn't just "assign a role" — conditions are how you avoid creating a narrower custom role just to express a temporary or resource-scoped grant.

Reference: IAM Conditions overview

In GKE Standard, you choose and manage the node pools yourself — machine types, node counts, upgrades, security hardening; in GKE Autopilot, Google manages the nodes entirely and you only specify what your pods need, with billing based on the CPU, memory, and storage your pods actually request rather than on provisioned VM capacity.

Autopilot applies a set of secure-by-default settings automatically (Shielded Nodes, Workload Identity, no privileged pods by default) which removes a whole category of hardening work, and it removes node-level operational tasks like patching and right-sizing nodes. The trade-off is control: Autopilot restricts or disallows some node-level customizations that Standard allows — DaemonSets have restrictions, certain privileged operations aren't permitted, and you can't SSH into nodes. The practical guidance most teams follow: default to Autopilot for typical stateless workloads to cut operational overhead, and drop to Standard when you have a concrete requirement Autopilot doesn't support, like specialized hardware scheduling or node-level agents that need broader privileges.

Reference: GKE Autopilot overview

Cloud Build is GCP's managed CI/CD service that runs a sequence of build steps — each step is a container — defined in a YAML config, typically triggered by a push to a source repository.

A common pipeline looks like: a push to Cloud Source Repositories or a connected GitHub/GitLab repo triggers Cloud Build, which runs tests, builds a container image, pushes it to Artifact Registry, and then deploys it — to Cloud Run, GKE, or App Engine — often gated by a manual approval step or a canary rollout for production. Security-conscious pipelines add Binary Authorization in the deploy step, so only images that passed the pipeline's checks (and are signed accordingly) are allowed to actually run in the cluster.

◈ DIAGRAM
push to repo → Cloud Build trigger → run tests → build image
→ push to Artifact Registry → Binary Authorization check
→ deploy to Cloud Run / GKE

Reference: Cloud Build overview

Partitioning splits a table into segments based on a column (usually a date or timestamp), and clustering sorts data within those segments by other columns — both exist so that a query can skip scanning data it doesn't need, which directly reduces the bytes billed for the query.

BigQuery's default pricing charges by bytes scanned, not rows returned, so a query against an unpartitioned table with a WHERE date = '2026-09-01' filter still scans the entire table unless BigQuery can prune it. Partitioning by that date column means the query only touches the one day's partition. Clustering goes further within a partition — if you frequently filter or aggregate by, say, customer_id, clustering by that column lets BigQuery skip whole blocks of non-matching rows even inside a single day's data. The interview-worthy nuance: partitioning has a hard limit on the number of partitions per table (4,000 by default) and works best on columns with natural, bounded cardinality like dates, while clustering handles higher-cardinality columns and can be combined with partitioning for compounding savings.

Reference: Partitioned tables · Clustered tables

A Managed Instance Group autoscaler watches a signal you configure — CPU utilization, a Cloud Monitoring metric, load balancing capacity, or a schedule — and adds or removes VM instances from the group to keep that signal near a target you set.

You define a minimum and maximum instance count as guardrails, and the autoscaler works within that range. A key detail interviewers probe is the cooldown period: after a new instance is added, it's excluded from the average metric calculation for a configurable window (default 60 seconds) so the autoscaler doesn't overreact to a not-yet-warmed-up instance's low initial load and immediately scale back down. MIGs also handle self-healing independently of autoscaling — if a health check fails on an instance, the MIG recreates it, which is a separate mechanism from scaling up or down for load.

Reference: Autoscaling groups of instances

Cloud SQL is a managed instance of a traditional relational database (MySQL, PostgreSQL, SQL Server) that scales vertically and via read replicas; Cloud Spanner is Google's globally distributed relational database that scales horizontally for both reads and writes while still offering strong consistency and SQL.

The practical dividing line is write throughput and geographic scale. Cloud SQL's write capacity is bounded by a single primary instance's hardware — you can add read replicas, but writes still go through one node. Spanner shards data across many nodes and regions and still guarantees external consistency across that whole distributed system, which is genuinely hard to build yourself. That capability comes at a real cost: Spanner is pricier, has a steeper schema-design learning curve (interleaved tables, careful primary key choice to avoid hotspotting), and is overkill for an application that will never outgrow a single Cloud SQL instance. The honest interview answer is "use Cloud SQL until you have measured evidence you need Spanner" — reaching for Spanner by default is a common overengineering trap.

Reference: Cloud Spanner overview

Private Google Access lets VM instances that only have internal (private) IP addresses reach Google APIs and services — like Cloud Storage or BigQuery — without needing an external IP or routing through the public internet.

It's needed whenever you've deliberately removed external IPs from your VMs for security reasons (a common hardening step) but those VMs still need to call Google APIs, such as reading from a GCS bucket or writing logs. Without it enabled on the subnet, a VM with no external IP simply can't reach *.googleapis.com at all. It's enabled per-subnet, not per-VM, and it's distinct from Private Service Connect, which is for privately reaching specific published services (including third-party ones) rather than Google APIs generally — mixing those two up is a common small mistake in networking interviews.

Reference: Private Google Access

Synchronous processing means the caller waits for the work to finish before getting a response — like an HTTP API call that does the work inline; asynchronous processing with Pub/Sub means the caller publishes a message and moves on immediately, while one or more subscribers process it independently, on their own schedule.

Pub/Sub decouples the producer from the consumer: the publisher doesn't need to know who's listening, how many subscribers there are, or whether they're currently up, and a slow or temporarily-down subscriber doesn't block the publisher. This is the right shape when work can tolerate some delay, when you want to fan a single event out to multiple independent consumers (say, one subscriber updates a search index while another sends a notification, both off the same event), or when you need to absorb bursts of traffic — messages queue up rather than overwhelming a downstream service. The trade-off is that you give up an immediate response and have to design for eventual, at-least-once delivery: subscribers can receive a message more than once, so processing needs to be idempotent.

Reference: Pub/Sub overview

GCP's observability stack has three main pieces working together: Cloud Logging collects logs, Cloud Monitoring tracks metrics and fires alerts, and Cloud Trace captures distributed request latency — and most managed services (Cloud Run, GKE, App Engine) send data to all three automatically with no extra setup.

In practice you'd set up log-based metrics or alerting policies in Cloud Monitoring for the signals that actually indicate a problem (error rate, latency percentiles, saturation), rather than trying to watch raw logs. Cloud Trace becomes valuable once a request crosses multiple services, since it shows where time is actually being spent across that chain rather than just at one hop. For anything beyond the built-in tools, both Logging and Monitoring export to BigQuery or third-party tools like Grafana or Splunk via sinks, which is the usual answer for "how do you centralize logs from a multi-cloud or hybrid environment."

Reference: Cloud Monitoring overview · Cloud Logging overview

Binary Authorization is a deploy-time security control for GKE and Cloud Run that only allows container images to run if they're signed by an authority you trust — typically proof that the image passed your CI pipeline's build and test steps, or a vulnerability scan.

It protects against a specific failure mode: someone (a compromised credential, a misconfigured pipeline, or a well-meaning engineer under pressure) deploying an image that skipped your normal process — built on a laptop, never scanned, never tested — straight to production. Without it, IAM permission to deploy is the only gate, and IAM says nothing about what's inside the image. With a policy in place, even someone with deploy permissions can't run an unsigned image; the cluster or Cloud Run service enforces the check at admission time and rejects it. It's most useful paired with a CI/CD pipeline that automatically signs images after they pass tests and a vulnerability scan, so the signature becomes proof of "this went through the real pipeline," not just an extra manual step.

Reference: Binary Authorization overview

An external load balancer has a public IP and accepts traffic from the internet; an internal load balancer has a private IP reachable only from within your VPC (or a connected network), and is used purely for service-to-service traffic that should never be internet-facing.

Both come in HTTP(S)/Layer-7 and TCP-UDP/Layer-4 flavors, so "internal vs external" and "Layer 4 vs Layer 7" are two separate, independent choices you make together — for example, an internal Application Load Balancer is a real, common option for routing HTTP traffic between microservices inside a VPC without exposing anything publicly. The reason this comes up in interviews is a real production mistake: accidentally provisioning an external load balancer in front of an internal-only service (like an admin API or a backend database proxy) exposes it to the internet even if you intended it to stay private, so knowing which type you're creating — not just trusting a firewall rule to catch it — matters.

Reference: Load balancing overview

The principle of least privilege means granting an identity only the permissions it actually needs to do its job — nothing more — so that a compromised or misused credential has the smallest possible blast radius; IAM Recommender is GCP's tool for finding where that principle has drifted, by analyzing which permissions a principal has actually used over the past 90 days and suggesting a narrower role that still covers real usage.

In practice, roles tend to accumulate over time — someone gets Editor "just to unblock a task" and it's never revoked, or a service account was granted a broad predefined role early on and the workload only ever uses a fraction of it. IAM Recommender surfaces these gaps automatically instead of requiring a manual audit of every binding, showing exactly which granted permissions were never exercised and proposing a smaller role or a custom role that would have covered the observed activity. The nuance worth mentioning: it's a recommendation, not an automatic enforcement — accepting it can break a workload that uses a permission rarely (say, a monthly batch job), so recommendations should be reviewed against known infrequent usage before being applied, not auto-approved.

Reference: IAM Recommender overview

Cloud NAT lets VM instances that have no external IP address initiate outbound connections to the internet — for things like downloading packages or calling an external API — without exposing them to inbound connections from the internet at all.

A VM with only an internal IP is, by design, unreachable from the outside; that's the whole point of removing the external IP for security. But "unreachable from outside" and "can't reach outside" are two different properties, and outbound-only internet access is often still required — patching the OS, pulling a package, calling a third-party webhook. Cloud NAT provides that one-directional capability: it translates the VM's internal IP to a shared external IP (or pool of them) for outbound traffic only, and never accepts a new inbound connection, so the security posture of "no external IP means unreachable" is preserved while still allowing egress. This is distinct from Private Google Access, which specifically covers reaching Google APIs privately — Cloud NAT is the general-purpose answer for reaching any external destination, Google or not.

Reference: Cloud NAT overview

Start from the cluster autoscaler's own reasoning rather than guessing — gcloud container clusters describe and the autoscaler events (visible via kubectl get events or the GKE console's autoscaler status) will usually tell you exactly why it refuses to remove a node, because scale-down is conservative by design and blocks for well-documented reasons.

The most common causes, roughly in order of likelihood:

  1. A pod on the node can't be evicted. Pods without a controller (bare Pods, not part of a Deployment/ReplicaSet/Job), pods using local storage, pods with a restrictive PodDisruptionBudget that would be violated by removing them, or pods with the cluster-autoscaler.kubernetes.io/safe-to-evict: "false" annotation will all block a node from being drained.
  2. Requests, not actual usage, are what the autoscaler looks at for bin-packing decisions, but a node still can't be removed if any pod on it can't be rescheduled elsewhere — even one stuck pod pins the whole node.
  3. The node pool's minimum node count is set higher than what current load needs — worth checking before assuming it's a scheduling problem at all.
  4. kube-system pods with no disruption budget or with safe-to-evict implicitly false by default configuration, common on nodes running certain DaemonSets or system add-ons.

The fix is almost always to address the blocking pod directly — add a controller, loosen or add a PodDisruptionBudget, move local storage to something the autoscaler can safely move, or explicitly mark the pod safe to evict — rather than treating it as an autoscaler bug, since the autoscaler is behaving correctly by refusing to potentially disrupt a workload it can't safely reschedule.

Reference: Cluster autoscaler — scale down

The shape that satisfies "stateless service + relational backend + multi-region HA" on GCP is: a global external Application Load Balancer in front, Cloud Run (or a regional MIG behind a backend service) deployed in at least two regions, and a database layer chosen based on how strict the availability requirement really is.

◈ DIAGRAM
┌──────────────────────────┐
users ─────► │ Global external HTTP(S) │
│ Load Balancer (anycast) │
└───────────┬───────────────┘
┌─────────────┴─────────────┐
┌──────▼──────┐ ┌──────▼──────┐
│ Cloud Run │ │ Cloud Run │
│ us-central1 │ │ europe-west1 │
└──────┬──────┘ └──────┬──────┘
│ │
┌──────▼─────────────────────────────▼──────┐
│ Database layer (see options below) │
└──────────────────────────────────────────────┘

For the database, there's a real trade-off to walk through rather than one right answer:

  • Cloud SQL with cross-region read replicas is the simplest option — writes go to a single primary region, reads can be served locally from replicas elsewhere. It's HA for reads, but a regional outage in the primary's region means writes are down until you manually promote a replica, which is not instant and not automatic.
  • Cloud Spanner gives you a single logical database that's natively multi-region, with automatic failover and no manual promotion step, at higher cost and with schema-design trade-offs (see the Spanner-vs-Cloud-SQL question above).

The load balancer's global anycast IP and health checks route users to the nearest healthy region automatically, and because the app tier is stateless, any region can serve any user — the database choice is really the whole decision here, and the honest answer in an interview is naming the RPO/RTO the business actually needs before picking one, since Spanner solves a problem Cloud SQL replicas genuinely can't, but at a cost many services don't need to pay.

Reference: Cloud Architecture Center — reliability

Start with the INFORMATION_SCHEMA.JOBS view (or the BigQuery Admin resource charts / billing export), sorted by total_bytes_billed, to find which specific queries scanned the most data in the window where the spike happened — a cost spike is almost always a handful of expensive queries, not every query getting slightly worse.

Once you find the offending query or queries, the usual culprits are:

  • A SELECT * against a large table, or a query missing a partition filter on a partitioned table (which forces a full-table scan instead of pruning to one partition) — this is the single most common cause.
  • A scheduled query or dashboard refresh whose frequency or backing table grew (someone changed a refresh interval from daily to hourly, or the underlying table quietly grew 10x).
  • A new table without partitioning or clustering that a frequently-run query now hits, when the equivalent old table was well-partitioned.
  • Someone switched from on-demand to flat-rate pricing (or vice versa) without realizing the cost model changed, or a reservation's slot count changed.

Once the cause is identified, the fix is usually one of: add a required partition filter to the table (require_partition_filter: true forces every query against it to include one), rewrite the query to select only needed columns, or move a runaway scheduled query onto a materialized view so repeated runs don't re-scan the base data. Longer term, setting a custom cost control — a per-query or per-project bytes-scanned limit — catches this class of incident before it becomes a bill, which is worth mentioning as the preventive half of the answer, not just the reactive fix.

Reference: Control costs in BigQuery

Work through the layers in order, because a "permission denied" on GCS can come from several independent places, not just the IAM role binding you'd check first.

  1. Check the actual error. A 403 with storage.objects.get denied is a genuine IAM/permission problem; a 403 that mentions the organization policy engine, or a 404 where you expected a 403, points somewhere else entirely.
  2. Confirm the IAM binding is still there and on the right resource. Use gcloud storage buckets get-iam-policy gs://bucket and gcloud projects get-iam-policy. A binding that was on the bucket can be accidentally removed by someone re-running Terraform or gsutil with a stale policy that doesn't include it — check recent changes via Cloud Audit Logs (setIamPolicy events) filtered to that resource, which will show who changed what, when.
  3. Check for a newly-added Organization Policy constraint, such as a domain-restricted sharing policy or a constraints/iam.allowedPolicyMemberDomains rule that now blocks the service account's identity type, or a VPC Service Controls perimeter that started denying cross-perimeter access. This is a very common cause of "it worked yesterday" because org policies apply org-wide and can be changed by someone with no connection to your pipeline at all.
  4. Check the service account key or Workload Identity Federation binding hasn't been rotated, disabled, or expired. A key rotation policy, or someone disabling an unused-looking service account, produces exactly this symptom.
  5. Check for a VPC Service Controls perimeter change if the bucket and the caller are supposed to be inside the same perimeter — an unrelated perimeter update elsewhere in the org can silently start blocking previously-allowed cross-project access.

Audit Logs are the fastest way to actually answer "what changed," since checking every layer by hand is slow — searching Admin Activity logs for setIamPolicy or UpdatePolicy events around the time it broke usually points straight at the change.

Reference: Troubleshoot access · Cloud Audit Logs

The honest framing is that this is rarely a pure technology choice — it's a question of what the monolith actually needs that Cloud Run can't give it, because Cloud Run is the lower-effort path when it fits.

Reasons Cloud Run is enough:

  • The app is (or can be made) stateless, or state can move to a managed backend (Cloud SQL, Memorystore, GCS) instead of local disk.
  • It communicates over HTTP or can be adapted to, since Cloud Run's request model is built around that.
  • The team wants scale-to-zero economics and doesn't want to run a cluster.
  • Deploys are simple, single-container units — no need for sidecars, init containers, or complex multi-container pod coordination.

Reasons GKE is the real requirement:

  • The monolith needs to run multiple tightly coupled containers in one scheduling unit (a genuine multi-container pod, not just multiple services).
  • It needs long-lived, persistent local state or specific storage semantics (StatefulSets, PersistentVolumes with specific access modes) that don't map cleanly to a stateless request model.
  • It needs custom networking behavior — a service mesh, fine-grained network policies, or protocols beyond HTTP/gRPC that Cloud Run doesn't support well.
  • There's an existing Kubernetes-specific dependency: custom controllers, CRDs, or an operator the team relies on.
  • Steady, very high sustained traffic where GKE's cost-per-request at scale beats Cloud Run's, because reserved/committed-use capacity is cheaper than pay-per-request at high, constant volume.

The trap answer is defaulting to GKE because it's the "more capable, more serious" platform — for a genuinely stateless HTTP monolith without deep Kubernetes-specific dependencies, that adds real operational burden (cluster upgrades, node management, more moving parts) for capability the app will never use. The stronger answer names the specific requirement (state, multi-container coordination, custom networking, or sustained-traffic cost) that forces GKE, rather than treating it as the default "production-grade" choice.

Reference: Choosing a compute option

A 5-minute RPO means you can afford to lose at most 5 minutes of writes in a disaster, which rules out relying on scheduled backups alone (typically daily, sometimes hourly at best) and requires continuous or near-continuous replication of every transaction.

The mechanism that actually achieves this is cross-region read replicas with continuous replication, combined with Cloud SQL's point-in-time recovery (which requires binary/transaction logging to be enabled). In a real failure:

  1. Primary region fails. The application's writes stop reaching the primary instance.
  2. Promote a cross-region replica to become the new primary — this is a manual, deliberate action in Cloud SQL, not automatic; you decide when to trigger it, which matters because an automatic failover during a brief network blip can cause more damage (a split-brain scenario) than the outage itself.
  3. Replication lag determines actual data loss. Cross-region replication in Cloud SQL is asynchronous, so the true RPO is bounded by however far behind the replica's replication lag was at the moment of failure — under normal conditions this is seconds, well inside a 5-minute target, but it's not a hard guarantee, and it's worth monitoring replication lag as an ongoing metric with an alert if it grows.
  4. Update connection strings / DNS (or use a proxy layer like Cloud SQL Auth Proxy pointed at the new primary) so the application starts writing to the promoted instance.

For an RTO (how fast you're back up) alongside this RPO, you'd also want the application tier already deployed in the DR region (idle or warm), since standing up compute and promoting the database sequentially after a disaster is much slower than having the app tier ready and just repointing it. The honest caveat worth stating out loud in an interview: if the RPO requirement were 0 (zero data loss, ever), Cloud SQL's asynchronous replication can't guarantee that, and the answer becomes Cloud Spanner, which offers synchronous multi-region replication with no data-loss window by design — that's the real line between "Cloud SQL DR is good enough" and "you actually need Spanner."

Reference: Cloud SQL high availability and disaster recovery

Shared VPC alone doesn't isolate firewall rules between teams by default — firewall rules live in the host project and, without further scoping, any Shared VPC Admin on the host can see and edit all of them. The isolation has to come from combining Shared VPC's structure with hierarchical firewall policies and tightly scoped IAM roles, not from Shared VPC's basic mechanics alone.

The design:

  1. One host project, one VPC, with subnets allocated per team (or per environment) so each team's resources land in their own subnet(s) within the shared network.
  2. Firewall Insights / hierarchical firewall policies at the folder level, scoped so that each team's folder gets rules that apply only to its own subnet's IP ranges or resource tags — this is what actually prevents "team A's rule accidentally matches team B's VMs," rather than relying on people writing careful CIDR ranges by hand.
  3. IAM roles granted narrowly per team, using the predefined Compute Engine roles that separate network administration from instance administration: roles/compute.networkAdmin for whoever manages the shared network resources (typically the central platform team only), and roles/compute.networkUser granted to each team scoped to their specific subnet, not project-wide — this is the key detail, since networkUser can be bound at the subnet level, meaning Team A's networkUser grant on their subnet gives them zero visibility into Team B's subnet.
  4. No team gets networkAdmin on the host project, since that role can create, modify, or delete firewall rules and routes affecting everyone; only the central network/platform team holds it.

The IAM detail that's easy to miss and worth calling out explicitly: granting compute.networkUser at the subnet level rather than the project level is what actually achieves "team A can deploy into their subnet but can't touch team B's" — granting it project-wide on the host defeats the whole point of Shared VPC's isolation model.

Reference: Shared VPC provisioning · Hierarchical firewall policies

They operate at completely different layers and don't know about each other: VPC firewall rules control traffic at the network layer between VMs (GKE nodes) based on IP, tags, or service accounts, while Kubernetes NetworkPolicies control traffic between pods, enforced inside the cluster's networking layer (on GKE, via Dataplane V2's eBPF-based enforcement, or the older Calico-based enforcement on clusters that predate it).

A VPC firewall rule has no visibility into which pod on a node a packet is destined for — from the VPC's perspective, all pods on a given node share that node's IP for many purposes, so firewall rules can restrict traffic to and from the cluster's nodes (say, blocking all external access to the node pool except through the load balancer) but cannot express "pod A can talk to pod B but not pod C," since that distinction only exists inside the cluster's pod network. That's exactly what NetworkPolicies are for.

You need both, doing different jobs, in most real clusters:

  • VPC firewall rules define the outer boundary — what can reach the cluster's nodes from outside the VPC, and what the nodes can reach outside the cluster (an on-prem database, another VPC, the internet).
  • NetworkPolicies define the inner boundary — which pods can talk to which other pods once traffic is already inside the cluster, enforcing a default-deny-between-namespaces posture so a compromised pod in one namespace can't freely reach a database pod in another.

Skipping NetworkPolicies because "the VPC firewall already restricts access" is a common and serious gap: the firewall stops external attackers from reaching the cluster, but does nothing to limit lateral movement between pods once something inside the cluster is compromised — that's a separate threat model, and only NetworkPolicies address it.

Reference: GKE network policy · Dataplane V2

503s under moderate load on Cloud Run almost always mean requests are arriving faster than the service can accept them given its current concurrency and instance settings — not that the service is broken — so the investigation starts with Cloud Run's own scaling and concurrency configuration before looking at application code.

  1. Check maxInstances. If it's capped too low, Cloud Run simply can't scale out further once demand exceeds what the current instances can handle at their configured concurrency, and incoming requests get rejected with a 503 once the queue backs up. This is the single most common cause and the first thing to check.
  2. Check per-instance concurrency setting. If concurrency is set high (say, 80) but the container can only actually handle a handful of simultaneous requests before CPU or memory saturates, Cloud Run keeps routing requests to an already-overloaded instance instead of spinning up a new one, because from Cloud Run's perspective that instance still has "room." Lowering concurrency to match what the container can genuinely handle at once often fixes this directly.
  3. Check minInstances and cold starts. If minInstances is 0 and traffic is bursty, a burst can arrive faster than new instances can cold-start (especially for a heavier container image or a runtime with slow initialization), causing requests to queue and time out as 503s during the ramp-up window specifically, then recover once instances catch up. Setting a small minInstances above zero removes the cold-start gap for the baseline traffic level.
  4. Check request timeout settings versus how long the container actually takes to respond under load — if downstream calls (a database, another service) are slowing down under the same load, the service might be timing out on its own dependency rather than failing on its own.
  5. Look at CPU allocation. If "CPU is only allocated during request processing" is enabled (the default, cost-saving setting) rather than "CPU always allocated," background work or a burst of concurrent requests within one instance can starve each other for CPU in ways that look like intermittent failures under load specifically, not under light traffic.

The pattern that separates this from a genuine application bug: it's load-dependent and intermittent rather than consistent, which points at a scaling/concurrency configuration mismatch rather than broken logic — the fix is almost always in the Cloud Run service's scaling settings, not the code.

Reference: Cloud Run container runtime contract · About instance autoscaling

The core idea for minimal downtime is to do a bulk historical load once, then keep the target continuously in sync via change data capture (CDC) until the cutover moment, rather than a single big-bang export/import that requires the source to be frozen while it runs.

◈ DIAGRAM
on-prem DB
│
├── (1) Bulk historical export ──► GCS ──► BigQuery load job
│ (one-time, can run over hours/days)
│
└── (2) Datastream (CDC) ──► ongoing change stream ──► BigQuery
(continuous, starts once bulk load begins)
... once (2) has caught the target fully up to the source ...
(3) Cutover: pause writes to on-prem briefly, confirm
BigQuery is fully caught up, redirect readers to BigQuery

Concretely: Datastream is GCP's managed CDC service for exactly this case — it reads the source database's change log (binlog for MySQL, WAL for PostgreSQL, etc.) and streams inserts/updates/deletes into BigQuery continuously, without needing custom polling logic. In parallel, a one-time bulk export (via bq load, Dataflow, or a database-native export to GCS then load into BigQuery) seeds the historical data that predates when the CDC stream started. Once the CDC stream has fully caught up — verified by comparing row counts or checksums between source and target for a sample of tables — the actual cutover becomes a short window: briefly pause writes on the source (or route them read-only), let the last few changes flush through the stream, confirm the two are in sync, and switch consumers over to BigQuery. That downtime window is minutes, not the hours or days a full stop-the-world dump-and-load would take.

The details worth naming explicitly in an interview: CDC requires the source database to have the appropriate logging enabled (binlog format, WAL retention settings) before starting, and schema differences (data types that don't map 1:1 between the source engine and BigQuery) need to be resolved as part of the pipeline design, not discovered during cutover.

Reference: Datastream overview

The structural foundation is separate projects per environment (and often per environment-and-team), organized under folders like dev/, staging/, prod/ — because IAM and Organization Policy both apply at the project or folder level, environment separation has to exist in the resource hierarchy before it can exist in access control.

IAM layer:

  • Grant broad, permissive roles (Editor-equivalent predefined roles for the services engineers actually use) at the dev folder, since velocity matters more than tight control in dev and blast radius is naturally small.
  • Grant progressively narrower roles moving toward staging and prod — in prod, most engineers get read-only or no standing access at all, with elevated access granted through a break-glass process or time-bound IAM Conditions rather than a permanent role binding.
  • CI/CD service accounts get exactly the permissions their pipeline needs in each environment — a deploy-to-prod service account should not also have delete permissions it never uses, and should differ from the deploy-to-dev service account rather than reusing one identity across environments.
  • Avoid granting roles to individual users directly; grant them to Google Groups (dev-engineers@, prod-oncall@) so access changes when someone joins or leaves a team, instead of requiring a manual IAM audit per person.

Organization Policy layer (this is the part IAM alone can't do — org policies constrain what's possible, regardless of who has a role):

  • Restrict which regions resources can be created in per folder, if compliance requires prod data to stay in specific regions.
  • constraints/iam.disableServiceAccountKeyCreation enforced org-wide (or at minimum in staging/prod) to force Workload Identity Federation instead of long-lived keys.
  • constraints/compute.vmExternalIpAccess restricted in prod to prevent accidentally internet-exposed VMs.
  • A domain-restricted sharing constraint so resources can't be shared with identities outside the organization.

The distinction worth stating clearly in an interview: IAM answers "can this identity perform this action," while Organization Policy answers "is this action even allowed to happen in this part of the hierarchy, regardless of who's asking." A prod folder that relies only on IAM to keep engineers from creating public buckets is one misconfigured role binding away from an incident; an org policy constraint blocking public bucket creation in that folder closes the gap even if IAM is misconfigured.

Reference: Organization Policy Service overview · IAM best practices

This set of requirements — encryption in transit, mutual authentication (mTLS), and auditability, all without touching application code — is exactly what a service mesh is for, and on GKE the standard answer is Cloud Service Mesh (Google's managed Istio-based mesh), which handles all three by injecting a sidecar proxy alongside each pod rather than requiring the application itself to implement TLS or auth logic.

Here's why each requirement maps to the mesh rather than something else:

  • Encryption in transit without code changes: the sidecar proxy intercepts all traffic in and out of the pod and can enforce TLS transparently — the application still just makes a plain HTTP call to localhost or the service name, unaware that the actual wire traffic between pods is now encrypted.
  • Mutual authentication: the mesh can issue each workload a certificate-based identity and enforce mTLS between services, meaning both sides of a connection cryptographically verify who they're talking to — this is stronger than a NetworkPolicy, which only restricts which pods can send traffic, not whether the sender is who it claims to be.
  • Auditability: because all traffic flows through the sidecar, the mesh has visibility to log every service-to-service call, which can feed Cloud Logging and Cloud Monitoring for both an audit trail and traffic-shape observability (latency, error rates per service pair) essentially for free.

The trade-off worth naming: a service mesh adds real operational complexity and a small amount of latency per hop (the sidecar proxy sits in the request path), so this is the right tool when the combination of all three requirements is genuinely needed — for a single requirement in isolation there are lighter options (NetworkPolicies alone for traffic restriction, or TLS termination at an internal load balancer for encryption alone), but none of those lighter options gets you mutual authentication and per-hop audit logging together without code changes the way a mesh does.

Reference: Cloud Service Mesh overview

Keep going
All interview prep
Quizzes — test what you know
Modules — hands-on lessons
Glossary — quick term lookups