Can Docker Replicate Kubernetes Pods? Exploring Containers, Namespaces, and cgroups
This article investigates how containers and Kubernetes Pods differ and overlap by examining their underlying Linux namespaces, cgroup hierarchies, and runtime specifications, then demonstrates how to emulate a Pod using only Docker commands and shared resources.
Introduction
Containers could serve as lightweight VM alternatives, yet Docker/OCI standardization popularized the one‑process‑per‑container model, offering isolation, easier horizontal scaling, and reusability while sacrificing the multi‑service nature of traditional VMs.
Kubernetes advances this concept by introducing Pods—a set of co‑located containers that form the smallest deployable unit.
Personal Exploration of Pods
Initially, each Pod appears to have a unique IP and hostname, allowing containers to communicate via
localhost, suggesting a micro‑server. However, each container maintains an isolated filesystem and cannot see the processes of its siblings, revealing that a Pod is primarily a group of containers sharing a network stack.
Further investigation shows that containers within a Pod can also share memory, indicating that network namespaces are not the sole shared resource.
Questions Driving the Study
How are Pods implemented at the low level?
What practical differences exist between Pods and plain containers?
How can Docker be used to create Pod‑like constructs?
1. Exploring Containers
The OCI runtime specification does not restrict containers to Linux namespaces and cgroups, though this article focuses on the traditional implementation.
Setting Up a Playground
<code>$ cat > Vagrantfile <<EOF
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "debian/buster64"
config.vm.hostname = "docker-host"
config.vm.define "docker-host"
config.vagrant.plugins = ['vagrant-vbguest']
config.vm.provider "virtualbox" do |vb|
vb.cpus = 2
vb.memory = "2048"
end
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y curl vim
SHELL
config.vm.provision "docker"
end
EOF
$ vagrant up
$ vagrant ssh</code>Start a simple container:
<code>$ docker run --name foo --rm -d --memory='512MB' --cpus='0.5' nginx</code>Exploring Container Namespaces
After launching a container, the following isolation primitives are created:
mnt (mount): isolated mount table.
uts (Unix Time‑Sharing): unique hostname and domain.
ipc (inter‑process communication): processes can communicate via system‑level IPC within the container.
pid (process ID): processes see only those in the same PID namespace.
net (network): dedicated network stack.
User namespaces are not used by default, and Docker’s root user often maps to the host’s root.
Cgroup namespaces provide an isolated view of the cgroup hierarchy but are not enabled by default.
Exploring Container cgroups
cgroups limit resource usage. Example commands:
<code>$ sudo systemd-cgls</code>The hierarchy shows a parent node for the pod and individual containers underneath, matching expectations for per‑container resource limits.
2. Exploring Pods
Pod implementations vary across CRI runtimes; some, like Kata, may run Pods as lightweight VMs. This study uses a minikube cluster with the ContainerD runtime.
Setting Up a Playground
<code>$ curl -sLS https://get.arkade.dev | sh
$ arkade get kubectl minikube
$ minikube start --driver virtualbox --container-runtime containerd</code>Create a pod with two containers:
<code>$ kubectl --context=minikube apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: foo
spec:
containers:
- name: app
image: docker.io/kennethreitz/httpbin
ports:
- containerPort: 80
resources:
limits:
memory: "256Mi"
- name: sidecar
image: curlimages/curl
command: ["/bin/sleep", "3650d"]
resources:
limits:
memory: "128Mi"
EOF</code>Inspecting the pod on the node reveals three containers: the two defined containers and a pause container that provides the shared network, IPC, and PID namespaces.
<code>$ sudo ctr --namespace=k8s.io containers ls
$ sudo crictl ps</code>The pause container’s ID matches the POD ID field in
crictloutput, confirming its role as a sandbox.
Exploring Pod Namespaces
Running
lsnson the node shows that the pause container owns net, mnt, uts, ipc, and pid namespaces, while the app and sidecar containers reuse the net, uts, and ipc namespaces of the pause container.
<code># httpbin container
sudo ls -l /proc/5001/ns
# sidecar container
sudo ls -l /proc/5035/ns</code>Thus, containers in the same Pod can communicate via
localhostand shared IPC mechanisms.
Setting
shareProcessNamespace: truegives all containers a common PID namespace, and flags like
hostIPC,
hostNetwork, and
hostPIDallow containers to adopt the host’s namespaces.
Exploring Pod cgroups
Using
systemd-cglsvisualizes the pod’s cgroup hierarchy, confirming that each container can have individual resource limits while sharing a common parent cgroup.
<code>$ sudo systemd-cgls memory</code>3. Emulating a Pod with Docker
Install
cgroup-toolsand create a parent cgroup:
<code>$ sudo apt-get install cgroup-tools
$ sudo cgcreate -g cpu,memory:/pod-foo</code>Create a sandbox container that will host shared namespaces:
<code>$ docker run -d --rm \
--name foo_sandbox \
--cgroup-parent /pod-foo \
--ipc 'shareable' \
alpine sleep infinity</code>Launch the actual containers reusing the sandbox’s network and IPC namespaces:
<code># app container
$ docker run -d --rm \
--name app \
--cgroup-parent /pod-foo \
--network container:foo_sandbox \
--ipc container:foo_sandbox \
kennethreitz/httpbin
# sidecar container
$ docker run -d --rm \
--name sidecar \
--cgroup-parent /pod-foo \
--network container:foo_sandbox \
--ipc container:foo_sandbox \
curlimages/curl sleep 365d</code>Docker cannot currently share the UTS namespace, but network and IPC sharing works, effectively reproducing most Pod behavior.
Inspecting cgroups and namespaces shows a hierarchy and namespace reuse similar to the Kubernetes pod examined earlier.
4. Summary
Containers and Pods share the same underlying Linux namespaces and cgroups, but a Pod is a higher‑level construct that groups containers on the same node, synchronizes their lifecycles, and deliberately reduces isolation to simplify inter‑container communication, making Pods resemble traditional VMs with sidecar patterns.
Related links:
https://github.com/opencontainers/runtime-spec/issues/345
https://github.com/opencontainers/runtime-spec/pull/388
Efficient Ops
This public account is maintained by Xiaotianguo and friends, regularly publishing widely-read original technical articles. We focus on operations transformation and accompany you throughout your operations career, growing together happily.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.