interview-prep

Crisp answer: Namespaces give processes an isolated view of the system (they can't see outside their namespace). cgroups limit what resources they can consume. Together they're what makes containers work — there's no guest kernel, just isolation and resource limits on regular Linux processes.

Namespaces in depth:

A namespace wraps a global resource and gives each namespace its own isolated instance. Processes inside see only their namespace's resources.

PID namespace:

# On the host, a container's init process has some high PID:
ps aux | grep myapp
# 29847  /myapp/server

# Inside the container, the same process sees itself as PID 1:
docker exec mycontainer ps aux
# PID  CMD
#   1  /myapp/server

# Why this matters: a process can only send signals to processes in its
# namespace. PID 1 in a container namespace gets SIGTERM when the container
# stops — it must handle it to shut down gracefully.

Network namespace:

# Each container gets its own network namespace:
# - Its own loopback (127.0.0.1)
# - Its own eth0 with a container IP
# - Its own routing table
# - Its own iptables rules

# The veth (virtual ethernet) pair connects the container ns to the host:
ip link show    # On host: shows veth pairs
ip netns list   # List network namespaces (may not show Docker namespaces)

# Kubernetes creates a pause container to hold the network namespace for the pod.
# All containers in a pod share the same network namespace — that's why they
# can communicate on localhost.

Mount namespace:

# Each container has its own filesystem view.
# Uses overlay (overlayfs) filesystem:
#   Lower: read-only image layers
#   Upper: writable container layer (lost when container is removed)
#   Merged: what the container sees

docker inspect --format '{{.GraphDriver.Data.LowerDir}}' mycontainer
# /var/lib/docker/overlay2/abc.../diff:
# /var/lib/docker/overlay2/def.../diff

# Kubernetes uses similar overlay layers via containerd

User namespace (rootless containers):

# User namespaces map UIDs inside the namespace to different UIDs outside.
# This allows a process to be "root" (UID 0) inside a container,
# but map to an unprivileged user on the host.

# Inside container: root (UID 0)
# On host: UID 65534 (nobody)

# This is how Podman rootless and Docker rootless mode work.
cat /proc/<pid>/uid_map    # See the UID mapping

Inspecting namespaces:

# See what namespaces a process is in:
ls -la /proc/<pid>/ns/
# lrwxrwxrwx  cgroup -> cgroup:[4026531835]
# lrwxrwxrwx  ipc    -> ipc:[4026531839]
# lrwxrwxrwx  mnt    -> mnt:[4026532174]   ← unique = has its own mount namespace
# lrwxrwxrwx  net    -> net:[4026532176]   ← unique = has its own network namespace
# lrwxrwxrwx  pid    -> pid:[4026532175]   ← unique = has its own PID namespace
# lrwxrwxrwx  user   -> user:[4026531837]
# lrwxrwxrwx  uts    -> uts:[4026532173]

# Two processes with the same namespace inode share that namespace.
# Two processes with different inodes are isolated.

# Enter a process's namespace (debugging tool):
nsenter -t <pid> --net     # Enter its network namespace
nsenter -t <pid> --all     # Enter all namespaces (like exec into a container)

cgroups in depth:

cgroups (control groups) organise processes into a hierarchy and enforce resource limits. cgroups v2 (unified hierarchy) is the default on modern kernels.

# cgroups v2 filesystem:
ls /sys/fs/cgroup/
# cpu.stat  io.stat  memory.stat  ...
# system.slice/  user.slice/  docker/  kubepods/

# Kubernetes cgroup structure:
ls /sys/fs/cgroup/kubepods/
# burstable/  guaranteed/  besteffort/
# These correspond to Kubernetes QoS classes

# See a pod's cgroup:
cat /proc/<container-pid>/cgroup
# 0::/kubepods/burstable/podXXXX/containerYYYY

# Memory limit for a container:
cat /sys/fs/cgroup/kubepods/burstable/podXXXX/containerYYYY/memory.max
# 536870912  ← 512MB in bytes (or "max" = unlimited)

# Current memory usage:
cat /sys/fs/cgroup/kubepods/burstable/podXXXX/containerYYYY/memory.current

# CPU limit (100000 = 100ms of 100ms = 1 full CPU):
cat /sys/fs/cgroup/kubepods/burstable/podXXXX/containerYYYY/cpu.max
# 200000 100000  ← 200ms per 100ms = 2 CPUs

OOM killer and cgroups:

When a cgroup exceeds its memory limit, the kernel's OOM killer fires and kills processes in that cgroup. In Kubernetes:

# Check for OOM kills:
dmesg | grep -i "oom\|killed process"
# [1234.567] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=...,
#            task=myapp,pid=29847,uid=0
# [1234.568] Memory cgroup out of memory: Killed process 29847 (myapp)

# Or in journalctl:
journalctl -k | grep -i oom

# Kubernetes event:
kubectl describe pod <pod>
# Last State: Terminated Reason: OOMKilled

The complete container creation sequence (using namespaces + cgroups):

  1. Container runtime creates a new cgroup for the container
  2. Sets memory, CPU, pids limits on the cgroup
  3. Calls clone() with namespace flags:
    • CLONE_NEWPID — new PID namespace (process sees itself as PID 1)
    • CLONE_NEWNET — new network namespace
    • CLONE_NEWNS — new mount namespace
    • CLONE_NEWUTS — new hostname namespace
    • CLONE_NEWIPC — new IPC namespace
  4. Adds the new process to the cgroup
  5. Sets up the overlay filesystem and pivot_root into it
  6. Applies seccomp profile and drops capabilities
  7. execve() the container entrypoint

What to say in the interview:

"Namespaces and cgroups are the two kernel primitives that make containers work. Namespaces give each container an isolated view: its own PID space so it thinks it's PID 1, its own network stack with its own IP, its own filesystem. cgroups enforce the limits: memory.max triggers OOM kills, cpu.max enforces CPU quotas. When you see OOMKilled in Kubernetes, that's the kernel's OOM killer firing because the container exceeded its cgroup memory limit. I can inspect this directly in /sys/fs/cgroup/kubepods — the container's cgroup is there with current usage and limits. The container runtime sets all this up with clone() flags and cgroup writes; the kernel enforces everything. There's no hypervisor, no guest kernel — just very well-configured isolation on the host kernel."

My notes