skip to content

Before moving a ticket-booking checkout service onto Kubernetes, what must the application itself change to run well as a container?

level: juniorimportance: must knowfreq 70%

answer

  1. same image, different inputs
  2. where does kubectl logs read
  3. ready means able to serve
  4. signal, drain, exit in time
  5. cgroup, not the node

basics

~20 s

The app must read configuration from environment variables or mounted files, log to stdout and stderr, expose a readiness signal that reflects real ability to serve, shut down cleanly on SIGTERM, and size itself from its container limits.

solid answer

~50 s

Kubernetes assumes a process it can start, stop and replace anywhere, so the app has to give up host assumptions. **Config** comes from environment variables or mounted files, so the same image runs in every environment. **Logs** go to stdout/stderr, where the container runtime captures them and `kubectl logs` or a log agent picks them up; files inside the container vanish with it. A **readiness endpoint** reports whether this instance can actually take a checkout right now, so the Service only routes to ready pods. On **SIGTERM** the app stops taking new work, finishes in-flight requests and exits before the grace period (30 seconds by default) ends, or it is killed. Finally the **runtime** must size thread pools and memory from the container's cgroup limits, not from the node, and anything it writes should go to a declared volume, not the image filesystem.

code

yaml · 23 lines
yaml
apiVersion: v1
kind: Pod
metadata:
  name: checkout
spec:
  terminationGracePeriodSeconds: 45
  containers:
    - name: checkout
      image: registry.example.com/booking/checkout:4.12.3
      env:
        - name: PAYMENT_GATEWAY_URL
          value: https://pay.internal.example.com
        - name: LOG_FORMAT
          value: json
      readinessProbe:
        httpGet:
          path: /ready
          port: 8443
          scheme: HTTPS
      resources:
        limits:
          cpu: 1500m
          memory: 3Gi

go deeper

for a junior

Recall the five items by name: config from the environment, logs to stdout, honest readiness, clean SIGTERM handling, sizing from limits. Give one concrete example of each from a real app.

for a middle

Explain the mechanism behind each item: where kubectl logs reads from, what happens after the grace period, why Services only route to Ready pods, and why a runtime can misread its CPU count.

for a senior

Show how you would verify readiness before a migration: run the image with limits and a read-only root filesystem, send SIGTERM under load, and check logs, readiness and exit timing.

for a principal

Frame the checklist as a platform contract: which items the platform can enforce by policy or templates, which only code review catches, and how to keep legacy apps moving without waiving the rules.

## Why an application needs a pre-flight check A Kubernetes **Pod** is disposable. The scheduler can place it on any node, the Deployment controller can replace it during a rollout, and a node drain can evict it at any time. A ticket-booking checkout that ran for years on one VM usually carries assumptions that break under that model: a config file edited by hand, a log file rotated by the host, a process that dies abruptly, a runtime that thinks the whole machine is its own. Application readiness is the list of assumptions to remove **before** the first deploy, not after the first incident. ## The five items | Item | VM habit | Container-ready form | |---|---|---| | Configuration | `/etc/checkout/app.conf` edited per host | environment variables or files mounted into the pod | | Logging | `/var/log/checkout.log` plus host rotation | one event per line on stdout/stderr | | Readiness | "the port is open" | an endpoint that answers "can I take a booking now?" | | Shutdown | killed with the VM | handles SIGTERM, drains, exits in time | | Sizing | reads the host's CPUs and RAM | reads the container's cgroup limits | ### 1. Configuration from the environment The image should be **identical** in every environment; only its inputs differ. Kubernetes can inject values as environment variables (`env`, `envFrom`) or as files in a mounted volume. The application's job is simply to read them at startup, and to fail fast with a clear message when a required value is missing. Secrets such as the payment gateway key follow the same rule and are never baked into the image. ### 2. Logs on stdout and stderr The container runtime captures a container's standard streams, the kubelet exposes them, and `kubectl logs` reads them. Cluster log agents usually collect the same files from each node. A log written to a file inside the container is invisible to all of that and disappears when the container is replaced. Practical rules: - write one structured event per line, so a multi-line stack trace does not become many records; - do not rotate or compress logs in the app; - keep the request ID in every line, because a request may cross several pods. ### 3. A readiness signal that tells the truth Kubernetes routes Service traffic only to pods that report **Ready**. The application must expose an endpoint that turns healthy only once caches are warm and the connection pool is open, and that turns unhealthy when this instance genuinely cannot take bookings. "The HTTP port answers" is not the same statement. How the probe itself is configured is a separate topic; the application's duty is to make the answer honest. ### 4. A clean SIGTERM path When a pod is deleted, the kubelet sends the container's main process **SIGTERM**, waits up to `terminationGracePeriodSeconds` (30 seconds unless the pod sets another value), then sends SIGKILL. A checkout that ignores SIGTERM loses every in-flight payment when the kill arrives. A ready application: 1. stops accepting new requests (or starts failing readiness); 2. finishes or hands back work already in progress; 3. closes connections and flushes buffers; 4. exits with status 0 well inside the grace period. The process must also actually **receive** the signal, which means it runs as the container's main process or behind an init that forwards signals. A `preStop` hook can add a short pause before SIGTERM; its detailed timing belongs to the graceful-shutdown topic. ### 5. A runtime that reads its cgroup limits Container limits are enforced by Linux cgroups, but a process that asks "how many CPUs are there?" may be told the node's count. A JVM or Go program that sizes thread pools, GC threads or heap from the node's 32 cores and 128 GiB will overrun a 2-CPU, 3 GiB container. Modern runtimes read the cgroup limits; older ones need explicit settings. ## Two items people forget - **Writes**: anything the app writes (temp files, upload buffers, caches) should land on a declared volume such as `emptyDir`, so the root filesystem can later be made read-only. - **No local identity**: session state, scheduled jobs and fixed IPs belong elsewhere; the app must tolerate running as several interchangeable replicas. ## Putting it together A useful habit is to run the checkout locally in a container with a memory limit, a read-only root filesystem and a `docker stop` timeout, and watch what breaks. Every failure there is a failure you would otherwise have met in the cluster during a rollout.

  • Why is logging to a file inside the container a problem even if the app rotates it?
    The file lives in the container's writable layer, so it disappears when the container is replaced, and `kubectl logs` and node log agents never see it because they read the captured stdout/stderr. It also consumes node ephemeral storage, which can get the pod evicted. Writing to stdout lets the platform own collection, rotation and retention.
  • What happens to in-flight checkouts if the app ignores SIGTERM?
    Nothing changes when SIGTERM arrives, so the app keeps working until `terminationGracePeriodSeconds` (30 seconds by default) expires. The kubelet then sends SIGKILL, which cannot be caught, and every request still running is cut off mid-flight. Clients see reset connections or half-finished bookings, and each rollout also takes the full grace period per pod.
  • Why must a readiness endpoint differ from simply checking that the port is open?
    An open port only proves the listener exists. The checkout may still be warming caches, missing its database pool, or overloaded. Readiness decides whether the Service sends it traffic, so it should report whether this instance can complete a booking now; otherwise users hit a pod that accepts connections and then fails.

It is like moving from a home kitchen to a shared commercial one: you bring labelled ingredients, clean up when the shift bell rings, and cook to the size of your station, not the whole building.

saying these in an interview costs you the question

  • Bakes per-environment config files into separate images for dev and prod
  • Keeps logging to a local file because the container has a writable layer
  • Says Kubernetes waits for the app to finish before stopping it, however long
  • Treats an open TCP port as proof the service is ready for traffic
  • Assumes the JVM or runtime automatically sees only the container's resources, whatever its version