Namespaces and cgroups: what a container actually is
A container is not a thing the kernel knows about. It is an ordinary process with two kernel features applied to it: namespaces, which change what it can see, and cgroups, which change what it can use. Understanding that split explains most container behaviour that otherwise looks like magic, including why a container can see the host's memory and size itself wrong.
TL;DR: Namespaces control visibility, cgroups control consumption. A container is a normal Linux process with both applied. There is no container object in the kernel, which is exactly why several confusing behaviours make sense once you stop looking for one.
The two mechanisms
Namespaces partition what a process can see. There are several kinds and each virtualises one global resource. The PID namespace gives the process its own process tree, whose first process is PID 1; an application started by an init wrapper may have another PID. The mount namespace gives it its own filesystem view, which is what makes the image look like the whole disk. The network namespace gives it its own interfaces, routing table and port space, so processes in separate network namespaces can both bind port 8080. Containers in the same Kubernetes pod normally share a network namespace and therefore compete for the same ports. The UTS namespace gives it its own hostname, the IPC namespace its own shared memory segments, and the user namespace its own UID mapping, so root inside can be an unprivileged user outside.
Cgroups limit what a process can consume. Cgroups v2 is a unified hierarchy where each controller caps or weights a resource: memory, CPU, IO, PIDs. A memory limit in a container manifest becomes memory.max on the cgroup. A CPU limit becomes a quota and period pair.
Put both on a process and you have a container. Take them off and you have a process. The runtime, whether containerd or CRI-O, is doing bookkeeping around these two kernel features and an image filesystem.
Why this matters in an interview
Three behaviours follow directly and all three appear in interviews.
PID 1 has no default signal handlers. When your app is PID 1 inside its PID namespace, the kernel does not apply default actions for catchable signals to it. An unhandled SIGTERM is discarded rather than terminating the process, which is why containers hang until the grace period expires.
A CPU limit is throttling, not slowing. The CPU controller enforces a quota per period, typically 100 milliseconds. A process that exhausts its quota is stopped until the next period begins. So a limit of 0.5 CPU does not run your code at half speed, one continuously runnable thread can consume its 50-millisecond budget before the 100-millisecond period ends; parallel threads can exhaust the aggregate budget sooner, which is why latency-sensitive services with tight CPU limits show periodic multi-millisecond stalls that look like garbage collection but are not.
Memory pressure can trigger reclaim, throttling or OOM. On cgroup v2, memory.high puts allocating tasks under reclaim pressure and throttles them when usage exceeds the threshold. memory.max is the hard boundary: if reclaim cannot bring usage under the limit, the cgroup can invoke the OOM killer. Kubernetes memory limits do not imply that memory.high has been configured. Exit code 137 means SIGKILL under the usual shell convention; it can also result from an administrator or runtime killing the process. Check memory.events, the container’s termination reason and node logs before calling it OOM. The kernel’s cgroup v2 documentation defines both controls and their failure behaviour.
The classic trap
Older runtimes and older language runtimes read /proc/meminfo, which is not namespaced. A JVM or Node process therefore sees the host's total memory rather than its cgroup limit, sizes its heap against that number, and dies during warm-up on a container with a limit far below it. Modern JVMs are container-aware by default and Node accepts an explicit heap size, but the underlying reason is worth carrying: /proc shows host values for anything the kernel has not namespaced, so a container's view of the machine is only partly virtual.
Self-check
Can you say, in one sentence each, what a network namespace gives a container and what a memory cgroup takes away? If a container is stuck at 100 percent CPU with a limit of 1, is it saturated or throttled, and which file would tell you?