CCloudSuccessTechworld with Randy
Episodes / Episode 3
Episode 3 · ~55 min · Coming soon

Platform Engineer Mock Interview: Kubernetes, Karpenter, GitOps, Terraform and Databases

The deep platform interview: kubectl apply internals, scheduling and preemption, networking, Karpenter on real EKS, Argo CD drift, Terraform state, multi-tenancy and policy, a live Postgres failover, and designing a platform for 200 engineers.

Coming soon to YouTube

Every question in this interview

The full conversation for each question, including the follow-ups. Alex is a simulated candidate (AI voice); the answers are written to show what a strong answer sounds like.

Platform engineering vs DevOps?
Platform Fundamentals
R

Let's start with the one everyone gets. What is platform engineering, and how is it different from DevOps?

A

DevOps is a culture. Developers and operations owning software together, all the way to production. Platform engineering is one way to make that work at scale.

A

Instead of every team learning Kubernetes, Terraform, CI, secrets and monitoring from scratch, a platform team builds an internal product. Golden paths. A developer runs one command or fills one form, and gets a service with CI, deploys, dashboards and alerts already wired in. Done the right way by default.

R

You said product. Who's the customer?

A

The developers. And that changes everything. You do user research, you have a roadmap, you measure adoption. If nobody uses your platform, it doesn't matter how clever it is.

Mandatory platform?
Platform Fundamentals
R

Should it be mandatory? Force every team onto it?

A

I'd make it optional but obviously better. Paved road, not a cage. If teams are routing around the platform, that's feedback. Something in it is slower or harder than doing it yourself. The exceptions are guardrails, like security policies, which apply to everyone.

How do you measure a platform?
Platform Fundamentals
R

How do you know your platform is actually working? Give me numbers.

A

The four DORA metrics are a good start. Deployment frequency, lead time for changes, change failure rate, and time to restore. Then platform-specific ones. Time for a new engineer to get their first change into production. Time to create a new service. And adoption, what percentage of services are on the golden path.

A

And I'd ask developers directly, every quarter. A short survey. Numbers tell you what, people tell you why.

Does the platform need an SLO?
Platform Fundamentals
R

The platform itself. Does it need an SLO?

A

Yes. The platform is production for every team on it. If the CI system or Argo is down, nobody can ship, and during an incident nobody can ship the fix. So I'd set SLOs on the things developers feel. Pipeline success rate, time from merge to running in production, and cluster API availability. And the platform team goes on call for them, like any other—

R

Hold on. On call for CI? At three a.m.?

A

Not for everything. For the paths that block a production fix. If the deploy pipeline is down during an outage, that's a page. If a nightly report job fails, that's a ticket. Same rule as any service. Page on what hurts users, and here the users are engineers trying to fix production.

6 platform engineers, 80 requests
Platform Fundamentals
R

Good. Now here's a harder one. Your platform team is six people, and you've got eighty product engineers asking for things. Everyone's request is urgent. How do you decide what to build?

A

I'd look for the thing that's blocking the most people the most often. Usually it's not the shiny request. It's something boring, like environments taking two days to create, or every team writing its own CI pipeline. Fix the paved road for the common case first, and give the rare cases a documented way out. And say no out loud, with a reason, so people aren't waiting on a ticket that's never going to happen.

R

Five out of five. Saying no out loud is underrated. A platform team that says yes to everything ends up building nothing well.

What happens on kubectl apply?
Kubernetes Deep Dive
R

Kubernetes. Classic question, but I want the deep version. What actually happens when I run kubectl apply on a deployment?

A

Kubectl reads the YAML, and talks to the API server over HTTPS. The API server authenticates you, checks RBAC, runs admission controllers, validates the object, and stores it in etcd.

R

Show me. I'll turn on verbose logging and you narrate.

A

Okay. First it fetches the OpenAPI schema, so kubectl knows what a deployment looks like. Then a GET for the deployment called hello. Four oh four, it doesn't exist yet. So it sends a POST, and gets two oh one, created. That's client-side apply. Check if it exists, then create or patch.

R

And after the two oh one? Nothing's running yet.

A

Right. At that point it's just a record in etcd. Then the controllers take over. The deployment controller sees a new deployment and creates a ReplicaSet. The ReplicaSet controller creates pods. The scheduler sees pods with no node and picks one. Then the kubelet on that node—

How does the controller find out?
Kubernetes Deep Dive
R

Stop there. How does the deployment controller know? Does the API server call it?

A

No. Nothing calls the controllers. They watch. Each controller has a long-running watch on the API server for the objects it cares about, and keeps a local cache. When something changes, it compares desired state with actual state and acts on the difference. That's the reconcile loop. And it's level-based, not event-based, so if a controller misses an event, the next resync still catches the difference.

How does the scheduler pick a node?
Kubernetes Deep Dive
R

Good. That reconcile loop is going to come up again. What does the scheduler actually do when it picks a node?

A

Two phases. Filtering, then scoring. Filtering removes nodes that can't run the pod. Not enough CPU or memory requested, a taint the pod doesn't tolerate, a node selector or affinity that doesn't match, a volume in a different zone. Then scoring ranks what's left, like spreading pods or packing them, depending on config. Highest score wins, and it writes the node name onto the pod. That's called binding.

R

You said requested. Not used?

A

Requested. The scheduler only looks at requests, never at real usage. A node can be at five percent real CPU and still be full, if the requests add up. That's why bad requests waste so much money.

A node runs out of real memory
Kubernetes Deep Dive
R

So if the scheduler only looks at requests, what happens when real usage runs out on a node? Memory, say.

A

Two different things can happen. If a container goes over its own memory limit, the kernel kills it. That's OOMKilled, exit code one three seven. But if the whole node runs low, the kubelet starts evicting pods before the kernel has to step in. It picks by quality of service. Best effort pods first, the ones with no requests at all. Then burstable pods using more than they asked for. Guaranteed pods last.

R

And how do I make a pod guaranteed?

A

Requests equal to limits, for both CPU and memory, on every container in the pod. That's why critical system pods often look like that.

6 replicas across 3 zones, evenly
Kubernetes Deep Dive
R

Okay. Spreading. I want six replicas of a cart service across three zones, evenly. How?

A

Topology spread constraints. Topology key is the zone label, max skew one, and when unsatisfiable, do not schedule. Max skew one means the difference between the busiest zone and the emptiest zone can't be more than one pod.

R

Let's try it. This cluster has three workers labeled as three zones.

A

Two, two and two. Perfect.

R

Why did you pick do not schedule over schedule anyway? What's the trade-off?

A

Do not schedule is strict. If a zone is down, new pods stay pending rather than piling into the surviving zones. That protects your spread, but it can block a rollout. Schedule anyway is a preference. For most stateless services I'd actually use schedule anyway, plus enough replicas, so losing a zone degrades you but doesn't block you.

R

I like that you changed your mind out loud. That's what a senior does.

Cluster full. Checkout needs room.
Kubernetes Deep Dive
R

Now the cluster is full. Checkout needs to start a pod right now, but batch jobs are using all the CPU. What happens?

A

By default, nothing. The checkout pod sits in pending until something frees up. Unless you've set up priority. Give checkout a high priority class and the batch jobs a low one, and the scheduler is allowed to preempt. It evicts lower-priority pods to make room.

R

Let's see it. Six batch pods, each asking for three CPUs. Low priority.

R

Now a critical checkout pod, also asking for three CPUs.

A

Pending for a moment, with no node. And there it is. The scheduler preempted one of the batch pods. That's the event, preempted by the checkout pod, on the first worker.

R

What's the danger with priority?

A

Two big ones. If everyone marks their service as critical, priority means nothing. So the platform team owns the priority classes, and teams can't create their own. And preemption ignores some of your safety. It tries to respect pod disruption budgets, but it won't wait forever. So a low-priority service can lose pods suddenly. That's acceptable for batch, not for anything stateful.

R

Five for the whole Kubernetes section. The point about owning the priority classes is exactly what I wanted.

CRD vs operator
Kubernetes Deep Dive
R

Quick one before we move on. What's a CRD and what's an operator?

A

A CRD, a custom resource definition, teaches the API server a new kind of object. Like an Argo CD application, or a Postgres cluster. Just the data. An operator is a controller that watches those objects and does the work. The same reconcile loop we talked about, but for your own kind of thing.

R

Every tool we use today is one of those. Hold on to that.

Pod to pod across nodes
Kubernetes Networking
R

Networking. A pod in one node talks to a pod on another node. What makes that work?

A

Kubernetes has one rule. Every pod gets its own IP, and every pod can reach every other pod without NAT. How that happens is the CNI plugin's job. On EKS, the default is the VPC CNI, which gives pods real IP addresses from the VPC subnet. So to the network, a pod looks like any other machine.

R

What's the catch with real VPC IPs?

A

You run out. Every pod uses an IP from the subnet, and each instance type can only hold a limited number of network interfaces and addresses. Small subnets fill up fast. The fixes are bigger subnets, prefix delegation so each interface gets blocks of addresses, or a secondary CIDR range just for pods.

Where does a ClusterIP live?
Kubernetes Networking
R

Now a service. The ClusterIP. Where does that IP actually live?

A

Nowhere. That's the trick. No interface has that IP. Kube-proxy on every node watches services and their endpoints, and writes rules, iptables or IPVS, or eBPF if you use something like Cilium. When a packet goes to the ClusterIP, the node rewrites the destination to one of the healthy pod IPs.

R

So if I ping a ClusterIP?

A

Usually nothing. The rules match the service port and protocol, not ICMP. People see the ping fail and think the service is broken. Test it with curl on the actual port instead.

Default deny does nothing. Why?
Kubernetes Networking
R

Network policies. You apply a default deny policy and nothing changes. Traffic still flows. Why?

A

Because network policies are just objects. Something has to enforce them, and that's the CNI. If your CNI doesn't support network policies, they're ignored silently. On EKS you have to turn on network policy support in the VPC CNI, or use something like Calico or Cilium.

R

And once it's enforced, what usually breaks first?

A

DNS. People deny all egress, then forget to allow traffic to CoreDNS on port fifty-three, UDP and TCP. Suddenly nothing resolves and every service looks down.

Internet → pod on EKS
Kubernetes Networking
R

How does traffic from the internet reach a pod on EKS?

A

Usually through the AWS Load Balancer Controller. An ingress or a gateway creates an Application Load Balancer. In IP mode, the load balancer sends traffic straight to pod IPs, skipping the extra hop through the node. And for TLS, certificates from ACM on the load balancer, or cert-manager inside the cluster.

R

Four. Solid. I'd have liked you to mention that a service with no ready endpoints is the most common reason traffic goes nowhere. But the CNI points were exactly right.

HPA vs VPA vs CA vs Karpenter
Autoscaling & Karpenter
R

Autoscaling. Explain the difference between HPA, VPA, the cluster autoscaler and Karpenter.

A

HPA changes the number of pods. More replicas when CPU or a custom metric goes up. VPA changes the size of pods, their requests. Those two scale the workload. The cluster autoscaler and Karpenter scale the nodes. When pods can't be scheduled, they add machines, and when machines are empty, they remove them.

R

So why would I use Karpenter instead of the cluster autoscaler?

A

The cluster autoscaler works through node groups. On AWS that's auto scaling groups, each with a fixed instance type. It decides which group to grow, and the group launches a node. Karpenter skips the node groups. It looks at the pending pods, works out what they need, and launches the right instance directly from a wide range of types. So it's usually faster, it can pick a bigger or cheaper machine for the actual workload, and you're not managing dozens of node groups.

R

Show me. This is a real EKS cluster with Karpenter installed. Two small nodes from a managed node group.

R

Here's my NodePool. Walk me through it.

A

The NodePool says what Karpenter is allowed to launch. AMD64, on-demand only, from the C, M or R families, with fewer than nine CPUs. The node class points at the EC2 settings, the AMI, subnets and security groups, found by tag. Then limits, CPU eight, so it can never buy more than eight CPUs in total. And the disruption block, consolidate when empty or underutilized, after one minute.

R

Why does that limit matter so much?

A

Because without it, a bug that creates ten thousand pods becomes ten thousand dollars. The limit is your blast radius for cost. Every NodePool should have one.

R

Okay. Apply it, and scale a deployment to five pods. Each one asks for one CPU.

A

All five pending. The existing nodes can't fit them.

A

And Karpenter has already created a NodeClaim. That's Karpenter's record of a machine it's launching.

R

Let's read the controller logs.

A

Found provisionable pods. Computed a new NodeClaim. Created it, launched it, then registered and initialized. Thirty seconds from pending pods to a ready node. And it picked one c6a two x large for all five pods, instead of five small machines. That's bin-packing.

R

Five running, all on the new node.

R

Now the part you mentioned at the start. I'm scaling down to two, then deleting the deployment.

A

That's consolidation. Karpenter noticed the node was empty, tainted it so nothing new lands there, and about thirty seconds later the node and the NodeClaim were gone. The instance is terminated, so you stop paying for it.

R

Back to the two original nodes. No NodeClaims left.

Consolidation is restarting my pods
Autoscaling & Karpenter
R

Okay. Now the follow-ups. Consolidation is moving my pods around all day. A team complains their service restarts too often. What do you do?

A

A few things. First, pod disruption budgets on every service, so Karpenter can only take down a limited number at once. Second, disruption budgets on the NodePool itself, like at most ten percent of nodes at a time, or no disruption during business hours. Third, for something that truly can't be interrupted, like a long batch job, the do-not-disrupt annotation on the pod.

R

And if I set do-not-disrupt on everything?

A

Then you've turned off consolidation and you're paying for empty machines. It's an exception, not a default.

Karpenter + topology spread
Autoscaling & Karpenter
R

Remember the topology spread from earlier? Does Karpenter respect it?

A

Yes. It runs a scheduling simulation that understands spread constraints, affinity and taints. If your pods must spread across three zones, it can launch nodes in three zones. That's one of its advantages over node groups that are tied to one zone. But it can only use the zones your subnets allow, so if the node class only finds subnets in two zones, a three-zone hard spread will leave pods pending.

R

expireAfter. Seven hundred and twenty hours in my NodePool. Why would I want nodes to expire?

A

To keep nodes fresh. After thirty days, Karpenter replaces the node, so you pick up patched AMIs and you never have a server that's been running for two years that nobody dares to touch. It forces your workloads to survive node replacement all the time, which is exactly what you want to find out before an incident, not during one.

Spot interruption with Karpenter
Autoscaling & Karpenter
R

Spot instances. You want to run on spot to save money. What happens when AWS takes a spot node back?

A

AWS gives a two-minute warning. Karpenter listens for those through an SQS queue fed by EventBridge. That's the interruption queue setting in the Helm chart. When the warning comes, it cordons the node, launches a replacement, and drains the pods, inside those two minutes.

A

And I'd only put workloads on spot that can handle it. Stateless, multiple replicas, spread across zones and across many instance types. The more types you allow, the less likely they're all reclaimed at once.

Where does Karpenter itself run?
Autoscaling & Karpenter
R

Should every workload run on Karpenter nodes? What about Karpenter itself?

A

Not on nodes it manages. If Karpenter is running on a node it decides to remove, you can end up with nothing left to launch capacity. That's why this cluster has a small managed node group, the two original nodes, just for Karpenter and other critical system pods. Or you run it on Fargate. Everything else can go on Karpenter nodes.

Pods pending. Karpenter does nothing.
Autoscaling & Karpenter
R

Last one in this round. You deploy, and your pods sit in pending forever. Karpenter isn't launching anything. Where do you look?

A

The pod events first. Then the Karpenter logs, because it will usually say why. The common causes are: the NodePool requirements don't match what the pod asks for, like the pod wants ARM and the pool only allows AMD64. The NodePool limit is already reached. The pod has a taint toleration or node selector no pool satisfies. Or on the AWS side, there's no capacity for those instance types, or the account hit its vCPU quota.

R

The vCPU quota one is real. I hit it setting up this video.

A

Everyone hits it once.

R

Five out of five. You knew the moving parts, the cost risk, and the failure modes.

What is GitOps?
GitOps & Delivery
R

GitOps. What is it, in one sentence, and why would I want it?

A

Git is the source of truth for what should be running, and a controller in the cluster continuously makes the cluster match Git. You get history, review and rollback for free, and nobody needs admin credentials to the cluster to deploy.

R

Push versus pull. Explain.

A

Push is your CI pipeline running kubectl apply against the cluster. That means CI needs cluster credentials, and if someone changes the cluster by hand, nothing notices. Pull is an agent inside the cluster, like Argo CD or Flux, pulling from Git. The credentials stay inside, and it keeps checking for drift.

R

Let's test the drift part. Here's an Argo CD application. Synced and healthy, one replica.

R

Now I'm going to do the thing everyone does at two a.m. Scale it by hand to five.

A

Argo noticed. The app is out of sync, and the diff shows exactly which resource. The deployment. The service is still fine.

R

But it didn't fix it. Why not?

A

Because self-heal is off. Auto-sync only applies new commits from Git. Self-heal is what reverts manual changes. Turn it on and Argo puts it back.

Self-heal: always on?
GitOps & Delivery
R

One replica, synced again. So should self-heal always be on?

A

In production, I'd say yes, with one exception. During an incident, someone might scale up by hand to survive, and self-heal would fight them. So you either make the emergency change through Git, which can be fast if your pipeline is fast, or you have a documented way to pause sync for that app. What you don't want is a cluster that quietly drifts away from Git for months.

HPA vs replicas in Git
GitOps & Delivery
R

Here's the tricky one. HPA is scaling this deployment between three and twenty replicas. And replicas is set to three in Git. What happens?

A

They fight. The HPA scales to ten, Argo sees ten instead of three, and puts it back. So you remove replicas from the manifest when an HPA owns it, or tell Argo to ignore that field.

Secrets in GitOps?
GitOps & Delivery
R

Secrets. Where do they live in GitOps?

A

Never as plain text in Git. Two common patterns. Encrypt them, like Sealed Secrets or SOPS, so only the cluster can decrypt. Or better at scale, keep them in a secret manager like AWS Secrets Manager or Vault, and use the External Secrets Operator to sync them in. Git only holds a reference to the secret, not the value.

Promoting dev → staging → prod
GitOps & Delivery
R

Promotion. How does a change get from dev to staging to production?

A

I prefer one folder per environment, not one branch per environment. Branches drift and merges get scary. The pipeline builds the image once, then promotes the same image tag by opening a pull request that bumps the version in the staging folder, then production. Production needs an approval. And the image is never rebuilt between environments, so what you tested is what you ship.

R

And rollback?

A

Git revert. The controller applies the old version. And for risky changes, I'd add progressive delivery, like Argo Rollouts, so a canary takes a small slice of traffic first and rolls back automatically if error rates go up.

R

Five. The HPA conflict catches a lot of people in real life, not just in interviews.

What is Terraform state?
Infrastructure as Code
R

Terraform. What's state, and why do people get nervous about it?

A

State is Terraform's memory. It maps every resource in your code to the real thing it created, with its ID and attributes. Terraform compares three things: your code, the state, and what's really there. People get nervous because if state is lost or corrupted, Terraform forgets it owns things, and might try to create them again.

R

So where should it live?

A

Remote. An S3 bucket with versioning and encryption, with locking so two people can't apply at the same time. Newer Terraform versions can lock with S3 itself, older setups use a DynamoDB table. Never in Git, because it can contain secrets.

R

Let's watch a real plan. This creates a namespace for the payments team and a resource quota.

A

Two resources added, and both are in state now.

R

Now someone deletes the quota by hand. And I run plan again.

A

Terraform refreshed, saw the quota is gone, and plans to create it again. That's drift detection. The plan is the most important review step in Terraform. You read it before every apply.

R

How would you catch drift without waiting for someone to run plan?

A

Run plan on a schedule in CI, every night, with detailed exit codes. Exit code two means there are changes, so you alert the team. And the long-term fix is cultural. Remove write access in the console, so changes go through code.

One state, 2,000 resources
Infrastructure as Code
R

Your Terraform has grown. One state file with two thousand resources. Plans take twenty minutes. What do you do?

A

Split it. By layer and by blast radius. Networking in one state, clusters in another, each team's app infrastructure in its own. Things that change together live together. Smaller states mean faster plans, and a mistake in one can't destroy everything.

R

And how do the layers talk to each other?

A

Outputs. Read them with a remote state data source, or better, look things up by tag or from a parameter store, so the layers aren't tightly coupled. Then modules for anything repeated, versioned, so a change to the module doesn't hit every team on the same day.

Bring a hand-made bucket into Terraform
Infrastructure as Code
R

Last one. Someone created an S3 bucket by hand two years ago. Now you want Terraform to own it. How?

A

Import. Write the resource block, then an import block with the bucket's ID, and run plan. The goal is a plan that shows no changes. If it wants to change things, adjust the code until it matches reality, then apply. Never just apply an import plan that modifies the resource you don't fully understand yet.

R

Four out of five. Strong. I'd want you to mention policy checks on the plan, like OPA or Sentinel, and running plan on every pull request. That's how a platform team reviews infrastructure at scale.

How does CI authenticate to AWS?
CI/CD & Supply Chain
R

CI. Your pipeline needs to push images and deploy to AWS. How does it authenticate?

A

With OIDC. GitHub Actions or GitLab gets a short-lived token, and AWS trusts it for one specific role, limited to one repository and branch. No access keys stored anywhere. Long-lived keys in CI secrets are one of the most common ways companies get breached.

Compromised dependency. Now what?
CI/CD & Supply Chain
R

Someone pushes a compromised dependency. How does your platform protect you?

A

Layers again. Pin dependencies with lock files, so builds are repeatable. Scan images for known vulnerabilities in the pipeline, and fail on critical ones with a fix available. Generate an SBOM, a software bill of materials, for every image, so when the next big vulnerability lands, you can answer which services are affected in minutes, not weeks.

R

And how do you know the image running in production is the one your pipeline built?

A

Sign it. The pipeline signs every image with something like cosign, using keyless signing tied to the pipeline's identity. Then an admission policy in the cluster only allows signed images from our registry. If someone pushes an image by hand, it can't run. And deploy by digest, not just by tag, because a tag can be moved.

CI takes 40 minutes
CI/CD & Supply Chain
R

Your CI takes forty minutes. Developers are angry. Where do you look?

A

Measure first. Which steps take the time. Usually it's the same few things. No caching of dependencies or Docker layers. Running every test for every change, instead of only what changed. Tests running one after another, instead of in parallel. And slow, shared runners. Fixing caching alone often cuts it in half. And I'd track CI duration as a platform metric, because slow feedback quietly kills productivity.

R

Five. Deploy by digest is a detail a lot of senior people miss.

20 teams, one cluster
Multi-tenancy & Policy
R

Twenty teams share one cluster. How do you stop one team from hurting the others?

A

Layers. Each team gets its own namespace. Resource quotas, so one team can't take all the CPU. Limit ranges, so pods get default requests. RBAC, so a team can only touch its own namespace. Network policies, so traffic is denied by default between namespaces. And admission policies, for rules everyone has to follow.

R

Let's check that quota from the Terraform round. Two pods max. I deploy three replicas.

A

Two out of three ready. The third pod was rejected at admission. Exceeded quota. It's not pending, it was never created. That's an important difference when you debug. You won't find it with get pods. You find it in the ReplicaSet events.

R

Now RBAC. I give the payments developers the built-in edit role, only in their namespace.

A

Can create deployments in team payments, yes. In the guestbook namespace, no. And they can't create resource quotas, even in their own namespace. So they can't raise their own limits. That's what you want.

Stop the latest tag
Multi-tenancy & Policy
R

Policy. A team deploys an image tagged latest. Why is that bad, and how do you stop it?

A

Latest isn't a version. Two nodes can pull two different images under the same tag, and you can't roll back to latest. So you block it with an admission policy. Kyverno or Gatekeeper. Here, a Kyverno policy.

A

Denied at the door, with a message telling the developer exactly what to fix. And with a pinned version, it's allowed.

New policy broke every deploy
Multi-tenancy & Policy
R

You've just rolled out a new policy, and it's blocking deploys across the company. What did you do wrong?

A

I went straight to enforce. Every new policy should start in audit mode. Report violations, don't block. Then share the report with teams, give them time to fix, and only then switch to enforce. And test policies in CI against real manifests, so you know the blast radius before it's live.

R

When is one shared cluster not enough? When do teams get their own cluster?

A

When the isolation needs are hard. Regulated data, like payments under PCI. Very different Kubernetes versions or upgrade schedules. Untrusted code from customers. Or one team so large it's noisy for everyone. Namespaces are soft isolation. Separate clusters are hard isolation, but each one costs you upgrades, monitoring and money.

R

Five. Audit before enforce is the lesson every platform team learns the hard way once.

Postgres on Kubernetes?
Databases on Kubernetes
R

Okay, Alex. Databases. Should you run Postgres on Kubernetes?

A

It depends on who's going to operate it. If you're on AWS and you don't have database expertise, a managed service like RDS or Aurora is usually the right answer. Backups, failover and patching are someone else's job.

A

But it can be done well now, with a good operator. CloudNativePG, for example. You get consistency with everything else on the platform, it can be cheaper, and it works the same on any cloud. The cost is you own the operations, including the scary night when it fails.

R

Let's make it fail. Three-instance Postgres cluster, managed by CloudNativePG.

A

One primary, two replicas. Each with its own persistent volume.

R

How do applications connect? Which pod do they talk to?

A

They shouldn't pick a pod. The operator creates services. R W always points at the primary, for writes. R O points at replicas, for read-only traffic. And R is any instance. When the primary changes, the service changes with it, so the app config never does.

R

I'm writing two orders into the database. Then I'm deleting the primary pod.

A

Instance two is the primary now. The operator promoted a replica. And instance one came back as a replica.

R

And the data?

A

Reading from the new primary. Keyboard and monitor. Both orders survived.

Would we always lose nothing?
Databases on Kubernetes
R

So we lost nothing. Would that always be true?

A

No. By default, replication is asynchronous. The primary confirms a write before the replicas have it. If the primary dies at the wrong moment, the last few transactions can be lost. You can turn on synchronous replication, so a write waits for at least one replica. Then you lose nothing, but every write is a bit slower, and if the replicas are down, writes can block.

R

That's RPO. Explain RPO and RTO properly.

A

RPO, recovery point objective, is how much data you can afford to lose, measured in time. RTO, recovery time objective, is how long you can be down. Synchronous replication gets your RPO close to zero for a single failure. Fast automatic failover gets your RTO down to seconds. And the business picks the targets, because each step costs money.

Why replication is not a backup
Databases on Kubernetes
R

Replication isn't a backup. Why not?

A

Because replication copies mistakes too. Someone runs delete without a where clause, and it's deleted on every replica within a second. A backup is a copy from the past. So you need base backups plus the write-ahead log archived to object storage, like S3. That gives you point-in-time recovery. Restore to two forty-nine, one minute before the bad delete.

R

And when did you last test a restore?

A

That's the right question. A backup you've never restored is just a hope. I'd automate a restore every week into a separate cluster, check the data, and alert if it fails.

Postgres major upgrade
Databases on Kubernetes
R

Major version upgrade. Postgres sixteen to seventeen. How?

A

Carefully, and rehearsed. The safest pattern is a new cluster on the new version, kept in sync with logical replication, then a short cut-over. Point the read-write service at the new primary, and keep the old cluster around for a quick rollback. An in-place upgrade can be faster, but it's harder to undo. Either way, test it against a restored copy of production first, because extensions and query plans can change between versions.

DB pod stuck pending after a zone failure
Databases on Kubernetes
R

Stateful sets and zones. Your Postgres pod's node dies in zone A. The pod reschedules to zone B. And it's stuck pending. Why?

A

Because the volume is in zone A. An EBS volume only lives in one zone, so the pod can only attach it in that zone. That's why you don't rely on moving a database volume between zones. You run replicas in each zone and fail over to one that already has its data. And the storage class should use wait for first consumer, so volumes get created in the zone where the pod is actually scheduled.

200 pods × 20 connections
Databases on Kubernetes
R

Connections. Your app scales to two hundred pods, each with a pool of twenty connections. Postgres falls over. Why?

A

That's four thousand connections. Each Postgres connection is a process with its own memory. Most databases are tuned for a few hundred. You need a connection pooler in between, like PgBouncer, in transaction mode, so thousands of app connections share a small number of real database connections. CloudNativePG can run one for you, called a pooler.

Rename a column, zero downtime
Databases on Kubernetes
R

Last database question. You need to rename a column on a table that's being written to all day. No downtime. How?

A

Never in one step. Expand and contract. First, add the new column. Then deploy code that writes to both columns. Backfill the old rows in small batches. Then switch reads to the new column. And only when nothing uses the old column anymore, drop it. Each step is safe to roll back. A single rename would break every running pod that still uses the old name.

R

Five out of five. And you said you were worried about databases.

Deploy stuck on "progressing"
Troubleshooting Scenarios
R

Troubleshooting. I'll describe what I see, you tell me what you'd do. No terminal this time, just your thinking. Scenario one. A team says their deploy has been stuck for twenty minutes. Argo says progressing. What do you check?

A

Progressing usually means the new pods never became ready. So I'd look at the rollout status, then the new ReplicaSet's pods. Three common answers. The pods are pending, so it's capacity or scheduling, and the events will say why. The pods are running but not ready, so the readiness probe is failing, often a wrong port or path. Or they're crash looping, so it's the logs from the previous container.

R

They're running, not ready. Readiness probe says connection refused.

A

Then the app isn't listening where the probe is looking. Either the port changed in the new version, or the app listens on localhost only, so nothing outside the container can reach it. I'd exec in and check what it's actually listening on. And the fix goes in Git, not in the cluster, or Argo will just revert it.

Every new image pull is failing
Troubleshooting Scenarios
R

Scenario two. Monday morning. Every pod in the cluster that pulls a new image is failing. Existing pods are fine.

A

Something shared changed. The registry, or the path to it. I'd look at one pod's events for the exact error. If it's unauthorized, the pull credentials expired or a token rotated. On EKS pulling from ECR, it's the node role's permissions. If it's a timeout, it's network. A NAT gateway, a VPC endpoint, or a firewall rule. And if it's too many requests, it's Docker Hub rate limits, which is why a platform should run a pull-through cache.

R

It's too many requests.

A

Then short term, the pods already running are fine, so I'd pause non-urgent deploys. Then point images at a mirror or pull-through cache, like ECR pull-through cache for Docker Hub. And afterwards, a policy that images must come from our own registry, so this can't happen again.

The API server is slow
Troubleshooting Scenarios
R

Scenario three. The API server is slow. Kubectl takes ten seconds for everything. Nothing else looks wrong.

A

Something is hammering it. The usual suspect is a controller or an operator listing everything, over and over, without a cache. Or a script in a loop. I'd look at API server metrics by user agent and by resource, to find who's making the most requests. And on EKS, the control plane logs and audit logs show it. Then there's API priority and fairness, which can stop one noisy client from starving everyone else.

R

And if it's not requests, but etcd?

A

Then it's usually too many objects or very large ones. Thousands of old ReplicaSets, completed jobs that never get cleaned up, or huge config maps. Set revision history limits, set a time to live on finished jobs, and keep big data out of etcd.

R

Five. Every answer started with finding the evidence before guessing. That's the habit.

Design a platform for 200 engineers
Design an Internal Platform
R

Design round. Two hundred engineers, forty teams, one hundred and fifty microservices, all on AWS. Leadership wants a platform. Design it. Start wherever you want.

A

I'd start with questions, not boxes. What hurts today? How long does it take to ship a change? How many incidents come from deploys? Which teams are happy and which are stuck? The platform should solve the top three problems, not everything.

R

Assume it's the usual. Slow environments, every team has its own pipeline, and nobody knows who owns what.

A

Okay. Then the layers. At the bottom, infrastructure. Terraform modules, one account per environment, EKS clusters per region and per environment, with Karpenter for nodes.

A

On top, the delivery layer. One standard CI template, image build and scan, and Argo CD for deploys, with promotion through pull requests.

A

Then shared services built in. Logging, metrics and tracing with OpenTelemetry. Secrets through External Secrets. Policy with Kyverno. Certificates and DNS automated.

A

And at the top, the developer portal. Something like Backstage, with a service catalog so every service has an owner, and templates. A team clicks new service, and gets a repo, a pipeline, a namespace, dashboards and alerts in about ten minutes.

Why start with Backstage?
Design an Internal Platform
R

Wait. Backstage. That's a big project. Why would you start there?

A

I wouldn't. Good catch. The portal is the last thing, not the first. It's just a front door. I'd start with the golden path itself. One working template for the most common service type, used by two friendly teams. Prove it saves time. The portal goes on top once there's something worth putting in it.

How many clusters?
Design an Internal Platform
R

Better. How many clusters?

A

As few as you can get away with. Production and non-production separated, at minimum. Per region if you serve multiple regions. Split further only for hard isolation, like the PCI case. Every extra cluster is another thing to upgrade three times a year.

A whole region goes down
Design an Internal Platform
R

A whole AWS region goes down. What happens to your platform?

A

That depends on what the business paid for, and I'd make them choose explicitly. The cheap option is backup and restore. Infrastructure as code and data backups copied to a second region, so we can rebuild in hours. The middle option is a pilot light. A small cluster already running in the second region, database replicas there, scale it up when needed. The expensive one is active-active, both regions serving traffic, which is hard mainly because of the data.

R

And the platform part specifically?

A

Everything to rebuild has to live outside the failed region. Terraform state, container images, Git, secrets. If your image registry only exists in the region that's down, you can't start anything anywhere. I'd replicate the registry and the secrets, and practice the failover once a quarter. A disaster plan you've never run is fiction.

Upgrade K8s without an outage
Design an Internal Platform
R

Upgrades. How do you upgrade Kubernetes across all of them without an outage?

A

Read the changelog for removed APIs, and scan the manifests with a tool like Pluto before you start. Upgrade non-production first and let it soak. Upgrade the control plane, then the add-ons, then the nodes. With Karpenter, you bump the AMI version in the node class, Karpenter sees the nodes have drifted, and replaces them gradually, respecting the disruption budgets and the pod disruption budgets. And never be more than one version behind, because skipping versions isn't allowed and the backlog only grows.

K8s bill doubled
Design an Internal Platform
R

Cost. The CFO says the Kubernetes bill doubled this year. First steps?

A

Make it visible first. Cost per team and per namespace, using labels and a tool like Kubecost or OpenCost. Then the usual wins. Right-size requests, because most are set way too high. Consolidation with Karpenter. Spot for stateless work. Savings plans for the steady baseline. And turn off non-production at night and on weekends. When teams see their own bill, behaviour changes fast.

R

Four. Starting with questions was great. Starting the build with Backstage would have cost you. You recovered well when I pushed, and that matters.

Rapid fire
Rapid Fire
R

Rapid fire. Short answers. Liveness versus readiness probe?

A

Liveness failing restarts the container. Readiness failing removes it from the service. Never point liveness at a database check, or a database blip restarts everything.

R

Requests versus limits on CPU?

A

Requests are for scheduling and fair share. A CPU limit throttles, even when the node is idle. Many teams set CPU requests, memory limits, and no CPU limit.

R

A pod is stuck in terminating. Why?

A

Usually a finalizer that never finished, or a node that's gone so the kubelet can't confirm. Check finalizers before force deleting.

R

Ingress versus Gateway API?

A

Gateway API is the newer standard. Split roles, so the platform owns the gateway and teams own their routes, and it supports more than HTTP.

R

Service mesh. Do you need one?

A

Not by default. If you need mutual TLS everywhere, fine-grained traffic shifting or deep traffic telemetry, maybe. Otherwise it's a lot of moving parts for problems you don't have yet.

R

How do pods get AWS permissions?

A

EKS Pod Identity or IRSA. A role per service account. Never node-wide credentials. That's how Karpenter itself gets its permissions in this cluster.

R

Helm or Kustomize?

A

Helm for packaging things you install from others. Kustomize for patching your own manifests per environment. Plenty of teams use both.

R

Last one. What's the most important thing a platform team ships?

A

Documentation that's actually up to date. A golden path nobody can find doesn't exist.

R

I wasn't expecting that one. No score for rapid fire, that was just for fun. But that's a good answer.

A migration that did not go to plan
Behavioural
R

Couple of behavioural questions to finish. Tell me about a platform migration you led that didn't go to plan.

A

We moved forty services from an old deploy system to Argo CD. I planned it team by team, and the first five went perfectly. Then we hit the team with the most traffic, and their deploys suddenly took twice as long. Turned out their old system skipped health checks, and Argo was correctly waiting for readiness probes that took ninety seconds.

A

The mistake was mine. I'd tested with easy teams. So we paused, fixed their probes together, and I changed the plan. For each team, a dry run in staging first, plus a checklist of what's different. The rest of the migration took three weeks longer than planned, but nobody had an outage.

R

What would you do differently?

A

Pick one hard team early, not last. The easy teams make you feel fast, but the hard team is where you learn what the migration really involves.

A developer refuses the platform
Behavioural
R

A senior developer refuses to use the platform. Says it's slower than what they built themselves. What do you do?

A

I'd sit with them and watch them deploy, their way and our way. Sometimes they're right, and that's free product research. If their way really is faster, I want to know why and copy it. If ours is better for the company, like security or cost, I explain the trade-off honestly. And I'd never escalate first. Forcing people onto a platform makes enemies. Making it better makes fans.

R

Five. Picking a hard team early is a lesson most people learn too late.