Containers
What is a container?
What is a container image?
A container image is essentially a snapshot of a distribution’s "user space" (all the files, libraries, and binaries) but it relies entirely on the host for its "kernel space."
The "Distro minus Kernel" Rule
When you pull an Alpine image and run it on an Ubuntu host:
- The Container provides: The
apkpackage manager, themuslC library, and all the small binaries (likels,sh,python). - The Host provides: The Linux Kernel (the code that talks to the CPU, RAM, and Disk).
If you run uname -a inside an Alpine container, you will see the kernel version of the Ubuntu host (e.g., 5.15.0-generic), even though Alpine itself might have been designed alongside a different kernel version. The uname binary is provided by the container, however the uname binary doesn't know anything on its own, it doesn't read a text file like version.txt, instead it calls the uname syscall provided by the kernel.
What if the container needs a feature not on the host?
This is the "Achilles' heel" of containers. Because the container shares the host kernel, it is strictly limited by what that kernel can do.
Scenario A: A missing Kernel Module
Imagine your containerized app needs a specific filesystem (like XFS) or a specific networking feature (like IPVS).
- Result: The app inside the container will try to make a syscall to use that feature. The Host Kernel will look at its table, see that the feature is missing or the module isn't loaded, and return an error (usually
ENOSYS: Function not implemented). - The Fix: You cannot "install" a kernel feature inside a container. The Host Administrator must load the module on the host (
modprobe xfs) for the container to be able to use it.
Scenario B: Version Mismatch (New Syscalls)
Imagine you have a very new container binary compiled to use io_uring (a modern high-speed I/O feature), but you are running it on an old RHEL 6 host with a 2.6.x kernel.
- Result: The binary will attempt a syscall that the old kernel doesn't even recognize. The application will likely crash immediately with a "Segmentation Fault" or "Function not implemented."
Scenario C: Differing C-Libraries
This is a common point of confusion. Alpine uses musl, while Ubuntu uses glibc.
- Result: This actually works fine. Because the container includes its own libraries (
musl), the application inside talks tomusl, andmuslthen translates those requests into standard Linux syscalls that the host kernel understands. As long as the syscall interface is the same, the internal distro doesn't matter.
Do containers need the exact same kernel version?
No! And this is the magic of Linux. The distro inside the container does not assume or need the host to be running the exact same kernel version.
You can effortlessly run a brand-new Ubuntu 24.04 container (which expects a modern 6.x kernel) on an old CentOS 7 host running an ancient 3.10 kernel.
This works because of a foundational law in Linux development dictated by Linus Torvalds himself: "Do not break user space."
Built on top of cgroups and namespaces
Containers are enabled by the linux kernel features of cgroups and namespaces.
cgroups: provides namespace isolation and abilities to limit, account and isolate resource usage (CPU, memory, disk I/O, network, etc.) of process groups.
Docker
An open source Linux containerization technology. Package, distribute and runtime solution.
Does Docker use LXC?
Previously Docker used LXC as its default execution environment.
Since the release of version 0.9, Docker replaced LXC (liblxc) with its own component, libcontainer, which was written in Golang.
runc is basically a repackaging of libcontainer, to comply with the OCI spec. You can find libcontainer code in the runc repo: https://github.com/opencontainers/runc
LXC
Linux Containers (LXC): on top of cgroups, operating system–level virtualization technology for running multiple isolated Linux systems (containers) on a single control host.
LXC's main focus is system containers: containers which offer an environment as close as possible as the one you'd get from a VM but without the overhead that comes with running a separate kernel and simulating all the hardware (i.e. a full Linux OS inside a container).
LXD
- Canonical suppots LXC, but fully owns LXD.
- LXD can run both KVM-based VMs, or on top of LXC (providing a REST API on top of
liblxc).
Docker vs LXC
- Docker: application container
- LXC: system container
Sandbox: gvisor, kata-container
kata-container
kata-container: container in light weight VM.
Kata Containers implements the CRI (Container Runtime Interface) and provides an additional level of isolation for standard containers by running them in virtual machines.
gVisor
https://github.com/google/gvisor
A user-space kernel for containers. It limits the host kernel surface accessible to the application while still giving the application access to all the features it expects. It leverages existing host kernel functionality and runs as a normal user-space process. For running untrusted workloads. Lower memory and startup overhead compared to a full VM.
bootc
Boot and upgrade via container images.
https://github.com/containers/bootc
Where to run the containers?
- in a Kubernetes cluster.
- in a serverless plaform like Google Cloud Run.
- directly in a VM or a bare metal machine.