DevOps.Academy
CoursesCurriculumPricing

    Loading…

    ↑ ↓ move · ↵ open · Ctrl K anywhere

    ◔My learningSign in
    DevOps.Academy

    Free, hands-on DevOps lessons. Made with care in India.

    Learn

    All coursesLinux roadmapCurriculumMy learning

    Academy

    PricingCertificates

    Legal

    PrivacyTermsContact

    devops.rajeev.pro

    DockerLesson 2 of 14
    Your progress · 0%
    On this page
    1. A container is a process
    2. Namespaces: what the container can see
    3. cgroups: what the container can use
    4. Layers and copy-on-write
    ↖ Course roadmap
    Lesson 2 · Under the hood

    Under the hood: namespaces, cgroups and layered filesystems

    What Docker does when it starts a container: namespaces hide the rest of the machine, cgroups cap its resources, and an overlay filesystem stacks a writable layer on a shared image.

    30 min read · beginner · hands-on

    By the end of this lesson you'll be able to

    0 of 5 complete

    A container is a process

    There's no "container" inside the Linux kernel. When you docker run, Docker starts an ordinary process and wraps it in three kernel features: namespaces (what it can see), cgroups (what it can use) and an overlay filesystem (its own /, built from image layers). The Linux course's Containers on Linux lesson builds one by hand; here you'll see exactly what Docker sets up.

    $ docker run -d --name cow alpine:3.22 sleep 300
    $ docker exec cow ps                         # inside: the app is PID 1
    PID   USER     TIME  COMMAND
        1 root      0:00 sleep 300
        8 root      0:00 ps
    $ docker inspect -f '{{.State.Pid}}' cow     # the same process, seen from the host
    215435
    $ ps -o pid,user,cmd -p 215435
        PID USER     CMD
     215435 root     sleep 300
    
    docker run -d --name cow alpine:3.22 sleep 300
    • dockerbuild and run containers
    • runstart a new container
    • -drun in the background
    • --name cowname the container
    • alpine:3.22the image to start
    • sleep 300the command run inside, instead of the image’s default
    docker exec cow ps
    • execrun a command in a running container
    • cowthe container
    • pslist the services and their state
    docker inspect -f '{{.State.Pid}}' cow
    • inspectshow every detail as JSON
    • '{{.State.Pid}}'the container or image
    • cowthe container or image
    ps -o pid,user,cmd -p 215435
    • pslist processes

    Same process, two numbers. That's the whole trick, and it's why host tools such as ps, top, kill and strace work on containers.

    Namespaces: what the container can see

    Each namespace gives the process its own copy of one part of the system. Compare the namespace IDs on the host and in a container:

    $ readlink /proc/self/ns/pid /proc/self/ns/net      # on the host
    pid:[4026532219]
    net:[4026531840]
    $ docker run --rm alpine:3.22 sh -c 'readlink /proc/self/ns/pid; readlink /proc/self/ns/net'
    pid:[4026532234]
    net:[4026532236]
    
    docker run --rm alpine:3.22 sh -c 'readlink /proc/self/ns/pid; readlink /proc/self/ns/net'
    • dockerbuild and run containers
    • runstart a new container
    • --rmdelete the container when it exits
    • alpine:3.22the image to start
    • sh -c 'readlink /proc/self/ns/pid; readlink /proc/self/ns/net'the command run inside, instead of the image’s default

    Different numbers mean different namespaces:

    NamespaceThe container getsYou notice it as
    pidits own process listps shows only its processes, the app is PID 1
    netits own interfaces, IP and portsit has its own localhost; ports must be published (lesson 10)
    mntits own mounts/ is the image, not the host
    utsits own hostnamedocker run --hostname shop … hostname prints shop
    ipc, user, cgroupshared memory, user IDs, cgroup viewrarely, until something needs them

    cgroups: what the container can use

    Control groups limit CPU, memory and process counts. The docker run flags write straight into the container's cgroup files:

    $ docker run --rm --memory 128m --cpus 0.5 alpine:3.22 \
        sh -c 'cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max'
    134217728
    50000 100000
    
    docker run --rm --memory 128m --cpus 0.5 alpine:3.22 \
    • dockerbuild and run containers
    • runstart a new container
    • --rmdelete the container when it exits
    • --memory 128mmemory limit
    • --cpus 0.5CPU limit, in cores
    • alpine:3.22the image to start
    • \the command run inside, instead of the image’s default
    Flagcgroup fileMeans
    --memory 128mmemory.max = 134217728128 × 1024 × 1024 bytes; over it, the OOM killer stops a process (exit 137)
    --cpus 0.5cpu.max = 50000 10000050 ms of CPU time in every 100 ms: half a core
    --pids-limit 200pids.max = 200stops fork bombs

    Without limits, one leaking container can use all the host's memory and take every other container down with it. docker stats shows live usage against these limits.

    Layers and copy-on-write

    An image is a stack of read-only layers. Starting a container adds one thin writable layer on top, and the kernel's overlay filesystem merges them into the / the container sees:

    $ docker run --rm alpine:3.22 sh -c 'mount | head -1'
    overlay on / type overlay (rw,relatime,lowerdir=…/snapshots/227/fs:…/snapshots/218/fs,upperdir=…/snapshots/228/fs,workdir=…)
    
    docker run --rm alpine:3.22 sh -c 'mount | head -1'
    • dockerbuild and run containers
    • runstart a new container
    • --rmdelete the container when it exits
    • alpine:3.22the image to start
    • sh -c 'mount | head -1'the command run inside, instead of the image’s default

    lowerdir is the read-only layers; upperdir is this container's writable layer.

    Reads come from the shared image. Writes, and even deletes, only touch the container's own layer, which dies with it.© Animation: DevOps Academy

    Reads come from the shared image. Writes, and even deletes, only touch the container's own layer, which dies with it.© Animation: DevOps Academy

    docker diff lists what a container changed compared with its image:

    $ docker diff cow
    C /root
    A /root/note.txt
    C /etc
    A /etc/resolv.conf.bak
    D /etc/motd
    
    docker diff cow
    • dockerbuild and run containers

    Added, Changed, Deleted. Writing a file that exists in the image first copies it up into the writable layer (copy-on-write), then changes the copy. Deleting one adds a "whiteout" marker that hides it. The image itself never changes, which is why a hundred containers can share one image, and why anything a container writes is lost when you remove it.

    $ docker image history alpine:3.22 --format '{{.Size}}\t{{.CreatedBy}}'
    0B      CMD ["/bin/sh"]
    8.97MB  ADD alpine-minirootfs-3.22.6-x86_64.tar.gz /
    
    docker image history alpine:3.22 --format '{{.Size}}\t{{.CreatedBy}}'
    • dockerbuild and run containers
    • imagemanage images
    • --format '{{.Size}}\t{{.CreatedBy}}'print only these fields (Go template)
    In production

    Treat the writable layer as scratch space. Logs go to stdout and stderr (read with docker logs), data goes to volumes, and the container itself stays disposable: you replace it, you don't repair it.

    ⌘
    Mini mission

    Prove a container is just a process

    Start an alpine container with memory and CPU limits, then prove from the outside and the inside that it is a host process with its own PID numbering, its own namespaces, the cgroup limits you set, and a writable layer that records every change.

    Input: Docker on a Linux host (or Docker Desktop)Output: Notes showing host PID vs PID 1, two namespace IDs, the cgroup values and docker diff
    Reveal one safe solution
    $ docker run -d --name probe --memory 128m --cpus 0.5 alpine:3.22 sleep 600
    $ docker exec probe ps                                   # PID 1 inside
    $ ps -o pid,cmd -p "$(docker inspect -f '{{.State.Pid}}' probe)"   # a normal PID outside
    $ docker exec probe readlink /proc/self/ns/pid && readlink /proc/self/ns/pid
    $ docker exec probe sh -c 'cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max'
    134217728
    50000 100000
    $ docker exec probe sh -c 'echo hi > /root/note.txt && rm /etc/motd'
    $ docker diff probe
    C /root
    A /root/note.txt
    C /etc
    D /etc/motd
    $ docker rm -f probe
    
    docker run -d --name probe --memory 128m --cpus 0.5 alpine:3.22 sleep 600
    • dockerbuild and run containers
    • runstart a new container
    • -drun in the background
    • --name probename the container
    • --memory 128mmemory limit
    • --cpus 0.5CPU limit, in cores
    • alpine:3.22the image to start
    • sleep 600the command run inside, instead of the image’s default
    docker exec probe ps
    • execrun a command in a running container
    • probethe container
    • pslist the services and their state
    ps -o pid,cmd -p "$(docker inspect -f '{{.State.Pid}}' probe)"
    • pslist processes
    docker exec probe readlink /proc/self/ns/pid && readlink /proc/self/ns/pid
    • readlink /proc/self/ns/pidthe command to run inside it
    • &&run the next command only if this one worked
    docker exec probe sh -c 'cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max'
    • sh -c 'cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/cpu.max'the command to run inside it
    docker exec probe sh -c 'echo hi > /root/note.txt && rm /etc/motd'
    • sh -c 'echo hi > /root/note.txt && rm /etc/motd'the command to run inside it
    docker rm -f probe
    • rmdelete a container
    • -fstop it first if running

    The two pid:[…] numbers differ, memory.max is exactly 128 × 1024 × 1024, and docker diff lists your two changes. After docker rm, start a fresh container from the same image: /root/note.txt isn't there and /etc/motd is back.

    Try it yourself

    0 of 6 steps done

    Run each task in your own account or terminal, then tick it off.

    Knowledge check

    Quick check

    Question 1 of 5

    Inside a container, ps shows the app as PID 1. What PID does it have on the host?

    Go deeper

    Official reading for this lesson

    • DocsDocker Docs: storage drivers and copy-on-writeDocker Docs ↗
    • DocsDocker Docs: resource constraintsDocker Docs ↗
    • Docsnamespaces(7) manualUbuntu manpages ↗
    • DocsLinux kernel: overlay filesystemLinux kernel ↗

    Or get every quiz answer right and it completes itself.

    ← PreviousContainers: what they are and why they wonNext lesson →Installing Docker: Desktop, Engine on Linux, and rootless