R14 · Week 14 Recall & Drill
Week 14 revision: declarative reconciliation over imperative starts, config and secrets injection, cloud identity and network isolation, Terraform state as a recorded resource mapping, and CAP versus PACELC.
Original publication date unrecordedUpdated
Lesson preparation & details
Level: intermediate
By the end, you should be able to
- Explain pod, deployment, and service in under two minutes, and distinguish a readiness probe from a liveness probe by what each one causes.
- Contrast environment variables and projected configuration files, including eventual updates, application reload, and subPath limitations.
- Write a default-deny network policy and explain what an empty policy targeting a set of workloads does.
- Explain workload-attached identity and how it avoids application-managed long-lived credentials while retaining token and authorization responsibilities.
- Describe what state is in an infrastructure-as-code tool, how backup/import recovers its mapping, and why exposing sensitive state is a security incident.
- State CAP precisely as a decision made during a partition, and say what PACELC adds for the non-partitioned case.
Bring with you
Sources checked: . A source check does not change the publication or update date.
Editorial review: · What review means
In this article · 18 sections
Assessment scope
Attempt the recall prompts unaided, then work through the implementations and answer keys. The local programs use explicit fixtures and independent checks; product behavior is tied to named primary sources, not inferred from the toy models. Service deployment and production readiness are separate from this conceptual review. Timing budgets are adjustable practice suggestions.
Sessions covered
- S066 — Kubernetes I — Pods, Deployments, Services
- S067 — Kubernetes II — ConfigMaps, Secrets, HPA, Network Policies
- S068 — Azure Cloud — Identity, Storage, Networking, App Service
- S069 — Infrastructure as Code — Terraform / Bicep Basics
- S070 — CAP & PACELC — the Actual Trade-Offs
By the end of this revision you can
- Explain pod, deployment, and service in under two minutes, and distinguish a readiness probe from a liveness probe by what each one causes.
- Inject configuration two ways and say why one can update in place while the other requires a restart.
- Write a default-deny network policy and explain what an empty policy targeting a set of workloads does.
- Explain workload-attached identity and how it avoids application-managed long-lived credentials while retaining token and authorization responsibilities.
- Describe what state is in an infrastructure-as-code tool, how backup/import recovers its mapping, and why exposing sensitive state is a security incident.
- State CAP precisely as a decision made during a partition, and say what PACELC adds for the non-partitioned case.
90-min structure
| Block | Minutes | Practice |
|---|---|---|
| Warm-up recall | 5 | Summarize the five subjects without notes. |
| Blank-page reconstruction | 30 | Answer the fifteen prompts, then check the subject solutions. |
| Hands-on drill | 30 | Trace and run the bounded implementations; explain their limits. |
| Quiz + misconception | 15 | Defend the corrected answers with a failure case. |
| Gap analysis + preview | 10 | Record a discriminating retest and connect the next subjects. |
The 90-minute budget is pacing guidance, not a claim that full execution and every extension fit every learner's session.
Blank-page reconstruction · 30 min
S066 · Kubernetes I
- Define pod, deployment, and service in one sentence each, and say why you rarely create the first directly.
- Distinguish a readiness probe from a liveness probe by what each causes when it fails.
- Explain the two rolling-update parameters that control surge and unavailability.
Answer key — controllers maintain intent. A Pod is Kubernetes' smallest deployable unit, containing co-scheduled containers that share networking and can share volumes. A Deployment manages ReplicaSets and rollout intent; a Service supplies a stable access abstraction over selected endpoints. A bare Pod has no Deployment maintaining its replica count, so deletion does not imply a replacement. Reconciliation repeatedly compares observed state with intent; requests can fail or observations can be stale, so convergence requires retries and conditions, not one successful command.
Readiness failure marks the Pod unready for normal Service traffic without itself restarting the container. Liveness failure at its configured threshold causes the kubelet to restart the container according to restart behavior; a startup probe can gate both while initialization completes. These are not interchangeable database health checks. During a Deployment rolling update, maxSurge limits extra Pods and maxUnavailable bounds unavailable desired capacity; percentages round surge up and unavailability down. For desired 4, surge 25% and unavailability 25% allow one extra and one unavailable; this is not a guarantee of request success or strict total resource usage during termination.
Unreachable Service drill: inspect the Service selector, Pod labels, EndpointSlices and endpoint readiness; an empty eligible set can mean selector mismatch, no Pods or readiness failure, not one certain cause. Then check Service port/targetPort, the application's listening address, NetworkPolicy/CNI, and routing/DNS. A mismatch need not be rejected by API validation because each object is independently valid.
S067 · Kubernetes II
- Say when to use plain configuration versus a secret, and give the two injection methods.
- Name the two things the autoscaler needs that people forget to provide.
- Say what an empty policy targeting a set of workloads does.
Answer key — updates and isolation have direction. ConfigMaps carry nonsensitive configuration; Secrets are for sensitive data but their API serialization's base64 encoding is not encryption. Restrict read access, configure appropriate at-rest protection, and prevent logs/images from copying values. Environment injection is resolved at container startup and is not automatically refreshed from a changed ConfigMap/Secret. Projected volume updates propagate eventually, not instantaneously; the application must reopen/reload the file, and a subPath mount does not receive those automatic updates. Immutable objects require replacement. Neither approach guarantees a successful credential rotation unless the receiving service and application support it.
For CPU-utilization HPA, supply a working resource-metrics API and CPU resource requests for the relevant containers; utilization is relative to requests. The simplified recommendation is ceiling(current replicas × current/target utilization), subject to tolerances, stabilization, missing metrics and configured bounds. At 4 replicas, 90% current and 60% target, the raw recommendation is 6. This is not an instantaneous six-Pod capacity guarantee. Custom/external metrics need their adapter instead; memory or CPU requests are not a universal denominator for every HPA type.
A namespaced NetworkPolicy selects Pods and isolates specified traffic directions. Empty ingress rules on an ingress policy deny ingress to selected Pods unless another selecting policy allows it; an empty podSelector selects all Pods in the namespace, not no Pods. Policies are additive, so adding a default deny does not override an existing allow. Enforcement requires a supporting network plugin. The worked manifest below isolates both directions; DNS and intended dependencies require explicit allows before using this pattern in a real deployment.
S068 · Cloud Platform
- Name the four pillars with a flagship service in each.
- Explain the difference between a standalone service principal and an identity attached to a running workload.
- Explain a private endpoint in one sentence.
Answer key — name a boundary for each service. One useful four-part Azure inventory is compute (App Service or Virtual Machines), storage (Blob Storage), identity (Microsoft Entra ID/managed identities), and networking (Virtual Network/Private Link). This is a teaching grouping, not a claim that databases or observability do not exist. A conventional app registration/service principal can authenticate using an app-managed certificate/secret or supported federation. A managed identity also has a service-principal representation, but Azure manages its credential lifecycle; the workload obtains scoped access tokens without shipping a long-lived password in configuration. System-assigned identity lifecycle is tied to one resource; user-assigned identities are independent and can be attached to supported resources.
No identity grants access by itself. Assign least-privilege data-plane roles, choose the intended identity when several are available, validate token audience at the receiver, and protect the workload that can request tokens. “No credential anywhere” is false: tokens and platform-managed credentials still exist. A private endpoint exposes a supported service through a private IP in a VNet; DNS, routes and authorization must still be correct, and disabling public access is a separate service setting. It does not mean the service has become your VM.
Availability zones are separate physical locations within a region; a supported zone-redundant design helps with a zone failure, not every regional/shared-control-plane, bad rollout, identity or quota incident. Multi-region adds replication, failover, capacity and dependency decisions rather than automatically completing disaster recovery.
S069 · Infrastructure as Code
- Say in one sentence what the planning step does.
- Explain what happens when you rename a resource block, and how to avoid the destructive outcome.
- Contrast count and for_each and explain their instance-address stability.
Answer key — state is a binding, not the world. Terraform planning compares configuration, prior state and (normally) refreshed remote observations using provider schemas to propose actions. State binds configuration addresses to provider object IDs and stores attributes/dependencies; it can be stale and is not the authoritative truth about every live attribute. Renaming an address without migration can yield destroy/create; a supported moved block or carefully reviewed state move preserves the binding. Losing state calls for trusted backup recovery or supported import with correct configuration/ownership verification, not blind apply. Some mappings can be reconstructed/imported; not every provider attribute or historical binding is automatically discoverable.
count uses integer instance addresses: deleting an interior element of a list used by indexed configuration shifts later identities. for_each uses stable keys: deleting east does not rename west, but changing a key still changes its address. Neither is universally safer; use count for genuinely interchangeable instances and stable keys for individually owned objects. Provider replacement rules are attribute/resource/version specific; a database SKU is not universally immutable. The toy Step 2 uses fictional replacement rules explicitly.
State can contain secret attributes in plaintext in local storage; sensitive mainly controls display, not encryption. Remote backend protection, access control, locking and versioned recovery are separate responsibilities. Newer ephemeral/write-only features can omit some values, so “every credential always appears” is also false. Do not commit state; if sensitive state was exposed, restrict access, assess disclosure and rotate affected credentials rather than merely deleting the current file.
S070 · CAP & PACELC
- State CAP in one precise sentence.
- Say why choosing consistency and availability while ignoring partitions is not a real option.
- Explain what PACELC adds that CAP omits.
Answer key — define the object and the promise. For a read/write register in the asynchronous model, no implementation can guarantee both linearizable (atomic) consistency and availability in every fair execution when messages between groups of nodes can be lost indefinitely. Linearizability means operations can be arranged in one legal sequential history respecting real-time precedence: if write 1 finishes before a read starts, that read cannot return an older value unless an intervening write justifies it. CAP availability means every request to a nonfailed node eventually completes its specified operation; it is not a monthly uptime percentage or permission to count “unavailable” errors as successful reads. It supplies no finite response-time bound.
During a partition, a node unable to learn whether a remote write completed cannot both always answer its read and promise the required latest value. The Step 3 schedule makes this concrete: A/B acknowledge 1, then isolated C either cannot complete the read or returns stale 0. Choosing a reliable network changes risk, not this impossibility under the allowed failure model. If a system assumes partitions never happen, its consistency/availability promise is conditional on that assumption; it has not solved the partition case. CAP does not say every operation must fail everywhere, nor that every available data type inevitably loses updates.
PACELC asks: if Partition, how does the system trade Availability against Consistency; Else, how does it trade Latency against Consistency? Even with a healthy network, coordinating a replicated write before acknowledgement or validating a read can require remote communication; serving an asynchronous replica locally can avoid that wait but may return an older value. State the operation, replica placement and protocol before predicting latency: this is a design lens, not a universal millisecond penalty or a claim that partitions are rare in this workload. A vendor's “strong” label is insufficient—check whether the documented guarantee is for one key/replica group, a transaction, or a global real-time order. Linearizable reads do not by themselves prove serializable multi-key transactions.
Hands-on drill · 30 min
The three executable models below are pure in-memory objects. They perform no Kubernetes or cloud mutations. Work from intent → differences → explicit preconditions → observed result, then explain which production guarantees the model deliberately lacks.
Step 1 — write a reconciler (10 min)
Keep the original replica-count/image objective, but expose actions before applying them. This simplified reconciler replaces every wrong-image instance before creating replacements: it is not a safe rolling-update algorithm.
import copy
def reconcile(observed, replicas, image):
if type(replicas) is not int or replicas < 0:
raise ValueError('nonnegative integer replicas required')
keep = sorted(k for k,v in observed.items() if v == image)[:replicas]
actions = [('delete', k, observed[k]) for k in sorted(set(observed)-set(keep))]
occupied = set(observed)
n = 0
for _ in range(replicas-len(keep)):
while f'pod-{n}' in occupied:
n += 1
name = f'pod-{n}'; occupied.add(name)
actions.append(('create', name, image))
return actions
def apply_model(observed, actions):
result = copy.deepcopy(observed)
for action,name,image in actions:
if action == 'delete':
if result.get(name) != image:
raise ValueError('stale deletion precondition')
del result[name]
elif action == 'create':
if name in result:
raise ValueError('name already occupied')
result[name] = image
else:
raise ValueError('unknown action')
return result
initial = {'pod-0':'v1', 'pod-1':'v2'}
actions = reconcile(initial, 3, 'v2')
after = apply_model(initial, actions)
assert initial == {'pod-0':'v1', 'pod-1':'v2'}
assert len(after) == 3 and set(after.values()) == {'v2'}
assert reconcile(after, 3, 'v2') == []
del after[sorted(after)[0]] # simulated failure, not a real Pod
healed = apply_model(after, reconcile(after, 3, 'v2'))
assert len(healed) == 3 and reconcile(healed, 3, 'v2') == []
assert apply_model(healed, reconcile(healed, 0, 'v2')) == {}
print('actions', actions, 'healed', healed)Why it converges: after deleting undesired/excess instances there are at most replicas matching survivors. It creates exactly the deficit, so the output has the desired count and image and the next diff is empty. This proof assumes the finite snapshot and actions are applied without outside mutation. Production controllers need resource-version/precondition checks, retryable idempotent operations, ownership, readiness and rollout budgets; the API is not an atomic dictionary.
Configuration and policy exercise: identify which of these sample patterns reloads without restarting and what traffic remains allowed. The value is intentionally nonsensitive.
apiVersion: v1
kind: Pod
metadata:
name: config-exercise
spec:
containers:
- name: app
image: example.invalid/learning-app:v1
env:
- name: MODE
valueFrom:
configMapKeyRef:
name: app-settings
key: mode
volumeMounts:
- name: settings
mountPath: /etc/app-settings
readOnly: true
volumes:
- name: settings
configMap:
name: app-settings
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
ingress: []
egress: []Answer: MODE is not refreshed automatically; /etc/app-settings/mode can eventually update, but only code that re-reads/reloads notices. The image name is an explicit non-runnable placeholder, not a deployment instruction. The policy selects namespace Pods and supplies no ingress/egress allows; with no other allows, ordinary selected-Pod traffic is denied by an enforcing plugin, including DNS egress. Host/node and implementation exceptions need the real policy contract. An added ingress allow implicitly permits reply traffic on that allowed connection, but does not grant a new application-initiated outbound connection. For a new Pod-to-Pod connection, both source egress and destination ingress must allow it; analyze each direction and all selecting policies. Do not apply this example blindly.
Step 2 — plan before apply (10 min)
DESIRED = {
'storage.main': {'type':'bucket', 'copies':3},
'vault.new': {'type':'vault', 'protected':True},
'db.main': {'type':'toydb', 'layout':'v2'},
}
STATE = {
'storage.main': {'id':'object-1', 'type':'bucket', 'copies':1},
'vault.old': {'id':'object-2', 'type':'vault', 'protected':True},
'db.main': {'id':'object-3', 'type':'toydb', 'layout':'v1'},
}
# Fictional provider rule, not a statement about Azure SQL SKUs.
IMMUTABLE = {'toydb': {'layout'}}
def plan(desired, state):
actions = []
for address in sorted(set(desired) | set(state)):
if address not in desired:
actions.append(('destroy',address,{})); continue
if address not in state:
actions.append(('create',address,desired[address].copy())); continue
want,have = desired[address],state[address]
attributes = (set(want)|set(have)) - {'id'}
changes = {k:(have.get(k),want.get(k)) for k in sorted(attributes) if have.get(k) != want.get(k)}
if changes:
replacement = 'type' in changes or bool(set(changes)&IMMUTABLE.get(want['type'],set()))
actions.append(('replace' if replacement else 'update',address,changes))
return actions
before_state = copy.deepcopy(STATE)
proposal = plan(DESIRED, STATE)
assert [a[0] for a in proposal] == ['replace','update','create','destroy']
assert STATE == before_state
moved = copy.deepcopy(STATE)
moved['vault.new'] = moved.pop('vault.old')
assert [a[0] for a in plan(DESIRED,moved)] == ['replace','update']
print('plan',proposal,'after address move',plan(DESIRED,moved))Answer: the original unmigrated rename has one create and one destroy; preserving the object-2 binding removes both. The toydb.layout change still requests replacement, and storage copies still request update. A real plan also considers provider-computed/default fields, dependencies, lifecycle policy and refreshed observations; this dictionary comparison cannot decide whether a cloud resource is mutable. Read destructive actions, data backups and replacement ordering before an authorized apply; a saved plan can become stale or contain secrets.
Step 3 — a partition, two policies (10 min)
Use three replicas A/B/C rather than pretending one side of a two-node split is a majority. A and B can communicate; C cannot. This schedule stipulates a valid majority-leader/commit protocol; it does not implement one.
def partition_schedule(consistent):
values = {'A':0,'B':0,'C':0}
acknowledged_write = 1
values['A'] = values['B'] = acknowledged_write
# C receives a read after that acknowledged write completed.
c_response = None if consistent else values['C']
return values, c_response
values, response = partition_schedule(True)
assert values == {'A':1,'B':1,'C':0} and response is None
values, response = partition_schedule(False)
assert response == 0 # responds, but violates this register's real-time order
# Independent available writes can conflict; a LWW rule discards one value.
concurrent = [(2,'A','alice-edit'), (2,'C','carol-edit')]
assert max(concurrent) == (2,'C','carol-edit')
print('C read: consistent policy refuses; available policy returns stale 0')Answer: under the first policy C refuses/waits instead of returning a stale read; this sacrifices CAP availability there. Under the second it returns 0 even though write 1 already completed elsewhere, disproving linearizability for the register. The final lexical tie-break is a deliberately chosen conflict policy, not a clock or consensus algorithm; both original edits should be retained if the application needs reconciliation. An available grow-only set can merge additions by union instead of losing one—operation semantics determine that choice. After a partition heals, repair/replay and fencing are still required; simply copying an arbitrary “newest” counter is not a safe database failover.
Recall quiz — corrected decisions
- Readiness versus liveness? Stop routing versus restart after configured failures; choose restart only where it can repair the fault.
- Rotated Secret still old? Environment injection needs container restart; projected files need propagation and application reload, and subPath is an exception.
- Lost Terraform state? Recover trusted backup or import verified ownership/bindings; it is not an excuse to recreate everything, and not literally unrecoverable.
- PACELC beyond CAP? It adds normal-operation latency/consistency analysis; it does not prove partitions are rare in your service.
- Healthy Pods, unreachable Service? Inspect selector/EndpointSlices/readiness, ports and listening address, then the allowed network paths and DNS. Empty endpoints localize the stage, not the single cause.
Answer constraints and corrections
These models are not cloud deployment evidence. A Kubernetes Secret environment value is captured at process start; mounted updates are not instantaneous and subPath has additional restrictions. Infrastructure state is a mapping requiring recovery/import procedures, not a permission to recreate production resources. CAP concerns defined operations under partitions, not a generic choose-any-two slogan.
Gap analysis + next week preview · 10 min
- Does a second reconciliation compute no actions, including after scaling to zero and after a simulated deletion?
- Can you identify the planned destroy/create caused by an unmigrated address and distinguish fictional provider replacement rules from real ones?
- Can you define the unavailable operation on the isolated replica and give a mergeable available operation without claiming every partition loses writes?
Mark each answer unaided, hinted or not yet; select a failed assumption and use a new counterexample for the retest rather than memorizing one fixture. Keep the input contract, boundary case and independent expected answer next to any execution result.
Next week: S071–S075 cover replication, consistency models, consensus, sharding and queues. CAP/PACELC frames operations under failure; it does not choose their deduplication or conflict policy for you.
Corrective source notes
- Deployments | Kubernetes — Deployment maxUnavailable percentages round down, and maxSurge percentages round up; these bounds are not application success guarantees. Retrieved 2026-10-07T18:39:35.431034+00:00; exact passage reviewed 2026-10-07.
- Liveness, Readiness, and Startup Probes | Kubernetes — Startup, liveness and readiness have different gating/restart/traffic-readiness purposes. Retrieved 2026-10-07; exact passage reviewed 2026-10-07.
- ConfigMaps | Kubernetes — Environment-injected ConfigMaps require restart for automatic refresh; subPath mounts do not receive projected updates. Retrieved 2026-10-07T18:39:35.739810+00:00; exact passage reviewed 2026-10-07.
- Secrets | Kubernetes — Secret protection needs explicit at-rest and least-privilege controls; encoding alone is not confidentiality. Retrieved 2026-10-07T18:39:35.332431+00:00; exact passage reviewed 2026-10-07.
- Horizontal Pod Autoscaling | Kubernetes — Resource-utilization HPA depends on resource requests; current/desired utilization is a ratio relative to those requests. Resource-metric HPA needs a metrics API, commonly supplied by separately installed Metrics Server. Retrieved 2026-10-07T18:39:35.797338+00:00; exact passage reviewed 2026-10-07.
- Network Policies | Kubernetes — NetworkPolicies select isolation per direction and combine allowed connections additively; default deny does not override another allow. Retrieved 2026-10-07T18:39:35.543585+00:00; exact passage reviewed 2026-10-07.
- Managed identities for Azure resources - Managed identities for Azure resources | Microsoft Learn — Managed identities remove developer management of long-lived credentials while applications still obtain tokens. A system-assigned identity has a service principal tied to the resource lifecycle; user-assigned identity lifecycle is separate. Retrieved 2026-10-07T18:39:34.296842+00:00; exact passage reviewed 2026-10-07.
- What is a private endpoint? - Azure Private Link | Microsoft Learn — Private endpoints use a private VNet IP to connect to a Private Link service, not replace authentication/authorization. Retrieved 2026-10-07; exact passage reviewed 2026-10-07.
- Manage sensitive data in your configuration | Terraform | HashiCorp Developer — Ephemeral and write-only mechanisms are version/provider dependent; not every sensitive value must invariably persist in state. Retrieved 2026-10-07T18:39:35.816171+00:00; exact passage reviewed 2026-10-07.
- Refactor modules | Terraform | HashiCorp Developer — Terraform moved blocks preserve resource-address bindings rather than triggering an unmigrated destroy/create. Retrieved 2026-10-07; exact passage reviewed 2026-10-07.
- Import resources overview | Terraform | HashiCorp Developer — Supported import reconstructs managed bindings from known IDs and matching resource configuration; state loss is not literally unrecoverable. Retrieved 2026-10-07; exact passage reviewed 2026-10-07.
- Gilbert and Lynch CAP proof, 2002 — CAP rules out always-available atomic read/write objects under the specified asynchronous partition/fair-execution assumptions. CAP availability means a nonfailed node eventually answers the requested operation, not an operational uptime percentage. Retrieved 2026-10-07; exact passage reviewed 2026-10-07.
- Abadi, PACELC, 2010 explanation — The originating PACELC formulation adds latency/consistency tradeoffs in the absence of partitions. Retrieved 2026-10-07; exact passage reviewed 2026-10-07.
These passages support the named propositions, not blanket product certification. The lesson-specific derivations and fixtures are separate evidence; runtime boundaries are stated beside each worked exercise.
Pause / Recall / Apply
Can you explain it without the page?
Close the example. Reconstruct the core idea, then change one assumption. Mark complete when you’re ready; you can always undo it.
Stored in this browser only. No account, no sync. Clearing browser data removes your record.