A ReplicaSet has a replica count, a label selector and a Pod template whose labels match it. Its controller counts matching Pods and creates or deletes Pods until the count is right:
kubectl apply -f - >/dev/null <<'EOF'
apiVersion: apps/v1
kind: ReplicaSet
metadata: { name: web }
spec:
replicas: 2
selector: { matchLabels: { app: booknest, tier: web } }
template:
metadata: { labels: { app: booknest, tier: web } }
spec: { containers: [{ name: nginx, image: localhost:33500/booknest-web:1.3 }] }
EOF
kubectl wait --for=condition=Ready pod -l tier=web >/dev/null
kubectl delete $(kubectl get pod -l tier=web -o name | head -1) --wait=false >/dev/null
sleep 3 && kubectl get pods -l tier=web && kubectl delete replicaset webOutput
NAME READY STATUS RESTARTS AGE web-cscc9 1/1 Running 0 4s web-fz26x 1/1 Running 0 3s replicaset.apps "web" deleted from booknest namespace
The replacement has a new name and IP, which is why objects find Pods by label. Each Pod names its owner in metadata.ownerReferences, so deleting the ReplicaSet deleted its Pods. Editing a ReplicaSet's template never changes existing Pods, so you rarely write one yourself.