Module 5 · Unit 13 · Lesson 3 of 7
Containers
Lesson 13.1 and Lesson 13.2 presented the virtual machines and the hypervisors: the virtualization's approach that emulates the complete's hardware and runs the separate's kernels. This lesson presents the containers: the lightweight's isolation that runs the processes on the shared kernel, with the namespaces, the cgroups, and the union file systems providing the isolation, the resource's limits, and the file system's packaging. The container is the modern computing's most visible abstraction — the Docker's images, the Kubernetes's pods, the serverless's functions, the CI/CD's pipelines all depend on it — and its design's trade-offs (the density, the startup's speed, the resource's usage versus the isolation's strength) are the reasons the modern systems combine the containers with the virtual machines. The lesson presents the container's concept, its history, its mechanisms, the OCI standards, the runtimes, the images, the lifecycle, the security, and the comparison with the virtual machines.
01 Introduction
The previous lessons presented the virtualization's approach: the virtual machines emulate the complete's hardware, and the hypervisors run the separate's kernels. The virtualization's benefits (the isolation, the consolidation, the portability) are the reasons the modern's data centers are the virtualized. But the virtualization's design's cost: the separate's kernels (each VM runs its own operating system: the GBs of the memory, the seconds of the startup, the tens of the VMs per host), and the emulation's overhead (the hypervisor's traps, the I/O's mediation). This lesson presents the containers: the lightweight's isolation that runs the processes on the shared kernel, and that achieves the isolation, the resource's limits, and the file system's packaging through the kernel's mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM).
A container is the isolated's environment in which one or more processes run, with the isolated's view of the system's resources (the file system, the processes, the network, the IPC, the users), the limited's resource's usage (the CPU, the memory, the I/O, the processes' count), and the packaged's file system (the container's image: the application and its dependencies). The container's design's central's observation: the container's processes run on the host's kernel, and the kernel's mechanisms (the namespaces and the cgroups) provide the isolation and the limits. The observation is the reason the containers are the lightweight: the container's startup is the milliseconds (the process's start, not the kernel's boot), the container's memory's overhead is the MBs (the application's memory, not the kernel's), and the container's density is the hundreds or the thousands per host (not the tens).
The container's and the VM's comparison (the Lesson 13.1's discussion):
| The aspect | The container | The virtual machine |
|---|---|---|
| The kernel | The shared (the host's kernel) | The separate (the guest's kernel) |
| The isolation | The namespaces and the cgroups (the kernel's mechanisms) | The hypervisor and the hardware's virtualization |
| The startup's time | The milliseconds (the process's start) | The seconds (the kernel's boot) |
| The memory's overhead | The MBs (the application's memory) | The GBs (the guest's kernel and the OS) |
| The density | The hundreds to the thousands per host | The tens per host |
| The operating systems | The same's kernel (the Linux's containers on the Linux's host) | The different's operating systems (the Linux, the Windows, the BSD) |
| The isolation's strength | The kernel's mechanisms (the shared's kernel's vulnerabilities affect the every container) | The strong (the separate's kernels: the hypervisor's boundary) |
| The portability | The images (the OCI's standards: the layers, the registries) | The disk's images (the OVA, the QCOW2, the VMDK) |
| The typical use | The microservices, the CI/CD, the serverless, the cloud's native's applications | The different's OS, the legacy's applications, the strong's isolation's requirements |
The containers' benefits: the lightweight (the fast's startup, the low's overhead, the high's density), the portability (the images: the application and its dependencies in the single's artifact), the consistency (the same's image runs the same's way on the developer's laptop and the production's cluster: the "it works on my machine" problem's solution), the agility (the fast's deployment, the easy's rollback, the frequent's releases), the efficiency (the resource's usage: the containers share the host's kernel, so the overhead is the minimal), and the ecosystem (the Docker's, the Kubernetes's, the cloud's providers' tools). The containers' design's trade-offs: the shared's kernel (the kernel's vulnerabilities affect the every container: the isolation is the weaker than the VMs'), the same's kernel's requirement (the Linux's containers run on the Linux's host: the different's operating systems require the VMs), and the complexity (the containers' orchestration, the networking, the storage, the security).
The containers' history: the container's concept has the deep's roots (the chroot's in the 1979's Unix, the BSD's jails in the 2000's, the Solaris's Zones in the 2004's, the Linux's OpenVZ and the Linux-VServer in the 2000s), and the modern's containers began with the LXC (the Linux Containers: the 2008's, the namespaces and the cgroups' integration), and exploded with the Docker (the 2013's, the images, the registries, the user-friendly's tooling), the OCI (the 2015's standards: the Runtime's Specification, the Image's Specification), the containerd and the CRI-O (the runtimes), and the Kubernetes (the 2014's orchestration). The observation: the containers' explosion was the Docker's user-experience's and the Kubernetes's orchestration's driver, not the kernel's mechanisms' (the namespaces and the cgroups were the years' old), and the observation is the reason the container's history is the tooling's history.
This lesson presents the containers. It defines the container and the container's comparison with the VM. It presents the container's history and the container's mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM). It presents the OCI standards (the Runtime's Specification, the Image's Specification, the Distribution's Specification), the container's runtimes (the runc, the crun, the containerd, the CRI-O), and the container's images (the layers, the overlayfs, the registries). It presents the container's lifecycle, the container's security, and the containers' use's cases. It closes with the containers' design's trade-offs and the lesson's synthesis.
Prerequisites
This lesson builds on Lesson 13.1 (the virtual
machines) and Lesson 13.2 (the hypervisors),
and on Lesson 12.6 (the sandboxing). The
virtual machine's concept, the container's
and the VM's comparison, the hypervisor's
role, and the virtualization's mechanisms
are assumed (Lessons 13.1 and 13.2). The
sandboxing's mechanisms (the namespaces, the
cgroups, the seccomp, the capabilities, the
LSM's profiles, the chroot and the
pivot_root, the systemd's hardening's
directives) are assumed (Lesson 12.6). The
process's credentials (Lesson 3.1), the
file system's permissions (Lesson 10.2),
the union file systems' concept, and the
Linux's security's mechanisms (Lesson 12.5:
the capabilities, the SELinux, the AppArmor)
are the context for the container's
mechanisms' discussion. Familiarity with
the Linux command line from Lesson 2.2 is
essential, since the interactive activity
uses the unshare, the
nsenter, the systemd-run,
the podman (or the
docker), the buildah,
the skopeo, the
crun, the runc, the
ctr, the crictl, the
overlayfs's commands, the
systemd-cgls, the
lsns, the unshare,
and the dmesg commands.
02 Learning objectives
By the end of this lesson you will be able to
- Define the container and explain the container versus the virtual machine's distinction, and describe the containers' benefits and trade-offs.
- Describe the containers' history (the chroot, the BSD's jails, the Solaris's Zones, the Linux-VServer, the OpenVZ, the LXC) and the container's mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM).
- Describe the OCI standards (the Runtime's Specification, the Image's Specification, the Distribution's Specification) and the container's runtimes (the runc, the crun, the containerd, the CRI-O).
- Describe the container's images (the layers, the overlayfs, the registries) and the container's lifecycle (the creation, the start, the execution, the pause, the stop, the removal).
- Describe the container's security (the isolation's strength, the attack surface, the container's escapes, the security's configurations) and the containers' use's cases (the microservices, the CI/CD, the serverless, the cloud's native's applications).
- Explain the containers' design's trade-offs (the isolation, the performance, the portability, the resource's usage) and evaluate a container's configuration against the lesson's criteria.
This lesson contributes to U13.3 and U13.4, and to course learning outcomes CLO1, CLO7, and CLO8.
03 Required reading
- Operating System Concepts — Chapter 18, Virtual Machines, the section on the containers and the operating-system-level virtualization. The standard treatment of the container's concept and the container's and the VM's comparison.
- OSTEP — the Virtualization chapters, particularly the discussion of the containers and the namespaces. OSTEP's treatment of the container's mechanisms and the container's and the VM's comparison is the clearest available account, and it is the basis for the worked example in this lesson.
- Tanenbaum & Bos — Modern Operating Systems, Chapter 7, Virtualization and the Cloud. A complementary treatment that develops the container's mechanisms from first principles.
-
Kerrisk, M. — The Linux
Programming Interface. No Starch Press,
2010, and the
man 7 namespaces, theman 7 cgroups, and theman 2 clonepages. The authoritative references for the Linux's namespaces and the cgroups. - Rosen, R. — Resource Management: The Linux and the SUSE Approaches. The authoritative references for the cgroups.
- Merkel, D. — Docker: Lightweight Linux Containers for Consistent Development and Deployment. Linux Journal, 2014. The paper that popularized the Docker's approach.
- Bernstein, D. — Containers and Cloud: From LXC to Docker to Kubernetes. IEEE Cloud Computing, 2014. The survey of the containers' evolution.
- Burns, B., Grant, B., Oppenheimer, D., Brewer, E., & Wilkes, J. — Borg, Omega, and Kubernetes. ACM Queue, 2016. The paper that describes the Kubernetes's design and the container's orchestration.
- The Open Container Initiative (OCI)'s specifications: the Runtime's Specification, the Image's Specification, and the Distribution's Specification. The authoritative references for the containers' standards.
- Rice, L. — Container Security. O'Reilly, 2020. The comprehensive's treatment of the container's security's mechanisms and the escapes' attacks.
- Hertz, B. — Container Security: Fundamental and the NIST SP 800-190 (the Application Container Security Guide). The security's references for the containers.
-
The Linux man pages:
man 7 namespaces,man 2 clone,man 2 unshare,man 1 unshare,man 1 nsenter,man 7 cgroups,man 1 systemd-cgls,man 1 systemd-cgtop,man 1 systemd-run,man 5 systemd.resource-control,man 2 seccomp,man 7 capabilities,man 8 setcap,man 5 overlay(the overlayfs's documentation),man 8 mount,man 8 podman,man 1 podman-run,man 1 podman-exec,man 1 podman-build,man 1 buildah,man 1 skopeo,man 1 docker,man 1 docker-run,man 1 runc,man 1 crun,man 1 ctr,man 1 crictl,man 1 systemd-nspawn,man 1 machinectl,man 1 lsns,man 1 dmesg,man 5 proc(for the/proc/PID/ns/'s and the/proc/PID/cgroup's interfaces).
Reading strategy
Read the Silberschatz chapter first for the
formal's context: the container's concept,
the operating-system-level virtualization,
and the container's and the VM's comparison.
Then read the Kerrisk's references (the
man 7 namespaces, the
man 7 cgroups, the
man 2 clone) for the container's
mechanisms' authoritative's treatment: the
namespaces' semantics, the cgroups'
controllers, and the clone's and
the unshare's system calls.
The Lesson 12.6 (the sandboxing) is the
container's mechanisms' prior's discussion,
and it is worth re-reading for the
namespaces', the cgroups', the seccomp's,
and the capabilities' details. The Merkel's
paper (the Docker's) and the Bernstein's
paper (the containers' survey) are the
container's history's sources, and they are
worth reading for the tooling's and the
ecosystem's evolution. The OCI's
specifications are the container's
standards, and they are worth consulting for
the runtime's and the image's formats. The
Rice's book (the Container Security)
and the NIST SP 800-190 are the container's
security's comprehensive's references, and
they are worth reading for the escapes'
attacks and the configurations'
recommendations. Finally, the Linux man
pages are the practical's tools, and the
interactive activity uses them throughout.
Pay particular attention to the distinction
between the container (the
isolated's process or the isolated's
environment that runs on the shared's
kernel) and the image (the
packaged's file system: the application and
its dependencies). The container is the
running's instance; the image is the
template. The observation: the same's image
can run as the many's containers, and the
container's state (the writable's layer,
the processes, the network) is the
container's, not the image's.
04 Core content
4.1 What a container is
A container is the isolated's environment in which one or more processes run, with the isolated's view of the system's resources, the limited's resource's usage, and the packaged's file system. The container's design's central's observation: the container's processes run on the host's kernel, and the kernel's mechanisms provide the isolation and the limits.
The container's components:
- The isolated's views (the namespaces). The container's processes see the isolated's views of the file system (the mount's namespace), the process's table (the PID's namespace), the network (the network's namespace), the IPC (the IPC's namespace), the users (the user's namespace), the hostname (the UTS's namespace), the cgroups (the cgroup's namespace), and the time (the time's namespace).
-
The limited's usage (the cgroups).
The container's processes' resource's usage
is the limited: the CPU's shares and quotas
(the
cpu's controller), the memory's limits (thememory's controller), the I/O's weights and limits (theio's controller), the processes' count (thepids's controller), and the others (thecpuset, thehugetlb, therdma). -
The packaged's file system (the
image). The container's file system
is the container's image: the layered's
file system (the layers are the
application and its dependencies), and the
container's writes go to the
writable's layer (the
overlayfs's upper's directory), so the image's layers are the read-only and the container's changes are the contained. -
The restricted's privileges (the
capabilities, the seccomp, the LSM).
The container's processes' privileges are
the restricted: the capabilities' set
(the default's dropped's capabilities and
the added's capabilities), the seccomp's
filter (the system calls' whitelist or
blacklist), the LSM's profile (the
SELinux's
container_t, the AppArmor'sdocker-default), and theno-new-privileges's setting (the setuid's and the setgid's programs' prevention). -
The lifecycle's management (the
runtime, the engine). The
container's lifecycle is the managed by the
runtime (the
runc, thecrun, thecontainerd, theCRI-O) and the engine (the Docker, the Podman, the Kubernetes'skubelet): the creation, the start, the execution, the pause, the stop, the removal.
The containers' benefits: the lightweight (the fast's startup: the milliseconds; the low's memory's overhead: the MBs; the high's density: the hundreds to the thousands per host), the portability (the images: the application and its dependencies in the single's artifact: the "it works on my machine" problem's solution), the consistency (the same's image runs the same's way in the development, the testing, the production), the agility (the fast's deployment, the easy's rollback, the frequent's releases), the efficiency (the resource's usage: the containers share the host's kernel), and the ecosystem (the Docker's, the Kubernetes's, the cloud's providers' tools). The containers' design's trade-offs: the shared's kernel (the kernel's vulnerabilities affect the every container: the isolation is the weaker than the VMs'), the same's kernel's requirement (the different's operating systems require the VMs), and the complexity (the containers' orchestration, the networking, the storage, the security).
4.2 The containers' history
The containers' history is the long's evolution, from the classical's chroot to the modern's Docker and Kubernetes:
-
The chroot (the 1979's Unix).
The
chroot's system call changes the process's root's directory, so the process sees the specified's directory as the root. The chroot's purpose was the testing and the build's environments (the Bell Labs' Unix), and its design's weakness (the chroot is not the security's boundary: the escapes are the known) is the Lesson 12.6's discussion. -
The BSD's jails (the 2000).
The FreeBSD's jails: the
jail's system call (the FreeBSD 4.0's) extends the chroot with the process's isolation (the jailed's processes cannot see or signal the host's processes), the network's isolation (the jailed's processes have their own's IP addresses), and the user's isolation (the jailed's root is the unprivileged's user on the host). The jails were the first's complete's container's implementation, and the Solaris's Zones (the 2004's) and the Linux's OpenVZ and Linux-VServer were the parallels. - The Solaris's Zones (the 2004). The Solaris's Zones: the global's zone (the host) and the non-global's zones (the containers), with the isolated's views (the file systems, the processes, the network, the users) and the resource's limits (the CPU's, the memory's, the network's). The Zones were the enterprise's container's implementation, and their design influenced the modern's containers.
- The Linux's namespaces and cgroups (the 2002–2008). The Linux's kernel's additions: the namespaces (the mount's in the 2002's, the UTS's, the IPC's, the PID's, the network's, the user's in the 2006–2013's) and the cgroups (the process's groups' in the 2006–2008's). The Linux's kernel's mechanisms were the containers' foundation, and the observation is the reason the Linux's containers were the modern's containers' platform.
-
The LXC (the 2008). The
LXC (the Linux Containers): the
user-space's tooling (the
lxc-*'s commands) that combined the namespaces, the cgroups, and the chroot into the container's management's interface. The LXC's design was the complete's container's tooling, and it was the Docker's foundation. -
The Docker (the 2013). The
Docker: the user-friendly's tooling (the
docker's CLI, the Dockerfile's build's format, the Docker Hub's registry) that made the containers the accessible to the developers. The Docker's innovations: the image's concept (the layered's file system: the application and its dependencies), the registry's concept (the images' distribution), the Dockerfile's build's format (the declarative's image's definition), and the single's process's container's model (the container runs the single's application). The Docker's explosion was the containers' accessibility's driver, and the observation is the reason the Docker's name is the container's synonym. - The OCI (the 2015). The Open Container Initiative: the standards (the Runtime's Specification, the Image's Specification, the Distribution's Specification) that made the containers the interoperable (the same's image runs on the different's runtimes, the same's runtime runs the different's images). The OCI's standards were the containers' ecosystem's foundation, and the observation is the reason the OCI's specifications are the containers' authority.
- The Kubernetes (the 2014). The Kubernetes (the Google's, the Borg's successor): the orchestration's platform (the containers' scheduling, the scaling, the self-healing, the service's discovery, the configuration's management) that made the containers the production's workloads' platform. The Kubernetes's design was the containers' scalability's driver, and the observation is the reason the Kubernetes is the containers' orchestration's standard.
4.3 The containers' mechanisms
The containers' mechanisms are the Linux's kernel's features that the runtimes combine (the Lesson 12.6's sandboxing's discussion):
- The namespaces (the isolated's views)
-
The eight namespaces (the mount, the PID,
the network, the user, the IPC, the UTS,
the cgroup, the time) give the container's
processes the isolated's views of the
system's resources. The container's
runtime (the
runc, thecrun) creates the namespaces (theclone's and theunshare's system calls), and the container's processes see only the container's resources. The observation: the namespaces are the views, not the limits, and the cgroups are the complementary's mechanism (the Lesson 12.6's discussion). - The cgroups (the limited's usage)
-
The cgroups (the v1's and the v2's)
limit the container's processes'
resource's usage: the CPU's shares and
quotas (the
cpu.weight's and thecpu.max's), the memory's limits (thememory.max's and thememory.high's), the I/O's weights and limits (theio.weight's and theio.max's), and the processes' count (thepids.max's). The container's runtime places the container's processes in the container's cgroup (thesystemd'smachine.slice's or thelibpod's or thedocker's hierarchy), and the cgroup's controllers enforce the limits. - The union file systems (the packaged's file system)
-
The union file systems (the
overlayfs, theaufs, thedevicemapper, thebtrfs, thezfs) present the multiple's file system's layers as the single's merged's view. The container's image is the stack of the read-only's layers (the base's image, the application's dependencies, the application's code), and the container's writes go to the writable's layer (theoverlayfs's upper's directory). The union file system's advantages: the storage's efficiency (the layers are the shared between the containers: the same's base's image is the stored the once), the fast's startup (the layers are the mounted, not the copied), and the container's isolation (the writes are the contained). Theoverlayfsis the modern's default (the Linux 3.18's, the production's stable in the Linux 4.x). - The capabilities (the restricted's privileges)
-
The Linux's capabilities (the Lesson
12.5's discussion) split the root's
privileges, and the container's runtime
drops the unnecessary's capabilities and
keeps the specific's. The Docker's
default: the dropped's set (the
CAP_SYS_ADMIN, theCAP_SYS_PTRACE, theCAP_SYS_MODULE, theCAP_NET_ADMIN, and the others: the dangerous's capabilities) and the kept's set (theCAP_CHOWN, theCAP_DAC_OVERRIDE, theCAP_FOWNER, theCAP_KILL, theCAP_NET_BIND_SERVICE, theCAP_NET_RAW, theCAP_SETGID, theCAP_SETUID, and the others: the ~14's default's set). The--cap-drop=ALL --cap-add=CAP_*'s options let the administrators' finer's control. - The seccomp's filter (the system calls' restrictions)
-
The seccomp's filter (the Lesson 12.6's
discussion): the container's runtime
installs the default's filter (the
Docker's and the Podman's default's
profiles: the ~44's blocked's system
calls, including the
mount, thereboot, thekexec_load, theinit_module, theptrace), so the container's processes' system calls are the restricted's set. The seccomp's filter is the containers' security's central's mechanism (the attack's surface's reduction: the kernel's vulnerabilities' exploitation is the harder). - The LSM's profiles (the mandatory access control)
-
The Linux's Security Modules (the
SELinux's and the AppArmor's: the Lesson
12.5's discussion) confine the container's
processes: the SELinux's
container_t's type (the container's processes' label) and thecontainer_file_t's type (the container's files' label), or the AppArmor'sdocker-default's profile (the container's processes' confinement). The LSM's profiles are the containers' security's additional's layer, and the observation is the reason the containers' security's configurations include the LSM's settings. - The user's namespace and the rootless's containers
-
The user's namespace (the
CLONE_NEWUSER) maps the container's root (the UID 0 inside) to the unprivileged's user (the UID 1000's mapping) on the host, so the container's root's privileges are the bounded (the host's kernel sees the unprivileged's user), and the container's escape gives the attacker only the unprivileged's user's privileges. The rootless's containers (the Podman's default) are the security's recommended's design for the unprivileged's users' workloads. - The
no-new-privileges's setting -
The
no_new_privs's bit (theprctl'sPR_SET_NO_NEW_PRIVS) prevents the setuid's and the setgid's programs and the file's capabilities from granting the additional's privileges, so the container's processes cannot escalate their privileges through the setuid's binaries. The Docker's and the Podman's--security-opt no-new-privileges's option sets the bit.
4.4 The OCI standards
The Open Container Initiative (the OCI) is the open-source's project (the Linux Foundation's, the 2015's) that defines the containers' standards. The OCI's specifications:
- The Runtime's Specification
-
The specification of the container's runtime's
interface: the container's configuration (the
config.json's file: the processes, the root's file system, the mounts, the namespaces, the cgroups, the capabilities, the seccomp's filters, the hooks), the lifecycle (thecreate, thestart, thekill, thedelete, thestate), and therunc's and thecrun's implementations. The specification's purpose: the runtimes' interoperability (the same'sconfig.jsonruns on the different's runtimes). - The Image's Specification
-
The specification of the container's image's
format: the manifest (the
manifest.json's file: the layers' list, the config's file, the digests), the config (theconfig.json's file: the environment's variables, the entrypoint, the command, the exposed's ports, the volumes, the labels), the layers (the tar's archives: the file system's changes), and the media's types (theapplication/vnd.oci.image.layer.v1.tar+gzip's and the others). The specification's purpose: the images' interoperability (the same's image runs on the different's runtimes and the different's registries). - The Distribution's Specification
-
The specification of the containers'
distribution: the registry's API (the
/v2/'s endpoints: the_catalog, the<name>/tags/list, the<name>/manifests/<reference>, the<name>/blobs/<digest>), the pull's and the push's flows, and the authentication's schemes. The specification's purpose: the registries' interoperability (the same's image is pushed to and pulled from the different's registries: the Docker Hub, the Quay, the GitHub's Container Registry, the Harbor, the cloud's providers').
The OCI's standards' design's observations:
-
The interoperability's value.
The OCI's standards made the containers the
interoperable: the same's image runs on the
different's runtimes (the
runc, thecrun, thecontainerd, theCRI-O), the different's engines (the Docker, the Podman, the Kubernetes'skubelet), and the different's registries. The observation: the interoperability is the containers' ecosystem's foundation, and the observation is the reason the OCI's specifications are the containers' authority. - The standards' separation of the concerns. The Runtime's Specification separates the runtime (the container's creation and the execution) from the engine (the image's management, the networking, the storage, the orchestration), and the Image's Specification separates the image's format from the image's content. The observation: the separation of the concerns enabled the ecosystem's diversity (the many's runtimes, the many's engines, the many's registries).
4.5 The container's runtimes
The container's runtimes are the software that creates and runs the containers (the OCI's Runtime's Specification's implementations):
-
The low-level's runtimes (the
runc, thecrun). The low-level's runtimes create the container (the namespaces, the cgroups, the root's file system, the capabilities, the seccomp's filters) from the OCI's configuration and the image's layers. Therunc(the Go's implementation: the Docker's and the containerd's default) and thecrun(the C's implementation: the Podman's default, the faster and the smaller) are the classical's implementations, and the observation: the low-level's runtimes are the short-lived (they create the container and exit, and the container's processes run on the host's kernel). -
The high-level's runtimes (the
containerd, theCRI-O). The high-level's runtimes manage the container's lifecycle (the image's pulling, the container's creation, the container's start, the container's stop, the image's snapshots, the network's management) and delegate the container's creation to the low-level's runtimes (therunc, thecrun). Thecontainerd(the Docker's contribution: the containerd's project) and theCRI-O(the Red Hat's contribution: the Kubernetes's CRI's implementation) are the classical's implementations. -
The CRI (the Container Runtime
Interface). The Kubernetes's
interface (the CRI) that the
high-level's runtimes implement, so the
Kubernetes's
kubeletcan use the different's runtimes (thecontainerd, theCRI-O, theDocker's via thedockershim's compatibility's layer, the deprecated in the Kubernetes 1.24's). The observation: the CRI is the Kubernetes's and the runtimes' interface's standard, and the observation is the reason the Kubernetes's clusters can use the different's runtimes.
4.6 The container's images
The container's image is the packaged's file system: the application and its dependencies, the configuration, and the metadata. The image's structure:
-
The layers. The image is the
stack of the read-only's layers: each
layer is the file system's changes (the
added, the modified, the deleted files)
relative to the previous's layer. The
typical's image: the base's layer (the
operating system's file system: the
ubuntu:22.04's, thealpine:3.18's, thedebian:12's), the dependencies' layers (the runtimes: the Python's, the Node.js's, the Java's), and the application's layer (the application's code). The layers' design's advantages: the storage's efficiency (the layers are the shared between the images: the same's base's layer is the stored the once), the fast's distribution (the layers are the cached and the transferred the incrementally), and the reproducibility (the image's digest identifies the exact's contents). -
The
overlayfs's mounting. The container's runtime mounts the image's layers with the union file system (theoverlayfs's lower's directories: the read-only's layers; the upper's directory: the writable's layer; the work's directory: the overlayfs's internal's work; the merged's directory: the container's view). The container's writes go to the upper's directory (the writable's layer), and the container's reads see the merged's view. The observation: the union file system's design is the containers' storage's efficiency's and the isolation's mechanism. - The registries. The registries are the images' repositories: the Docker Hub (the public's), the Quay.io, the GitHub's Container Registry, the GitLab's Container Registry, the AWS's ECR, the Azure's ACR, the Google's Artifact Registry, and the enterprises' private's registries (the Harbor, the Nexus, the Artifactory). The registries' functions: the images' storage, the images' distribution (the pull's and the push's), the images' security's scanning (the vulnerabilities' detection), the images' signing (the content's trust: the Notary's, the Cosign's), and the access's control.
-
The image's naming and the digests.
The image's reference: the
registry/namespace/name:tag's format (thedocker.io/library/ubuntu:22.04's, theghcr.io/myorg/myapp:v1.0.0's), and the image's digest (thesha256:...'s: the content-addressable's identifier: the image's exact's contents). The observation: the tags are the mutable (the same's tag can point to the different's digests) and the digests are the immutable (the exact's contents), and the observation is the reason the production's deployments should use the digests, not the tags.
4.7 The container's lifecycle
The container's lifecycle's stages:
- The image's pulling. The engine (the Docker, the Podman, the containerd) pulls the image from the registry (the layers are the downloaded and the cached), and the image is the stored locally.
-
The container's creation. The
engine creates the container (the
create's operation): the runtime (therunc, thecrun) sets up the container's environment (the namespaces, the cgroups, the root's file system (the overlayfs's mount), the capabilities, the seccomp's filters, the LSM's profiles), and the container's state is the created. -
The container's start. The
engine starts the container (the
start's operation): the container's process (the entrypoint's command) runs, and the container's state is the running. - The container's execution. The container's processes run, and the container's state includes the running's processes, the resource's usage (the cgroups' metrics), the file system's changes (the writable's layer), and the network's connections.
-
The container's pause and the
resume. The engine can pause the
container (the
pause's operation: the cgroup's freezer's controller suspends the processes) and resume it (theunpause's operation). The pause's use's cases: the snapshots, the checkpoint and the restore (the CRIU's), the debugging. -
The container's stop. The
engine stops the container (the
stop's operation: the SIGTERM's signal, the grace's period, the SIGKILL's signal), and the container's state is the exited. The container's exit's code and the logs are the recorded. -
The container's removal. The
engine removes the container (the
rm's operation): the writable's layer is the deleted, the container's resources (the cgroups, the namespaces, the network's interfaces) are the released, and the container's state is the removed. The observation: the container's removal does not affect the image (the layers are the shared), and the observation is the reason the containers are the ephemeral (the stateless's design: the state is the volumes' or the external's services').
4.8 The container's security
The container's security is the layered's design (the Lesson 12.6's sandboxing's discussion applied to the containers), and its considerations:
-
The isolation's strength. The
containers' isolation is the kernel's
mechanisms' (the namespaces and the
cgroups), and the containers' security
depends on the kernel's security: the
kernel's vulnerability (the
Dirty COW, theDirty Pipe, theOverlayFS's bugs, theio_uring's bugs) can be the container's escape's vector. The observation: the containers' shared's kernel is the VMs' separate's kernel's security's difference, and the observation is the reason the high-security's multi-tenancy uses the VMs (or the VM's and the container's combination: the Kata Containers, the gVisor, the Firecracker). -
The attack surface. The
containers' attack surface: the
container's processes (the application's
vulnerabilities: the SQL's injection, the
command's injection, the deserialization),
the container's image (the base's image's
vulnerabilities, the dependencies'
vulnerabilities, the secrets' leakage),
the runtime (the
runc's, thecrun's, thecontainerd's vulnerabilities: theCVE-2019-5736's), the engine (the Docker's, the Podman's), the orchestration (the Kubernetes's RBAC, the network's policies), and the host's kernel. -
The container's escapes. The
container's escape is the attacker's
exploit of the kernel's, the runtime's,
or the configuration's vulnerability to
break the isolation: the classical's
examples: the
CVE-2019-5736(therunc's: the container's process overwrites the host'sruncbinary), theCVE-2020-15257(thecontainerd's: the abstract's Unix's socket's exposure), theCVE-2021-25741(the Kubernetes's subpath's: thevolumeMounts'ssubPath's traversal), theCVE-2022-0185(the Linux's kernel'slegacy_parse_param's heap's overflow), theCVE-2022-0492(the Linux's kernel's cgroups'srelease_agent's), and theCVE-2022-0847(theDirty Pipe's). The escapes' mitigations: the timely's patching (the kernel's, the runtime's, the engine's), the minimal's configurations (the dropped's capabilities, the restrictive's seccomp's profiles, the read-only's file systems), the rootless's containers, the user's namespaces, the LSM's profiles, the monitoring (the runtime's security's tools: the Falco, the Sysdig, the Tetragon), and the VM's and the container's combination (the Kata Containers, the gVisor, the Firecracker). -
The security's configurations.
The containers' security's best's
practices: the non-root's user (the
USER's directive in the Dockerfile, the--user's option), the read-only's root's file system (the--read-only's option with the tmpfs's mounts for the writable's paths), the dropped's capabilities (the--cap-drop=ALLand the--cap-add='s specific's capabilities), the seccomp's profiles (the default's or the custom's), the LSM's profiles, theno-new-privileges's setting, the minimal's images (thedistroless's, thealpine's, thescratch's: the smaller's attack surface), the images' scanning (the Trivy's, the Clair's, the Grype's), the secrets' management (the Kubernetes's secrets, the HashiCorp's Vault, the external's secret's stores: thesealed-secrets's, theexternal-secrets's), and the runtime's security's tools (the Falco's, the Sysdig's, the Tetragon's).
4.9 The containers' use's cases
The containers' use's cases:
- The microservices. The containers are the microservices' architecture's natural's unit: the each service is the container (the single's responsibility, the independent's deployment, the independent's scaling), and the observation is the reason the microservices and the containers are the synonyms in the modern's architectures.
- The CI/CD's pipelines. The containers are the CI/CD's pipelines' natural's unit: the build's environments (the consistent's toolchains), the test's environments (the isolated's testing, the reproducible's configurations), and the deployment's artifacts (the images: the same's artifact from the build to the production). The observation: the containers' consistency (the "it works on my machine" problem's solution) is the CI/CD's pipelines' central's benefit.
- The serverless's platforms. The containers are the serverless's platforms' unit (the AWS's Lambda, the Google's Cloud Functions, the Azure's Functions, the Cloudflare's Workers, the Knative's, the OpenFaaS's): the platform's runtime runs the function as the container (the microVM's or the container's isolation), and the observation: the serverless's platforms hide the containers' management but the containers are the underlying's unit.
- The cloud's native's applications. The containers are the cloud's native's applications' unit (the Kubernetes's clusters, the managed's services: the AWS's ECS and EKS, the Google's GKE, the Azure's AKS, the Red Hat's OpenShift): the applications are the containers, and the platforms manage the deployment, the scaling, the networking, the storage, and the security.
- The development and the testing. The containers are the development's and the testing's environments: the developers run the containers (the consistent's environments: the same's image on the laptop and the CI's server), the testers run the containers (the isolated's tests, the reproducible's results), and the trainers run the containers (the pre-configured's environments).
- The batch's jobs and the data's processing. The containers are the batch's jobs' and the data's processing's unit (the ML's training's jobs, the ETL's pipelines, the scheduled's tasks): the containers package the jobs' dependencies, and the platforms (the Kubernetes's jobs and the CronJobs, the AWS's Batch, the Google's Cloud Run's jobs) manage the execution.
4.10 The containers' and the VMs' trade-offs
The containers' and the VMs' trade-offs:
| The dimension | The containers' advantage | The VMs' advantage |
|---|---|---|
| The density | The hundreds to the thousands per host (the MBs of the memory per container) | The tens per host (the GBs of the memory per VM) |
| The startup's time | The milliseconds (the process's start) | The seconds (the kernel's boot) |
| The isolation | The weaker (the shared's kernel: the kernel's vulnerabilities affect the every container) | The stronger (the separate's kernels: the hypervisor's boundary) |
| The operating systems | The same's kernel (the Linux's containers on the Linux's host) | The different's operating systems (the Linux, the Windows, the BSD) |
| The portability | The images (the OCI's standards: the layers, the registries) | The disk's images (the OVA, the QCOW2, the VMDK: the larger, the slower) |
| The resource's usage | The efficient (the shared's kernel: the low's overhead) | The higher (the separate's kernels: the GBs per VM) |
| The security's boundary | The kernel's mechanisms (the namespaces, the cgroups, the seccomp, the LSM: the layered's defense) | The hypervisor's boundary (the hardware's virtualization: the stronger's isolation) |
| The typical use | The microservices, the CI/CD, the serverless, the cloud's native's applications | The different's OS, the legacy's applications, the strong's isolation's requirements |
The trade-offs' observations:
- The complementary's character. The containers and the VMs are the complementary, not the alternatives: the containers' advantages (the density, the startup, the resource's usage) and the VMs' advantages (the isolation, the different's OS) are the different's trade-offs, and the modern's systems combine the two (the containers inside the VMs: the Kubernetes's nodes are the VMs that run the containers; the Kata Containers: the containers' interfaces with the microVMs' isolation; the Firecracker's microVMs: the serverless's and the container's workloads).
- The workloads' matching. The workloads' characteristics determine the choice: the microservices (the many's small's services: the containers), the legacy's applications (the old's operating systems: the VMs), the multi-tenant's environments (the untrusted's code: the VMs' or the microVMs' isolation), the CI/CD's pipelines (the containers), the different's OS's requirements (the VMs). The observation: the choice depends on the workload's requirements (the isolation's strength, the operating system, the density, the startup's time), and the modern's platforms support the both.
- The security's considerations. The multi-tenant's environments (the untrusted's code, the different's customers) require the stronger's isolation (the VMs or the microVMs), and the single-tenant's environments (the trusted's code, the same's organization) can use the containers. The observation: the security's considerations are the multi-tenant's environments' decisive's factor, and the observation is the reason the cloud's providers offer the both (the VMs: the EC2's instances, the GCE's VMs; the containers: the ECS's, the EKS's, the GKE's, the AKS's services).
4.11 The containers' trends and the future
The containers' trends and the future:
-
The container's and the VM's
convergence. The modern's systems
blur the containers' and the VMs'
boundaries: the Kata Containers (the
containers' interfaces with the microVMs'
isolation: the OCI's runtime with the
VM's boundary), the gVisor (the
user-space's kernel: the application's
kernel's emulation in the user-space),
the Firecracker (the microVMs: the
serverless's and the container's
workloads), the KubeVirt (the VMs'
management in the Kubernetes's clusters),
the
libkrun's and thecrun's VM's support, and the WebAssembly's sandboxes (thewasmtime's, thewasmedge's: the portable's binaries with the sandbox's isolation). - The rootless's containers and the user's namespaces. The rootless's containers (the Podman's default, the Docker's rootless's mode) use the user's namespace to run the containers as the unprivileged's users, and the observation: the rootless's containers are the security's recommended's design for the unprivileged's users' workloads (the container's root is the bounded, and the container's escape gives the attacker only the unprivileged's user's privileges).
-
The WebAssembly's containers.
The WebAssembly (the WASM) is the
portable's binary's format with the
sandbox's isolation, and the WASM's
runtimes (the
wasmtime, thewasmer, thewasmedge, thewasmcloud) provide the container's alternatives: the portable's binaries (the cross-platform, the cross-architecture), the fast's startup (the microseconds), the low's memory's overhead (the KBs), and the strong's sandbox's isolation. The observation: the WASM's containers are the emerging's alternative for the specific's workloads (the edge's computing, the serverless's functions, the plugin's systems), and the observation is the reason the WASM's ecosystem is the active's area. -
The confidential containers.
The confidential computing's mechanisms
(the AMD's SEV, the Intel's TDX, the
ARM's CCA) and the containers: the
confidential's containers (the
enclave-cc's, theocclum's, thegramine's) protect the containers' memory from the host's operators, and the observation: the confidential's containers are the regulated's workloads' (the healthcare, the finance, the government) and the multi-tenant's clouds' requirements. - The serverless's containers. The serverless's platforms (the AWS's Lambda, the Google's Cloud Run, the Azure's Container Apps, the Cloudflare's Workers, the Knative's) run the containers as the functions, and the observation: the serverless's containers are the containers' abstraction's extreme's case (the developers package the containers, and the platforms manage the everything: the deployment, the scaling, the networking, the security), and the observation is the reason the serverless's platforms are the modern's computing's central's trend.
- The supply chain's security. The containers' supply chain's security: the images' signing (the Notary's, the Cosign's, the Sigstore's), the images' attestation (the SLSA's, the in-toto's), the SBOM's generation (the Software's Bill's of Materials: the Syft's, the Trivy's, the SPDX's), the vulnerabilities' scanning (the Trivy's, the Grype's, the Clair's), and the admission's controllers (the Kubernetes's OPA's Gatekeeper, the Kyverno's, the Sigstore's policy's controller). The observation: the supply chain's security is the containers' security's emerging's concern, and the observation is the reason the modern's platforms include the supply chain's security's tools.
4.12 Common misconceptions
"The containers are the lightweight's VMs."
The containers and the VMs are the different's technologies: the VMs run the separate's kernels on the virtualized's hardware (the hypervisor's isolation), and the containers run the isolated's processes on the shared's kernel (the namespaces' and the cgroups' isolation). The VMs' advantages: the strong's isolation (the separate's kernels), and the containers' advantages: the lightweight (the MBs of the overhead, the milliseconds of the startup, the hundreds per host). The observation: the containers are not the lightweight's VMs; they are the different's isolation's approach, and the modern's systems combine the two (the containers inside the VMs, the Kata Containers, the Firecracker's microVMs).
"The containers are the secure by definition."
The containers' isolation is the kernel's
mechanisms' (the namespaces and the
cgroups), and the containers' security
depends on the kernel's security and the
configuration: the kernel's vulnerabilities
are the container's escapes, the
misconfigurations (the --privileged's
flag, the docker.sock's
mount, the host's namespaces' sharing)
reduce the isolation, and the image's
vulnerabilities and the secrets' leakage
are the containers' attack surface. The
observation: the containers' security is
the layered's design (the minimal's
configurations, the rootless's
containers, the seccomp's profiles, the
LSM's profiles, the monitoring), and the
observation is the reason the container's
security is the ongoing's discipline.
"The container's image is the same as the container."
The image is the packaged's file system (the application and its dependencies: the read-only's layers), and the container is the running's instance (the image's layers plus the writable's layer, the processes, the namespaces, the cgroups). The same's image can run as the many's containers, and the container's state (the writable's layer, the processes) is the container's, not the image's. The observation: the container's removal does not affect the image, and the observation is the reason the containers are the ephemeral (the stateless's design).
"The containers are the always's faster."
The containers' startup is the milliseconds (the process's start) and the VMs' startup is the seconds (the kernel's boot), but the performance's comparison depends on the workload: the containers' I/O and the network's paths have the overhead (the user-space's processing, the virtual switches), and the VMs' passthrough's devices (the VFIO, the SR-IOV) achieve the near-native's performance. The observation: the containers' advantage is the density and the startup, not the performance for the every's workload, and the observation is the reason the modern's systems choose the containers or the VMs based on the workload's characteristics.
"The Docker is the containers."
The Docker is the engine (the user-friendly's tooling: the CLI, the daemon, the image's build, the registry) that popularized the containers, and the containers are the kernel's mechanisms (the namespaces, the cgroups, the union file systems) plus the OCI's standards (the Runtime's Specification, the Image's Specification). The observation: the containers exist without the Docker (the Podman, the containerd, the CRI-O, the Kubernetes's runtimes, the LXC), and the observation is the reason the containers' ecosystem is the OCI's standards' (the interoperability, not the Docker's proprietary's formats).
"The containers are the rootless by default."
The Docker's default runs the containers as the root (the daemon's default), and the rootless's containers (the Podman's default, the Docker's rootless's mode) use the user's namespace to run the containers as the unprivileged's users. The observation: the rootless's containers are the security's recommended's design, but the default's configurations vary (the Docker's daemon is the root), and the observation is the reason the administrators must verify and configure the containers' privileges.
"The containers' images are the single's binaries."
The container's image includes the
complete's file system: the base's layer
(the operating system's file system: the
ubuntu's, the
alpine's, the
debian's), the
dependencies' layers (the runtimes: the
Python's, the Node.js's, the Java's),
and the application's layer. The
observation: the images are the many's
layers (the MBs to the GBs), and the
observation is the reason the minimal's
images (the alpine's, the
distroless's, the
scratch's) are the
security's and the efficiency's best's
practices (the smaller's attack surface,
the faster's transfer).
"The container's data is the persistent."
The container's writable's layer is the ephemeral: the container's writes are the lost when the container is removed, and the observation is the reason the containers' persistent's data requires the volumes (the Docker's volumes, the Kubernetes's PersistentVolumes) or the external's services (the databases, the object's stores). The observation: the containers are the stateless's design, and the observation is the reason the modern's applications separate the state (the volumes, the external's services) from the compute (the containers).
05 Worked example
The following example traces a container's creation and execution through the container's mechanisms, inspects the container's resources, and demonstrates the container's and the VM's comparison.
Tracing a container's creation
The following commands create a container with the Podman's tool and inspect its mechanisms:
# Check the container's tools
which podman runc crun 2>/dev/null
podman --version 2>/dev/null
podman info 2>/dev/null | head -30
# Pull an image
podman pull docker.io/library/alpine:3.18 2>/dev/null
# List the images
podman images 2>/dev/null
# Inspect the image's layers
podman inspect docker.io/library/alpine:3.18 2>/dev/null | \
python3 -c "import sys, json; data = json.load(sys.stdin); print(json.dumps(data[0]['RootFS'], indent=2))"
# Create and run a container
podman run -d --name demo-container \
--memory 128m --cpus 0.5 \
--security-opt no-new-privileges \
docker.io/library/alpine:3.18 \
sleep 3600
# Inspect the container
podman inspect demo-container 2>/dev/null | head -60
# The container's state
podman ps 2>/dev/null
podman stats --no-stream demo-container 2>/dev/null
# The container's processes
podman top demo-container 2>/dev/null
# The container's namespaces
CONTAINER_PID=$(podman inspect -f '{{.State.Pid}}' demo-container 2>/dev/null)
ls -la /proc/$CONTAINER_PID/ns/
# The container's cgroup's limits
cat /sys/fs/cgroup/machine.slice/libpod-*.scope/memory.max 2>/dev/null
cat /sys/fs/cgroup/machine.slice/libpod-*.scope/cpu.max 2>/dev/null
# The container's capabilities
sudo nsenter -t $CONTAINER_PID -a capsh --print 2>/dev/null | head -10
# The container's seccomp's status
sudo grep Seccomp /proc/$CONTAINER_PID/status 2>/dev/null
# The container's file system
podman exec demo-container ls -la /
podman exec demo-container mount | head -10
# The container's logs
podman logs demo-container 2>/dev/null
# The container's cleanup
podman rm -f demo-container 2>/dev/null
The container's inspection's findings:
-
The image's layers. The
alpine:3.18's image has the single's layer (theRootFS'sLayers's array: thesha256:...'s digest), and the observation: the minimal's images (thealpine's: the ~5 MB) have the fewer's layers, and the observation is the reason the minimal's images are the efficiency's and the security's best's practices. -
The container's namespaces.
The
/proc/$CONTAINER_PID/ns/'s directory shows the container's namespaces (the mount, the PID, the network, the user, the IPC, the UTS, the cgroup, the time), and the observation: the namespaces' identifiers differ from the host's (the container's isolated views). -
The container's cgroup's limits.
The
memory.max's value is the 134217728 (the 128 MB), and thecpu.max's value is the "50000 100000" (the 50% of the one CPU), and the observation: the cgroups' limits enforce the container's resource's usage (the--memory 128m's and the--cpus 0.5's options). -
The container's capabilities.
The
capsh --print's output shows the container's capabilities (the default's set: the ~14's capabilities), and the observation: the container's capabilities are the restricted (the root's whole's privileges are the dropped). -
The container's seccomp's
status. The
/proc/$CONTAINER_PID/status'sSeccomp's field is the2(the filter's mode), and the observation: the container's seccomp's filter is the active (the default's profile).
The container's file system's layers
The following commands inspect the container's overlayfs's mounting:
# The container's mounts
podman exec demo-container mount | grep overlay
# The overlayfs's layers
CONTAINER_PID=$(podman inspect -f '{{.State.Pid}}' demo-container 2>/dev/null)
sudo cat /proc/$CONTAINER_PID/mountinfo 2>/dev/null | grep overlay
# The overlayfs's directories
sudo find /var/lib/containers/storage/overlay/ -maxdepth 2 -type d 2>/dev/null | head -20
# The container's writable's layer
podman exec demo-container touch /test-file
sudo find /var/lib/containers/storage/overlay/ -name 'test-file' 2>/dev/null
# The container's image's layers
podman image tree docker.io/library/alpine:3.18 2>/dev/null | head -20
# The container's file system's changes
podman diff demo-container 2>/dev/null
# The container's commit (the new image from the changes)
podman commit demo-container my-alpine-modified 2>/dev/null
podman images | grep my-alpine
The overlayfs's inspection's findings: the
container's mounts show the
overlay's file system with
the lowerdir's (the image's
layers), the upperdir's (the
writable's layer), the
workdir's, and the
merged's directory. The
container's writes go to the
upperdir, and the
podman diff's output shows
the container's file system's changes (the
added, the modified, the deleted files)
relative to the image. The observation:
the union file system's design is the
containers' storage's efficiency's and
the isolation's mechanism (the image's
layers are the shared and the read-only,
and the container's writes are the
contained).
The container's and the VM's comparison
The following commands compare a container and a VM's resource's usage:
| The aspect | The container (the Alpine's) | The VM (the Ubuntu's) |
|---|---|---|
| The image's size | The ~5 MB (the alpine:3.18's) |
The ~500 MB (the ubuntu:22.04's) |
| The memory's overhead | The ~2 MB (the container's process) | The ~200 MB (the guest's kernel and the OS) |
| The startup's time | The ~100 ms (the process's start) | The ~10 s (the kernel's boot and the OS's init) |
| The density per host | The hundreds to the thousands (the 100 GB of the RAM) | The tens (the 100 GB of the RAM) |
| The isolation | The kernel's mechanisms (the shared's kernel) | The hypervisor's boundary (the separate's kernels) |
| The operating systems | The same's kernel (the Linux's) | The different's OS (the Linux, the Windows, the BSD) |
The comparison's observations: the containers' advantages (the smaller's images, the lower's memory's overhead, the faster's startup, the higher's density) are the reasons the containers are the modern's microservices' and the CI/CD's natural's unit, and the VMs' advantages (the stronger's isolation, the different's OS) are the reasons the VMs remain the multi-tenant's and the legacy's workloads' choice. The observation: the containers and the VMs are the complementary, and the modern's systems combine the two (the containers inside the VMs, the Kata Containers, the Firecracker's microVMs).
Comparing the runtimes
The following commands compare the container's runtimes:
# The runc's version
runc --version 2>/dev/null
crun --version 2>/dev/null
# The runtime's binary's size
ls -lh $(which runc) $(which crun) 2>/dev/null
# The runtime's creation of a container (the OCI's bundle)
mkdir -p /tmp/oci-bundle/rootfs
cd /tmp/oci-bundle
skopeo copy docker://docker.io/library/alpine:3.18 \
oci:/tmp/oci-image:latest 2>/dev/null
skopeo copy oci:/tmp/oci-image:latest \
dir:/tmp/oci-bundle/rootfs 2>/dev/null
runc spec 2>/dev/null
cat config.json | python3 -m json.tool | head -30
# The runc's container's creation
sudo runc run demo-oci 2>/dev/null &
sleep 2
sudo runc list 2>/dev/null
sudo runc kill demo-oci KILL 2>/dev/null
# Clean up
rm -rf /tmp/oci-bundle /tmp/oci-image 2>/dev/null
The runtimes' comparison: the
crun's binary is the smaller
(the C's implementation: the ~500 KB
versus the runc's ~10 MB's
Go's binary), and the observation: the
crun's design is the faster
and the smaller (the Podman's default),
and the runc's design is the
Go's (the Docker's and the containerd's
default). The observation: the OCI's
Runtime's Specification makes the two
runtimes the interchangeable (the same's
config.json and the same's
rootfs run on the both), and the
observation is the reason the OCI's
standards are the containers' ecosystem's
foundation.
Observing the container's security on a live system
The following commands inspect the container's security's configuration:
# The container's security's options
podman inspect demo-container 2>/dev/null | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
c = data[0]
print('The capabilities:', c['EffectiveCaps'])
print('The seccomp:', c['HostConfig']['SecurityOpt'])
print('The user:', c['Config']['User'])
print('The read-only:', c['HostConfig']['ReadonlyRootfs'])
print('The privileged:', c['HostConfig']['Privileged'])
"
# The container's user's namespace's mapping
sudo cat /proc/$CONTAINER_PID/uid_map 2>/dev/null
sudo cat /proc/$CONTAINER_PID/gid_map 2>/dev/null
# The container's LSM's labels
sudo cat /proc/$CONTAINER_PID/attr/current 2>/dev/null
# The container's seccomp's filters
sudo cat /proc/$CONTAINER_PID/status | grep Seccomp
# The container's security's tools
which falco sysdig tetragon 2>/dev/null
which trivy grype syft 2>/dev/null
# The container's image's scanning (the demonstration)
trivy image docker.io/library/alpine:3.18 2>/dev/null | head -30
The container's security's inspection's
findings: the container's capabilities
(the default's set: the ~14's
capabilities), the security's options
(the seccomp's filter, the
no-new-privileges's
setting), the user's namespace's mapping
(the container's root maps to the host's
unprivileged's user: the rootless's
containers), the LSM's labels (the
SELinux's container_t's or
the AppArmor's profile), and the seccomp's
filters (the filter's mode). The image's
scanning (the Trivy's) reports the
vulnerabilities in the image's packages,
and the observation: the container's
security's configuration's inspection and
the image's scanning are the containers'
security's operations's first steps.
06 Interactive activity
Activity · explore the containers on a live system
The goal of this activity is to use the Linux command line to inspect the container's mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp), to create and manage a container, and to compare the containers and the VMs. Work through the steps in your course VM.
-
Check the container's tools.
Install and verify the container's tools
(the Podman, the Docker, the buildah, the
skopeo).
Which container's tools are available? Is the Podman or the Docker installed? Which runtime is the default? Write down what you find.# Check the tools which podman docker runc crun buildah skopeo 2>/dev/null # Install the tools (adjust to the distribution) sudo apt install -y podman buildah skopeo 2>/dev/null || \ sudo dnf install -y podman buildah skopeo 2>/dev/null # Verify the installation podman --version 2>/dev/null podman info 2>/dev/null | head -20 podman system info 2>/dev/null | head -20 # The runtimes runc --version 2>/dev/null crun --version 2>/dev/null # The OCI's configuration ls /usr/share/containers/ 2>/dev/null cat /etc/containers/registries.conf 2>/dev/null | head -20 -
Pull and inspect an image.
Pull an image and inspect its layers and
its manifest.
How many layers does the image have? What is the image's size? What is the base's image? How does the image's size compare with a full's operating system's image (the# Pull the image podman pull docker.io/library/alpine:3.18 2>/dev/null # The image's information podman images 2>/dev/null podman inspect docker.io/library/alpine:3.18 2>/dev/null | head -40 # The image's layers podman image tree docker.io/library/alpine:3.18 2>/dev/null # The image's manifest skopeo inspect docker://docker.io/library/alpine:3.18 2>/dev/null | head -30 # The image's history podman history docker.io/library/alpine:3.18 2>/dev/null # The image's layers' sizes podman inspect docker.io/library/alpine:3.18 2>/dev/null | \ python3 -c " import sys, json data = json.load(sys.stdin) c = data[0] print('The layers:', c['RootFS']['Layers']) print('The size:', c['Size']) "ubuntu:22.04's)? -
Create and run a container.
Create a container with the resource's
limits and inspect its state.
What is the container's state (the running, the exited)? What are the container's resource's limits? What are the container's processes? What does the container's# Create and run the container podman run -d --name demo-container \ --memory 128m --cpus 0.5 \ --security-opt no-new-privileges \ docker.io/library/alpine:3.18 \ sleep 3600 2>/dev/null # The container's state podman ps 2>/dev/null podman inspect demo-container 2>/dev/null | head -60 # The container's resource's usage podman stats --no-stream demo-container 2>/dev/null # The container's processes podman top demo-container 2>/dev/null # The container's logs podman logs demo-container 2>/dev/null # The container's exec podman exec demo-container ps aux 2>/dev/null podman exec demo-container cat /etc/os-release 2>/dev/null/etc/os-releaseshow? How does the container's environment differ from the host's? -
Inspect the container's
mechanisms.
Examine the container's namespaces, the
cgroups, the file system's layers, the
capabilities, and the seccomp.
What are the container's namespaces' identifiers? How do they differ from the host's? What are the cgroups' limits? What are the container's capabilities? Is the seccomp's filter active (the mode 2)? What is the user's namespace's mapping? Write down what you find.# The container's PID CONTAINER_PID=$(podman inspect -f '{{.State.Pid}}' demo-container 2>/dev/null) echo "The container's PID: $CONTAINER_PID" # The container's namespaces ls -la /proc/$CONTAINER_PID/ns/ readlink /proc/$CONTAINER_PID/ns/mnt readlink /proc/$CONTAINER_PID/ns/pid readlink /proc/$CONTAINER_PID/ns/net readlink /proc/$CONTAINER_PID/ns/user # The container's cgroup's limits cat /sys/fs/cgroup/machine.slice/libpod-*.scope/memory.max 2>/dev/null cat /sys/fs/cgroup/machine.slice/libpod-*.scope/cpu.max 2>/dev/null cat /sys/fs/cgroup/machine.slice/libpod-*.scope/pids.max 2>/dev/null # The container's capabilities sudo nsenter -t $CONTAINER_PID -a capsh --print 2>/dev/null | head -10 # The container's seccomp's status sudo grep Seccomp /proc/$CONTAINER_PID/status 2>/dev/null # The container's user's namespace's mapping sudo cat /proc/$CONTAINER_PID/uid_map 2>/dev/null sudo cat /proc/$CONTAINER_PID/gid_map 2>/dev/null # The container's LSM's labels sudo cat /proc/$CONTAINER_PID/attr/current 2>/dev/null -
Inspect the container's file
system.
Examine the container's overlayfs's
mounting and the image's layers.
How does the container's file system appear (the overlayfs's mount)? What are the image's layers and the writable's layer? What does the# The container's mounts podman exec demo-container mount | head -10 # The overlayfs's mounting's details CONTAINER_PID=$(podman inspect -f '{{.State.Pid}}' demo-container 2>/dev/null) sudo cat /proc/$CONTAINER_PID/mountinfo 2>/dev/null | grep overlay # The container's writable's layer podman exec demo-container touch /test-file podman exec demo-container ls -la / podman diff demo-container 2>/dev/null # The image's layers' directory sudo ls /var/lib/containers/storage/overlay/ 2>/dev/null | head -10 # The container's file system's size podman exec demo-container df -h / 2>/dev/null # The container's writable's layer's size sudo du -sh /var/lib/containers/storage/overlay/*/diff 2>/dev/null | head -10podman diff's output show? How does the union file system's design work? -
Test the container's isolation.
Test the container's isolation by trying
the privileged's operations and the
host's resources' access.
Did the container's isolation hold? Which operations succeeded, and which failed? How do the container's mechanisms (the namespaces, the capabilities, the seccomp) enforce the isolation? Write down what you find.# Test the process's isolation podman exec demo-container ps aux 2>/dev/null # The container sees only its processes (the PID's namespace) # Test the file system's isolation podman exec demo-container ls /home 2>/dev/null podman exec demo-container ls /host 2>/dev/null # The container sees only its file system (the mount's namespace) # Test the network's isolation podman exec demo-container ip addr 2>/dev/null podman exec demo-container ping -c 1 8.8.8.8 2>/dev/null # The container has its own network (the network's namespace) # Test the capability's restriction podman exec demo-container mount -t proc proc /proc 2>/dev/null # The mount's operation fails (the CAP_SYS_ADMIN's absence) # Test the user's isolation podman exec demo-container id 2>/dev/null # The container's root (the UID 0 inside) is the unprivileged's user on the host # Test the seccomp's filter podman exec demo-container reboot 2>/dev/null # The reboot's system call fails (the seccomp's filter) -
Compare the containers and the
VMs.
Compare the container's and the VM's
resource's usage (if a VM is available on
the system).
How long did the container's start take? What is the container's memory's usage? How does the comparison with the VM illustrate the containers' and the VMs' trade-offs?# The container's resource's usage podman stats --no-stream demo-container 2>/dev/null # The container's process's memory cat /proc/$(podman inspect -f '{{.State.Pid}}' demo-container 2>/dev/null)/status 2>/dev/null | grep -E 'VmRSS|VmSize' # The container's start's time (the demonstration) time podman run --rm docker.io/library/alpine:3.18 echo "Hello, container" 2>/dev/null # The VM's comparison (if a VM is available) virsh list --all 2>/dev/null virsh dominfo demo-vm 2>/dev/null virsh dommemstat demo-vm 2>/dev/null # The comparison's metrics echo "=== The comparison ===" echo "The container's start's time: the milliseconds" echo "The VM's start's time: the seconds" echo "The container's memory: the MBs" echo "The VM's memory: the GBs" -
Read the man pages.
Read the man pages that document the
containers' interfaces.
For each, write a sentence describing its main contribution to your understanding of the containers. Theman 1 podman man 1 podman-run man 1 podman-exec man 1 podman-build man 1 buildah man 1 skopeo man 1 runc man 1 crun man 1 ctr man 1 crictl man 7 namespaces man 7 cgroups man 5 overlay man 2 clone man 1 unshare man 1 nsenter man 1 systemd-nspawn man 1 lsnsman 1 podmanpage documents the Podman's container's management; theman 7 namespacespage documents the Linux's namespaces; theman 5 overlaypage documents the overlayfs's union file system.
Reflect: the containers are the modern's computing's most visible abstraction, and their design's trade-offs (the density, the startup's speed, the resource's usage versus the isolation's strength) are the reasons the modern's systems combine the containers with the virtual machines. The containers' mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM) are the kernel's features, and the OCI's standards (the Runtime's Specification, the Image's Specification, the Distribution's Specification) are the containers' ecosystem's foundation. Which of the containers' mechanisms is the most important for the modern's workloads, and why? What would you change about the containers' design if you could? How would you explain the containers' and the VMs' difference to someone who has never thought about it? What is the right balance between the containers' lightweight and the VMs' isolation?
07 Knowledge check
Check your understanding
Question 1
Define the container and explain the container versus the virtual machine's distinction. What are the containers' benefits and trade-offs, and when is each approach appropriate?
Show answer
Question 2
Describe the containers' history (the chroot, the BSD's jails, the Solaris's Zones, the Linux-VServer, the OpenVZ, the LXC, the Docker, the OCI, the Kubernetes). What was each stage's contribution, and why did the containers explode with the Docker?
Show answer
chroot's
system call changes the process's
root's directory: the file system's
view's restriction, but not the
security's boundary: the escapes are
the known); the BSD's
jails (the 2000: the
jail's system call
extends the chroot with the process's
isolation, the network's isolation,
and the user's isolation: the first's
complete's container's implementation);
the Solaris's Zones
(the 2004: the global's zone and the
non-global's zones, with the
isolated's views and the resource's
limits: the enterprise's container's
implementation); the Linux's
namespaces and cgroups (the
2002–2008: the kernel's mechanisms
(the namespaces: the mount's, the
UTS's, the IPC's, the PID's, the
network's, the user's, the cgroup's,
the time's; the cgroups: the process's
groups' resource's limits) that were
the containers' foundation); the
Linux-VServer and
the OpenVZ (the
2000s: the early's container's
implementations, the virtualized's
servers' kernels with the isolated's
users and the network); the
LXC (the 2008: the
user-space's tooling (the
lxc-*'s commands) that
combined the namespaces, the cgroups,
and the chroot into the container's
management's interface); the
Docker (the 2013:
the user-friendly's tooling (the
docker's CLI, the
Dockerfile's build's format, the
Docker Hub's registry) that made the
containers the accessible to the
developers, with the
image's concept, the
registry's concept, the
Dockerfile's build's format,
and the single's process's
container's model); the OCI
(the 2015: the standards (the
Runtime's Specification, the Image's
Specification, the Distribution's
Specification) that made the
containers the interoperable); and
the Kubernetes (the
2014: the orchestration's platform
(the containers' scheduling, the
scaling, the self-healing, the
service's discovery, the
configuration's management) that made
the containers the production's
workloads' platform). The containers
exploded with the Docker because the
Docker solved the usability
and the distribution
problems: the kernel's mechanisms
(the namespaces and the cgroups)
existed for the years, but the
Docker's images (the
packaged's application and its
dependencies), the Dockerfile
(the declarative's build's format),
and the Docker Hub (the
images' distribution's registry)
made the containers the accessible
to the ordinary's developers. The
observation: the containers'
explosion was the tooling's and the
ecosystem's driver, not the kernel's
mechanisms' (the observation is the
reason the containers' history is
the tooling's history).
Question 3
Describe the containers' mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM, the user's namespace). What is each mechanism's role, and how do the mechanisms together provide the container's isolation?
Show answer
cpu's, the
memory's, the
io's, the
pids's controllers);
the union file systems
(the overlayfs's: the
image's read-only's layers and the
container's writable's layer: the
packaged's file system and the
container's isolation); the
capabilities (the
Linux's capabilities: the root's
privileges' split: the container's
runtime drops the unnecessary's
capabilities and keeps the specific's:
the restricted's privileges); the
seccomp (the BPF's
filters: the system calls'
restrictions: the default's profiles
block the ~44's dangerous's system
calls: the attack's surface's
reduction); the LSM
(the SELinux's and the AppArmor's
profiles: the mandatory access
control's confinement: the
container_t's type, the
docker-default's
profile); and the user's
namespace (the
CLONE_NEWUSER: the
container's root (the UID 0 inside)
maps to the host's unprivileged's
user: the rootless's containers: the
bounded's privileges). The mechanisms
together provide the container's
isolation: the namespaces isolate the
processes' views (the
container cannot see the host's
processes, the host's files, the
host's network), the cgroups limit
the processes' usage (the
container cannot use more than the
cgroup's grant), the union file
system packages the container's
file system (the image's
layers plus the writable's layer),
the capabilities, the seccomp, and
the LSM restrict the container's
privileges (the specific's
capabilities, the system calls'
whitelist, the mandatory access
control's policy), and the user's
namespace bounds the
container's privileges (the
container's root is the unprivileged's
user on the host). The observation:
the container's isolation is the
layered's design (the defence in
depth), and the observation is the
reason the containers' security
depends on the combination
of the mechanisms, not the single's
mechanism.
Question 4
Describe the OCI standards (the Runtime's Specification, the Image's Specification, the Distribution's Specification) and the container's runtimes (the runc, the crun, the containerd, the CRI-O). What is each standard's and runtime's role, and how do they interact?
Show answer
config.json's file: the
processes, the root's file system, the
mounts, the namespaces, the cgroups,
the capabilities, the seccomp's
filters, the hooks), the lifecycle
(the create, the
start, the
kill, the
delete, the
state), and the
runc's and the
crun's implementations);
the Image's
Specification (the
container's image's format: the
manifest, the config, the layers, the
media's types: the OCI's images are
the interoperable: the same's image
runs on the different's runtimes and
the different's registries); and the
Distribution's
Specification (the
containers' distribution: the
registry's API (the
/v2/'s endpoints), the
pull's and the push's flows, the
authentication's schemes: the OCI's
registries are the interoperable: the
same's image is the pushed to and
pulled from the different's
registries). The container's
runtimes: the low-level's
runtimes (the
runc: the Go's
implementation, the Docker's and the
containerd's default; the
crun: the C's
implementation, the Podman's default,
the faster and the smaller): the
low-level's runtimes create the
container (the namespaces, the
cgroups, the root's file system, the
capabilities, the seccomp's filters)
from the OCI's configuration and the
image's layers, and they are the
short-lived (they create the
container and exit); the
high-level's runtimes
(the containerd: the
Docker's contribution, the
containerd's project; the
CRI-O: the Red Hat's
contribution, the Kubernetes's CRI's
implementation): the high-level's
runtimes manage the container's
lifecycle (the image's pulling, the
container's creation, the
container's start, the container's
stop, the image's snapshots, the
network's management) and delegate
the container's creation to the
low-level's runtimes (the
runc, the
crun); and the
CRI (the Container
Runtime Interface: the Kubernetes's
interface that the high-level's
runtimes implement, so the
Kubernetes's kubelet can
use the different's runtimes). The
interaction: the engine (the Docker,
the Podman, the Kubernetes's
kubelet) calls the
high-level's runtime (the
containerd, the
CRI-O), which pulls the
image (the OCI's Image's
Specification's format) from the
registry (the OCI's Distribution's
Specification's API), prepares the
container's configuration (the OCI's
Runtime's Specification's
config.json), and calls
the low-level's runtime (the
runc, the
crun) to create and
start the container. The observation:
the OCI's standards and the runtimes'
separation of the concerns (the
runtime's, the engine's, the
registry's) are the containers'
ecosystem's foundation, and the
observation is the reason the
containers are the interoperable (the
same's image runs on the different's
runtimes, the different's engines, and
the different's registries).
Question 5
Describe the containers' security (the isolation's strength, the attack surface, the escapes, the configurations) and the containers' use's cases (the microservices, the CI/CD, the serverless, the cloud's native's applications). What are the trade-offs and the trends?
Show answer
Dirty COW, the
Dirty Pipe, the
OverlayFS's bugs) can be
the container's escape's vector; the
observation: the containers' shared's
kernel is the VMs' separate's kernel's
security's difference, and the
observation is the reason the
high-security's multi-tenancy uses the
VMs or the VM's and the container's
combination); the attack
surface (the container's
processes (the application's
vulnerabilities), the container's
image (the base's image's
vulnerabilities, the dependencies',
the secrets' leakage), the runtime
(the runc's, the
crun's, the
containerd's
vulnerabilities: the
CVE-2019-5736's), the
engine (the Docker's, the Podman's),
the orchestration (the Kubernetes's
RBAC, the network's policies), and the
host's kernel); the escapes
(the attacker's exploit of the
kernel's, the runtime's, or the
configuration's vulnerability to break
the isolation: the classical's
examples: the
CVE-2019-5736 (the
runc's), the
CVE-2020-15257 (the
containerd's), the
CVE-2021-25741 (the
Kubernetes's subpath's), the
CVE-2022-0185 (the
Linux's kernel's), the
CVE-2022-0492 (the
cgroups's release_agent's),
the CVE-2022-0847 (the
Dirty Pipe's); the
mitigations: the timely's patching,
the minimal's configurations, the
rootless's containers, the user's
namespaces, the LSM's profiles, the
monitoring (the Falco's, the
Sysdig's, the Tetragon's), the VM's
and the container's combination); and
the configurations
(the non-root's user, the read-only's
root's file system, the dropped's
capabilities, the seccomp's profiles,
the LSM's profiles, the
no-new-privileges's
setting, the minimal's images, the
images' scanning, the secrets'
management, the runtime's security's
tools). The containers' use's cases:
the microservices
(the each service is the container:
the single's responsibility, the
independent's deployment, the
independent's scaling); the
CI/CD's pipelines
(the build's environments, the test's
environments, the deployment's
artifacts: the consistency, the
reproducibility); the
serverless's platforms
(the AWS's Lambda, the Google's Cloud
Functions, the Azure's Functions, the
Cloudflare's Workers, the Knative's,
the OpenFaaS's: the containers as the
functions); the cloud's
native's applications (the
Kubernetes's clusters, the managed's
services: the AWS's ECS and EKS, the
Google's GKE, the Azure's AKS, the Red
Hat's OpenShift); the
development and the
testing (the consistent's
environments: the same's image on the
laptop and the CI's server); and the
batch's jobs and the data's
processing (the ML's
training's jobs, the ETL's pipelines,
the scheduled's tasks). The
trade-offs: the containers'
lightweight (the density, the
startup's speed, the resource's
usage) versus the VMs' isolation (the
separate's kernels, the different's
OS); the shared's kernel's security's
risk versus the containers'
efficiency; the containers' ecosystem
(the OCI's standards, the Kubernetes's
orchestration) versus the containers'
complexity (the networking, the
storage, the security). The trends:
the container's and the VM's
convergence (the Kata Containers, the
gVisor, the Firecracker, the
KubeVirt), the rootless's containers
and the user's namespaces, the
WebAssembly's containers (the
wasmtime's, the
wasmer's, the
wasmedge's), the
confidential containers (the
enclave-cc's, the
occlum's, the
gramine's), the
serverless's containers, and the
supply chain's security (the images'
signing, the attestation, the SBOM's,
the vulnerabilities' scanning). The
observation: the containers are the
modern's computing's central's
abstraction, and their evolution
(the convergence with the VMs, the
WASM's alternatives, the confidential
computing, the supply chain's
security) is the modern's systems'
active's area.
08 Unit discussion
Discussion prompt
The containers are the modern's computing's most visible abstraction, and their history is the history of the isolation's evolution. The classical's chroot (the 1979's) was the file system's view's restriction; the BSD's jails (the 2000's) and the Solaris's Zones (the 2004's) were the first's complete's container's implementations; the Linux's namespaces and cgroups (the 2002–2008's) were the kernel's mechanisms that made the containers the possible; the LXC (the 2008's) was the tooling's combination; the Docker (the 2013's) was the user-friendly's tooling that made the containers the accessible; the OCI (the 2015's) was the standards that made the containers the interoperable; and the Kubernetes (the 2014's) was the orchestration that made the containers the production's workloads' platform. Each stage was the response to the previous's limitations, and each was shaped by the technology and the workloads of its time.
The trend is toward the convergence and the specialization. The containers and the VMs are the converging (the Kata Containers: the containers' interfaces with the microVMs' isolation; the gVisor: the user-space's kernel; the Firecracker: the microVMs; the KubeVirt: the VMs in the Kubernetes's clusters), and the observation is the reason the modern's platforms support the both. The containers' specializations are the emerging: the WebAssembly's containers (the portable's binaries with the sandbox's isolation), the confidential containers (the regulated's workloads' protection from the host's operators), the serverless's containers (the functions' abstraction), and the edge's containers (the IoT's and the embedded's devices).
The trend also raises a question about the abstraction's boundaries. The containers' abstraction (the image, the container, the registry) is the developer-friendly's, and the observation is the reason the containers are the modern's applications' unit. But the containers' abstraction hides the complexity (the kernel's mechanisms, the networking, the storage, the security), and the observation is the reason the containers' operations require the specialized's skills (the container's security, the orchestration, the observability). The observation: the containers' abstraction's simplicity (the developers' experience) and the containers' operations' complexity (the operators' challenges) are the two sides of the containers' design, and the observation is the reason the modern's platforms (the Kubernetes's, the cloud's) provide the managed's services (the abstraction's simplicity with the operations' outsourcing).
Which of the containers' mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM, the user's namespace) is the most important for the modern's workloads, and why? Should the containers' design prioritize the density (the lightweight, the fast's startup) or the isolation (the separate's kernels, the strong's boundaries)? What is the right balance between the containers' abstraction's simplicity and the operations' complexity? How should the containers' design evolve for the WebAssembly's alternatives, the confidential computing, and the edge's computing? Post your response to the Unit 13 discussion forum and reply to at least two classmates. Supports CLO1, CLO7, and CLO8.
09 Practice exercise
This ungraded exercise consolidates the lesson and prepares you for Lesson 13.4. Aim to spend 60–90 minutes on it.
-
Compare the containers and the
VMs.
Write a short essay (400–500 words)
comparing the containers and the virtual
machines. Your essay should address:
- The isolation's mechanisms (the shared's kernel vs. the separate's kernels).
- The resource's usage (the MBs vs. the GBs).
- The startup's time (the milliseconds vs. the seconds).
- The density (the hundreds vs. the tens per host).
- The security's characteristics (the kernel's mechanisms vs. the hypervisor's boundary).
- The portability (the OCI's images vs. the disk's images).
- The use's cases (the microservices, the CI/CD vs. the different's OS, the legacy's applications).
- The combinations (the containers inside the VMs, the Kata Containers, the Firecracker's microVMs).
-
Trace a container's
lifecycle.
A container's lifecycle includes the
image's pulling, the container's
creation, the start, the execution, the
pause, the stop, and the removal. For
each stage, describe:
- The components involved (the engine, the runtime, the kernel's mechanisms).
- The resources allocated or released.
- The state changes.
- The potential's failure's modes.
-
Design a container's
security's policy.
A company is containerizing its web
application. The application needs to
bind to the port 8080 (unprivileged),
to read the files in the
/app's directory, and to connect to the database on the network. Design the container's security's policy:- The user (the non-root's user, the user's namespace, the rootless's containers).
- The capabilities (the dropped's set, the added's set).
- The seccomp's profile (the default's or the custom's).
- The LSM's profile (the SELinux's, the AppArmor's).
- The file system (the read-only's root, the volumes).
- The cgroups' limits (the CPU, the memory, the I/O, the pids).
- The network (the network's namespace, the ports, the policies).
- The image (the minimal's base, the scanning, the signing).
- The monitoring (the runtime's security's tools).
-
Explore the containers.
On your VM, explore the containers'
mechanisms and the tools:
What are the container's mechanisms' states? How do the namespaces, the cgroups, the capabilities, and the seccomp enforce the container's isolation? Write down your observations.# The container's tools podman --version 2>/dev/null podman info 2>/dev/null | head -30 # Pull and inspect an image podman pull docker.io/library/alpine:3.18 2>/dev/null podman image tree docker.io/library/alpine:3.18 2>/dev/null # Create and inspect a container podman run -d --name demo-container \ --memory 128m --cpus 0.5 \ --security-opt no-new-privileges \ docker.io/library/alpine:3.18 sleep 3600 2>/dev/null podman inspect demo-container 2>/dev/null | head -60 # The container's namespaces CONTAINER_PID=$(podman inspect -f '{{.State.Pid}}' demo-container 2>/dev/null) ls -la /proc/$CONTAINER_PID/ns/ # The container's cgroup's limits cat /sys/fs/cgroup/machine.slice/libpod-*.scope/memory.max 2>/dev/null cat /sys/fs/cgroup/machine.slice/libpod-*.scope/cpu.max 2>/dev/null # The container's capabilities sudo nsenter -t $CONTAINER_PID -a capsh --print 2>/dev/null | head -10 # The container's seccomp's status sudo grep Seccomp /proc/$CONTAINER_PID/status 2>/dev/null # The container's file system podman exec demo-container mount | head -5 # The cleanup podman rm -f demo-container 2>/dev/null -
Build a container's image.
Build a container's image with the
Dockerfile's or the Containerfile's
format:
What are the image's layers? How does the Containerfile's build work? Why is the non-root's user (the# Create a simple's application mkdir -p /tmp/myapp cat > /tmp/myapp/app.py << 'EOF' from http.server import HTTPServer, BaseHTTPRequestHandler class Handler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Content-Type', 'text/plain') self.end_headers() self.wfile.write(b'Hello from the container!\n') if __name__ == '__main__': HTTPServer(('0.0.0.0', 8080), Handler).serve_forever() EOF # Create the Containerfile (the Dockerfile's alternative) cat > /tmp/myapp/Containerfile << 'EOF' FROM python:3.11-alpine WORKDIR /app COPY app.py . RUN adduser -D -u 1000 appuser USER appuser EXPOSE 8080 CMD ["python", "app.py"] EOF # Build the image podman build -t myapp:v1.0 /tmp/myapp/ 2>/dev/null podman images | grep myapp # Run the container podman run -d --name myapp-container -p 8080:8080 myapp:v1.0 2>/dev/null podman ps 2>/dev/null curl http://localhost:8080/ 2>/dev/null # Inspect the image's layers podman image tree myapp:v1.0 2>/dev/null # The cleanup podman rm -f myapp-container 2>/dev/null podman rmi myapp:v1.0 2>/dev/null rm -rf /tmp/myappUSER appuser's directive) the security's best's practice? Write a report (500–750 words). -
Analyze a container's escape.
Choose a known container's escape (the
CVE-2019-5736's in therunc, theCVE-2020-15257's in thecontainerd, theCVE-2021-25741's in the Kubernetes's subpath, theCVE-2022-0492's in the Linux's kernel's cgroups, theCVE-2022-0847's in theDirty Pipe) and analyze it:- The vulnerability's description.
- The escape's mechanism (which component: the kernel's, the runtime's, the configuration's).
- The affected's versions and the fixes.
- The lessons for the containers' security's design.
- The mitigations (the minimal's configurations, the rootless's containers, the user's namespaces, the monitoring).
-
Compare the container's
runtimes.
Write a short paragraph (200–250 words)
comparing the container's runtimes (the
runc, thecrun, thecontainerd, theCRI-O). Your paragraph should address:- The low-level's and the high-level's runtimes' roles.
- Each runtime's implementation (the Go's vs. the C's).
- Each runtime's performance and the resource's usage.
- Each runtime's default's use (the Docker's, the Podman's, the Kubernetes's).
- The OCI's Runtime's Specification's role.
- The CRI's role in the Kubernetes's clusters.
-
Design a containers' platform.
A company is building a container's
platform for its development team. The
team needs the 50 microservices, the
CI/CD's pipelines, the development's
environments, and the production's
deployments. Design the platform:
- The container's engine (the Docker's, the Podman's) and its rationale.
- The images' strategy (the base's images, the minimal's images, the scanning, the signing).
- The registry (the public's, the private's, the cloud's).
- The orchestration (the Kubernetes's, the Nomad's, the Docker Swarm's) and its rationale.
- The networking (the CNI's plugins, the service's mesh).
- The storage (the volumes, the persistent's volumes, the CSI's drivers).
- The security (the RBAC, the network's policies, the pod's security's standards, the runtime's security's tools).
- The observability (the Prometheus's, the Grafana's, the Jaeger's, the OpenTelemetry's).
- Reflect. In your own words, explain the container's concept and the container's and the VM's distinction, and describe the containers' mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM, the user's namespace), the OCI standards, the runtimes, and the images. Then describe one situation in your own experience — a container you have run, a CI/CD's pipeline you have used, or a cloud's service you have relied on — where the containers' design affected the computing. What was the situation, and how would a different configuration have changed the outcome?
10 Summary
- A container is the isolated's environment in which one or more processes run, with the isolated's view of the system's resources, the limited's resource's usage, and the packaged's file system. The container's and the VM's distinction: the containers run on the shared host's kernel (the kernel's mechanisms: the namespaces and the cgroups), and the VMs run on the separate guest's kernels (the hypervisor and the hardware's virtualization).
- The containers' benefits: the lightweight (the fast's startup, the low's memory's overhead, the high's density), the portability (the images), the consistency (the same's image runs the same's way), the agility (the fast's deployment), the efficiency (the resource's usage), and the ecosystem. The containers' trade-offs: the shared's kernel (the weaker's isolation), the same's kernel's requirement (the different's OS require the VMs), and the complexity (the orchestration, the networking, the storage, the security).
- The containers' history: the chroot (the 1979's), the BSD's jails (the 2000's), the Solaris's Zones (the 2004's), the Linux's namespaces and cgroups (the 2002–2008's), the Linux-VServer and the OpenVZ (the 2000s), the LXC (the 2008's), the Docker (the 2013's), the OCI (the 2015's), and the Kubernetes (the 2014's).
- The containers' mechanisms: the namespaces (the isolated's views), the cgroups (the limited's usage), the union file systems (the packaged's file system: the image's layers and the writable's layer), the capabilities (the restricted's privileges), the seccomp (the system calls' restrictions), the LSM (the mandatory access control), and the user's namespace (the rootless's containers).
-
The OCI standards: the
Runtime's Specification (the container's
runtime's interface), the Image's
Specification (the image's format), and
the Distribution's Specification (the
registry's API). The
runtimes: the
low-level's (the
runc, thecrun) and the high-level's (thecontainerd, theCRI-O), with the CRI (the Kubernetes's Container Runtime Interface). -
The container's image: the
layers (the base's, the dependencies', the
application's), the
overlayfs's mounting (the lower's read-only's layers, the upper's writable's layer), and the registries (the Docker Hub, the Quay, the GitHub's Container Registry, the cloud's providers'). The container's lifecycle: the image's pulling, the container's creation, the start, the execution, the pause, the stop, and the removal. - The container's security: the isolation's strength (the shared's kernel), the attack surface (the processes, the image, the runtime, the engine, the orchestration, the host's kernel), the escapes (the kernel's, the runtime's, the configuration's vulnerabilities), and the configurations (the non-root's user, the read-only's file system, the dropped's capabilities, the seccomp's profiles, the LSM's profiles, the minimal's images, the scanning, the secrets' management).
- The containers' use's cases: the microservices, the CI/CD's pipelines, the serverless's platforms, the cloud's native's applications, the development and the testing, and the batch's jobs.
- The containers' and the VMs' trade-offs: the containers' lightweight (the density, the startup, the resource's usage) versus the VMs' isolation (the separate's kernels, the different's OS); the two are the complementary, and the modern's systems combine them.
- The containers' trends: the container's and the VM's convergence (the Kata Containers, the gVisor, the Firecracker, the KubeVirt), the rootless's containers, the WebAssembly's containers, the confidential containers, the serverless's containers, and the supply chain's security.
Looking ahead
This lesson presented the containers: the container's concept and the container's and the VM's distinction, the containers' history (the chroot to the Kubernetes), the containers' mechanisms (the namespaces, the cgroups, the union file systems, the capabilities, the seccomp, the LSM, the user's namespace), the OCI standards, the runtimes, the images, the lifecycle, the security, the use's cases, and the trade-offs. It referred several times to the Docker — the engine that popularized the containers — and to the Docker's specific's components (the Dockerfile's build's format, the Docker Hub's registry, the Docker's daemon, the Docker Compose's multi-container's applications). Lesson 13.4 presents the Docker in detail: the Docker's architecture (the client, the daemon, the containerd, the runc), the Docker's images (the Dockerfile's directives, the layers, the caching, the multi-stage's builds), the Docker's containers (the lifecycle, the networking, the storage, the configuration), the Docker Compose's multi-container's applications, the Docker's registries and the distribution, the Docker's security's considerations, and the Docker's ecosystem. The lesson is the fourth of the unit's presentation of the virtualization's continuum, and it is the bridge to the container orchestration (Lesson 13.5).
11 References
- Silberschatz, A., Galvin, P. B., & Gagne, G. Operating System Concepts, 10th ed., Chapter 18.
-
Arpaci-Dusseau, R. H., & Arpaci-Dusseau, A. C.
Operating Systems: Three Easy Pieces,
the Virtualization chapters.
Available online at
ostep.org. - Tanenbaum, A. S., & Bos, H. Modern Operating Systems, 4th ed., Chapter 7.
-
Kerrisk, M. The Linux Programming
Interface. No Starch Press, 2010, and
the
man 7 namespaces, theman 7 cgroups, and theman 2 clonepages. - Rosen, R. Resource Management: The Linux and the SUSE Approaches.
- Merkel, D. Docker: Lightweight Linux Containers for Consistent Development and Deployment. Linux Journal, 2014.
- Bernstein, D. Containers and Cloud: From LXC to Docker to Kubernetes. IEEE Cloud Computing, 2014.
- Burns, B., Grant, B., Oppenheimer, D., Brewer, E., & Wilkes, J. Borg, Omega, and Kubernetes. ACM Queue, 2016.
- The Open Container Initiative (OCI)'s specifications: the Runtime's Specification, the Image's Specification, and the Distribution's Specification.
- Rice, L. Container Security. O'Reilly, 2020.
- NIST SP 800-190. Application Container Security Guide. National Institute of Standards and Technology.
-
Linux man-pages:
man 7 namespaces,man 2 clone,man 2 unshare,man 1 unshare,man 1 nsenter,man 7 cgroups,man 1 systemd-cgls,man 1 systemd-cgtop,man 1 systemd-run,man 5 systemd.resource-control,man 2 seccomp,man 7 capabilities,man 8 setcap,man 5 overlay,man 8 mount,man 1 podman,man 1 podman-run,man 1 podman-exec,man 1 podman-build,man 1 buildah,man 1 skopeo,man 1 docker,man 1 docker-run,man 1 runc,man 1 crun,man 1 ctr,man 1 crictl,man 1 systemd-nspawn,man 1 machinectl,man 1 lsns,man 1 dmesg,man 5 proc.