Module 5 · Unit 13 · Lesson 4 of 7
Docker
Lesson 13.3 presented the containers' concept and their mechanisms (the namespaces, the cgroups, the union file systems, the OCI standards, the runtimes). This lesson presents Docker: the platform that made the containers the accessible to the developers, and that created the ecosystem (the images, the registries, the Dockerfile, the Compose) that the modern's applications' development and deployment depend on. Docker's design's central's observation: the containers' kernel's mechanisms (the namespaces, the cgroups, the union file systems) existed for the years, and the Docker's contribution was the user experience: the images (the application and its dependencies in the single's artifact), the Dockerfile (the declarative's build's format), the registries (the images' distribution), and the developer-friendly's tooling. The lesson presents the Docker's platform, the Docker's objects, the Dockerfile, the images, the containers, the Docker Compose, the Docker's security, and the Docker's ecosystem.
01 Introduction
Lesson 13.3 presented the containers' concept and
their mechanisms: the namespaces (the isolated's
views), the cgroups (the limited's usage), the
union file systems (the packaged's file system),
the capabilities, the seccomp, the LSM, and the
user's namespace, and the OCI standards (the
Runtime's Specification, the Image's
Specification, the Distribution's Specification)
and the runtimes (the runc, the
crun, the containerd,
the CRI-O). This lesson presents
Docker: the platform that made
the containers the accessible to the developers,
and that created the ecosystem that the modern's
applications' development and deployment depend on.
Docker is the open-source's platform (the Docker Inc.'s, the 2013's release) for the containers' building, the sharing, and the running. The Docker's design's central's observation: the containers' kernel's mechanisms (the namespaces, the cgroups, the union file systems) existed for the years before the Docker, and the Docker's contribution was the user experience:
- The images. The Docker's image is the packaged's file system: the application and its dependencies in the single's artifact. The image's design: the layers (the base's image, the dependencies, the application), the metadata (the configuration, the environment's variables, the entrypoint, the command), and the digest (the content-addressable's identifier). The image's purpose: the application's dependencies' packaging (the "it works on my machine" problem's solution).
-
The Dockerfile. The
Dockerfile is the declarative's
format for the image's construction: the
sequence of the instructions (the
FROM, theRUN, theCOPY, theCMD, theENTRYPOINT, theENV, theEXPOSE, theUSER, theWORKDIR, theVOLUME, theLABEL, theARG, theHEALTHCHECK, theSHELL, theSTOPSIGNAL, theONBUILD), and the Docker's builder executes the instructions to produce the image. The Dockerfile's purpose: the image's reproducible's construction (the same's Dockerfile produces the same's image). -
The registries. The Docker's
registries are the images'
repositories: the Docker Hub (the
public's), the private's registries (the
Harbor, the Nexus, the Artifactory), the
cloud's providers' registries (the AWS's
ECR, the Azure's ACR, the Google's
Artifact Registry, the GitHub's Container
Registry, the GitLab's Container
Registry). The registries' purpose: the
images' distribution (the
docker pull's and thedocker push's commands), the images' versioning (the tags and the digests), and the images' sharing (thedocker.io/library/ubuntu's and the enterprises' private's images). -
The Docker's CLI and the API.
The Docker's CLI (the
docker's command) and the API (the REST's API: the/containers's, the/images's, the/networks's, the/volumes's, the/exec's endpoints) are the user's and the tools' interfaces. The Docker's CLI's design: the verbs (thebuild, therun, thepull, thepush, theps, theexec, thelogs, theinspect, thestop, therm, thermi) and the options (the resource's limits, the network's configuration, the volumes, the environment's variables, the ports, the security's options). - The Docker's ecosystem. The Docker's ecosystem: the Docker Compose (the multi-container's applications), the Docker Swarm (the native's orchestration), the Docker Desktop (the developer's environments), the Docker Hub (the public's registry), the BuildKit (the modern's builder), and the plugins (the storage's, the network's, the logging's, the security's).
The Docker's role in the containers' adoption: the Docker's release (the 2013's) was the containers' explosion's driver. The containers' kernel's mechanisms (the namespaces, the cgroups, the union file systems) existed for the years (the LXC's release was the 2008's), but the Docker's user experience — the simple's CLI, the images' concept, the Dockerfile's format, the Docker Hub's registry, the single's process's container's model — made the containers the accessible to the ordinary's developers. The observation is the reason the Docker's name is the container's synonym, and the observation is the reason the lesson's focus is the Docker's user experience (the images, the Dockerfile, the CLI) more than the kernel's mechanisms (the Lesson 13.3's focus).
This lesson presents Docker. It defines the Docker's platform and its components. It presents the Docker's objects (the images, the containers, the networks, the volumes, the plugins). It presents the Dockerfile's directives, the layers, the build cache, and the multi-stage's builds. It presents the Docker's images (the tags, the digests, the registries), the Docker's containers (the lifecycle, the networking, the storage, the configuration), the Docker Compose (the multi-container's applications), the Docker's security (the considerations, the best practices), and the Docker's ecosystem (the BuildKit, the Buildx, the Swarm, the plugins). It closes with the Docker's design's trade-offs and the lesson's synthesis.
Prerequisites
This lesson builds on Lesson 13.3 (the
containers) and the earlier lessons. The
containers' concept, the container's and the
VM's distinction, 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
are assumed (Lesson 13.3). The
virtualization's concept (Lesson 13.1), the
hypervisors (Lesson 13.2), the sandboxing
(Lesson 12.6), and the Linux's security's
mechanisms (Lesson 12.5) are the context for
the Docker's security's discussion.
Familiarity with the Linux command line
from Lesson 2.2 is essential, since the
interactive activity uses the
docker (or the
podman), the docker
compose, the docker build,
the docker run, the
docker exec, the
docker network, the
docker volume, the
docker inspect, the
docker logs, the
docker stats, the
docker save, the
docker load, the
docker history, the
docker diff, the
docker commit, the
buildah, the
skopeo, and the
dive commands.
02 Learning objectives
By the end of this lesson you will be able to
- Define Docker and describe its architecture (the client, the daemon, the containerd, the runc, the BuildKit), and explain the Docker's components' roles.
- Describe the Docker's objects (the images, the containers, the networks, the volumes, the plugins) and each object's purpose.
- Describe the Dockerfile's directives (the
FROM, theRUN, theCOPY, theCMD, theENTRYPOINT, theENV, theEXPOSE, theUSER, theWORKDIR, theVOLUME, theHEALTHCHECK, theARG, theLABEL, theSHELL, theSTOPSIGNAL, theONBUILD) and the layers' and the build's cache's mechanisms, and explain the multi-stage's builds. - Describe the Docker's images (the tags, the digests, the registries, the
docker pull's and thedocker push's commands) and the Docker's containers (the lifecycle, the networking, the storage, the configuration). - Describe the Docker Compose (the multi-container's applications: the
docker-compose.yml's or thecompose.yaml's files, the services, the networks, the volumes), and the Docker's security (the considerations, the best practices). - Explain the Docker's ecosystem (the BuildKit, the Buildx, the Swarm, the plugins) and the Docker's role in the containers' adoption, and evaluate a Docker's configuration against the lesson's criteria.
This lesson contributes to U13.4 and U13.5, and to course learning outcomes CLO1, CLO7, and CLO8.
03 Required reading
- Operating System Concepts — Chapter 18, Virtual Machines, the sections on the containers and the operating-system-level virtualization. The standard treatment of the containers' concept, the container's and the VM's comparison, and the containers' mechanisms that Docker builds on.
- OSTEP — the Virtualization chapters, particularly the discussion of the containers and the namespaces. OSTEP's treatment of the containers' mechanisms is the clearest available account of the layers that Docker builds on, 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 of the containers' mechanisms and the operating-system-level virtualization.
- Merkel, D. — Docker: Lightweight Linux Containers for Consistent Development and Deployment. Linux Journal, 2014. The paper that popularized the Docker's approach, and it is the source for the Docker's design's rationale.
- Bernstein, D. — Containers and Cloud: From LXC to Docker to Kubernetes. IEEE Cloud Computing, 2014. The survey of the containers' evolution and the Docker's role.
- Turnbull, J. — The Docker Book: Containerize your applications with Docker. The comprehensive's practical's reference for the Docker's usage, and it is the source for the Dockerfile's directives' and the Docker Compose's treatment.
- Nickoloff, J., & Kuenzli, S. — Docker in Action, 2nd ed. Manning, 2019. The comprehensive's reference for the Docker's design and the containers' management, and it is the source for the Docker's architecture's and the networking's treatment.
- Mouat, A. — Using Docker: Developing and Deploying Software with Containers. O'Reilly, 2016. The practical's reference for the Docker's workflows, and it is the source for the Docker's images' and the Dockerfile's treatment.
-
The Docker's documentation — The
Docker's official's documentation (the
docs.docker.com's site): the Dockerfile's reference, the Compose's reference, the Docker's CLI's reference, and the Docker's security's guides. The authoritative references for the Docker's usage. - 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 that the Docker's implements.
- Rice, L. — Container Security. O'Reilly, 2020. The comprehensive's treatment of the container's security's mechanisms and the escapes' attacks, and it is the source for the Docker's security's treatment.
- NIST SP 800-190. — Application Container Security Guide. National Institute of Standards and Technology. The security's reference for the containers.
-
The Linux man pages:
man 1 docker,man 1 docker-run,man 1 docker-build,man 1 docker-exec,man 1 docker-ps,man 1 docker-inspect,man 1 docker-logs,man 1 docker-network,man 1 docker-volume,man 1 docker-compose(or thedocker compose's subcommand),man 5 Dockerfile(or the Dockerfile's reference),man 5 docker-compose.yml(or the Compose's reference),man 7 namespaces,man 7 cgroups,man 5 overlay,man 1 podman(the daemonless's alternative),man 1 buildah,man 1 skopeo,man 1 nerdctl,man 1 dive(if available).
Reading strategy
Read the Merkel's paper first for the Docker's
design's rationale: the Docker's motivation
(the "it works on my machine" problem's
solution), the Docker's approach (the
images, the Dockerfile, the registry), and
the Docker's design's decisions (the
single's process's container's model, the
layered's images, the registry's
distribution). Then read the Turnbull's or
the Nickoloff and Kuenzli's book for the
practical's treatment: the Dockerfile's
directives, the images' layers, the
containers' lifecycle, the networking, the
storage, the Compose. The Bernstein's paper
is the containers' evolution's survey, and
it is worth reading for the Docker's
positioning in the containers' history.
The OCI's specifications are the standards
that the Docker implements, and they are
worth consulting for the runtime's and the
image's formats. The Rice's book and the
NIST SP 800-190 are the security's
references, and they are worth reading for
the Docker's security's considerations and
the containers' best practices. Finally,
the Docker's documentation is the
authoritative reference for the Docker's
usage, and the Linux man pages are the
practical's tools. Pay particular attention
to the distinction between the
image (the read-only's template:
the application and its dependencies) and
the container (the running's
instance: the image plus the writable's
layer, the processes, the namespaces, the
cgroups). The Docker's design's central's
observation: the image is the
build-time's artifact (the
Dockerfile's output), and the container is
the runtime's artifact (the
docker run's output). The
observation is the reason the Docker's
commands are the two's groups (the
build, the pull,
the push, the tag,
the rmi for the images; the
run, the ps, the
exec, the logs,
the stop, the rm
for the containers).
04 Core content
4.1 What Docker is
Docker is the open-source's platform for the containers' building, the sharing, and the running. The Docker's purpose: the applications' packaging and the deployment's consistency (the "it works on my machine" problem's solution: the application and its dependencies in the single's artifact, the image). The Docker's design's central's observation: the containers' kernel's mechanisms (the namespaces, the cgroups, the union file systems) existed for the years before the Docker, and the Docker's contribution was the user experience and the ecosystem.
The Docker's platform's components:
-
The Docker's client (the
docker's CLI). The user's interface: thedocker's command sends the requests to the daemon (the REST's API over the Unix's socket or the TCP's connection), and the daemon performs the operations. The client's design: the CLI's verbs (thebuild, therun, thepull, thepush, theps, theexec, thelogs, theinspect, thestop, therm, thermi, thenetwork, thevolume, thecompose), and the options (the resource's limits, the security's options, the network's configuration, the volumes, the environment's variables). -
The Docker's daemon
(
dockerd). The server: the daemon receives the client's requests (the REST's API), and it manages the Docker's objects (the images, the containers, the networks, the volumes, the plugins). The daemon's design: thedockerd's process, the containerd's child's process (the container's lifecycle's management), and the runc's children (the container's creation's and the execution's processes). -
The containerd. The
high-level's runtime (the Docker's
contribution, the containerd's project):
the component that manages 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
that delegates the container's creation
to the low-level's runtime (the
runc). The containerd's design: thecontainerd's daemon, the gRPC's API, the CRI's implementation, and the content's store (the images' layers' storage). -
The runc. The low-level's
runtime (the OCI's Runtime's
Specification's implementation): the
component that creates 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.
The
runc's design: the Go's implementation (the Docker's and the containerd's default), the short-lived's process (it creates the container and exits), and the OCI's standard's compliance. -
The BuildKit. The modern's
builder (the Docker's, the 2018's): the
component that builds the images from the
Dockerfiles with the performance's and
the security's improvements (the parallel's
builds, the cache's improvements, the
rootless's builds, the secrets' mounts,
the SSH's mounts, the multi-platform's
builds). The BuildKit's design: the
LLB's intermediate's representation (the
Low-Level's Build's the graph's), the
content-addressable's cache, and the
Dockerfile's frontend (the
# syntax=docker/dockerfile's directive). -
The Docker's storage's drivers.
The components that manage the containers'
file systems: the
overlay2(the default's on the modern's Linux: theoverlayfs's implementation), thebtrfs, thezfs, thedevicemapper(the deprecated), thevfs(the development's: the copies, not the layers). The drivers' design: the images' layers' storage, the containers' writable's layers, and the layers' sharing (the same's base's image is the stored the once). -
The Docker's networking's drivers.
The components that manage the containers'
networks: the
bridge(the default's: the private's network on the host's bridge), thehost(the container shares the host's network's namespace), theoverlay(the multi-host's networks: the Docker Swarm's), themacvlan(the containers get the MAC's addresses on the physical's network), thenone(the container has no network), and theipvlan.
4.2 The Docker's objects
The Docker's objects (the components that the Docker's platform manages):
- The image (the read-only's template)
-
The Docker's image is the
read-only's template that contains the
application and its dependencies: the
layered's file system (the base's image,
the dependencies, the application), the
metadata (the configuration: the
environment's variables, the entrypoint,
the command, the exposed's ports, the
volumes, the user, the working's
directory), and the digest (the
content-addressable's identifier). The
image's commands: the
docker build(the Dockerfile's construction), thedocker pull(the registry's download), thedocker push(the registry's upload), thedocker tag(the image's naming), thedocker images(the listing), thedocker inspect(the inspection), thedocker history(the layers' history), thedocker rmi(the removal). - The container (the running's instance)
-
The Docker's container is
the running's instance of the image: the
image's layers plus the writable's layer
(the container's changes), the processes
(the entrypoint's command), the
namespaces (the isolated's views), the
cgroups (the resource's limits), the
network's interfaces, and the volumes
(the persistent's storage). The
container's commands: the
docker run(the creation and the start), thedocker create(the creation), thedocker start(the start), thedocker stop(the stop), thedocker restart(the restart), thedocker pause(the pause), thedocker unpause(the resume), thedocker ps(the listing), thedocker exec(the command's execution inside the container), thedocker logs(the logs), thedocker inspect(the inspection), thedocker stats(the statistics), thedocker top(the processes), thedocker diff(the file system's changes), thedocker commit(the new image from the container), thedocker rm(the removal). - The network (the isolated's network)
-
The Docker's network is the
isolated's network that the containers
use: the bridge's networks (the default's:
the private's network on the host's
bridge), the overlay's networks (the
multi-host's: the Docker Swarm's), the
macvlan's networks (the containers get
the MAC's addresses on the physical's
network), the host's networks (the
container shares the host's network), and
the none's networks (the container has no
network). The network's commands: the
docker network create, thedocker network ls, thedocker network inspect, thedocker network connect, thedocker network disconnect, thedocker network rm. - The volume (the persistent's storage)
-
The Docker's volume is the
persistent's storage that the containers
use: the volumes (the Docker's managed's
storage: the
/var/lib/docker/volumes/'s directory), the bind mounts (the host's directories or the files mounted into the container: the-v /host/path:/container/path's option), and the tmpfs's mounts (the memory's file systems). The volume's purpose: the containers' data's persistence (the container's writable's layer is the ephemeral), and the observation: the volumes are the containers' state's management's mechanism. The volume's commands: thedocker volume create, thedocker volume ls, thedocker volume inspect, thedocker volume rm, thedocker volume prune. - The plugin (the extensibility)
-
The Docker's plugin is the
extension's mechanism: the storage's
plugins (the volumes' drivers: the
cloud's storage's integrations), the
network's plugins (the CNI's, the
overlay's), the logging's plugins (the
logs' drivers: the syslog's, the
fluentd's, the journald's, the splunk's),
the authorization's plugins, and the
metrics's plugins. The plugin's purpose:
the Docker's integration with the
external's systems (the storage, the
network, the logging, the monitoring).
The plugin's commands: the
docker plugin install, thedocker plugin ls, thedocker plugin inspect, thedocker plugin rm.
docker's CLI) sends the
requests to the Docker's daemon (the
dockerd) over the REST's API;
the daemon manages the Docker's objects and
delegates the container's lifecycle to the
containerd (the high-level's runtime), which
delegates the container's creation to the
runc (the low-level's runtime);
the BuildKit is the images' builder. The
bottom diagram shows the Docker's objects:
the image (the read-only's template: the
layers and the metadata), the container (the
running's instance: the image plus the
writable's layer, the processes, the
namespaces, the cgroups, the network, the
volumes), the network (the isolated's
network), the volume (the persistent's
storage), and the plugin (the extensibility).
The Docker's workflow: the build, the push,
the pull, the run, and the container's
execution.
4.3 The Dockerfile
The Dockerfile is the declarative's format for the image's construction: the text's file with the instructions, and the Docker's builder (the BuildKit) executes the instructions to produce the image. The Dockerfile's directives:
FROM-
The base's image: the
FROM ubuntu:22.04's directive starts the image's construction from theubuntu:22.04's image. TheFROM's directive is the mandatory's first's instruction (except theARG's directive before it), and the multi-stage's builds use the multiple'sFROM's directives. RUN-
The command's execution during the build:
the
RUN apt-get update's directive executes the command in the container's build-time's environment, and the command's results are the new's layer. TheRUN's directives are the image's construction's workhorses (the packages' installation, the build's commands, the configuration's commands), and the observation: theRUN's directive's layer's caching is the build's performance's mechanism. COPY-
The files' and the directories' copying from
the build's context into the image: the
COPY app.py /app/'s directive copies theapp.py's file from the build's context (the directory's where the Dockerfile is, or the specified's context) into the image's/app/'s directory. ADD-
The
COPY's superset: theADD's directive supports the remote's URLs and the local's archives' extraction (the tar's files), and the observation: the Docker's best's practice recommends theCOPY's directive (the explicit's behavior) over theADD's (the implicit's behavior) unless the archive's extraction or the remote's URL is the intended's behavior. CMD-
The default's command: the
CMD ["python", "app.py"]'s directive specifies the command that the container runs by default, and thedocker run's arguments override theCMD's directive. The Dockerfile's design: theCMD's directive has the three's forms (the exec's formCMD ["executable", "param1"], the shell's formCMD command param1, and the parameters' formCMD ["param1"]with theENTRYPOINT), and the observation: the exec's form is the recommended (the no shell's interpretation). ENTRYPOINT-
The container's executable: the
ENTRYPOINT ["python"]'s directive specifies the executable that the container runs, and theCMD's directive supplies the default's arguments. TheENTRYPOINT's directive is the container's "the application" (the container's design: the single's process's model), and the observation: theENTRYPOINT's and theCMD's combination's design is the container's interface's convention. ENV-
The environment's variables: the
ENV NODE_ENV production's directive sets the environment's variables that persist in the image, and the container's processes see them. TheENV's directive is the configuration's mechanism, and the observation: theARG's directive (the build-time's variables) and theENV's directive (the runtime's variables) are the different's scopes. ARG-
The build-time's variables: the
ARG VERSION=latest's directive declares the build-time's variable that thedocker build --build-arg VERSION=1.0's option can override. TheARG's directive is the build's parameterization's mechanism, and the observation: theARG's values are not the persisted in the image (the build's history's exclusion: the secrets' leakage's prevention), and the observation is the reason theARG's directive is the secrets' mechanism's candidate (the with the BuildKit's secrets' mounts, theRUN --mount=type=secret's directive). WORKDIR-
The working's directory: the
WORKDIR /app's directive sets the working's directory for theRUN, theCMD, theENTRYPOINT, theCOPY, and theADD's directives, and the container's processes' working's directory. TheWORKDIR's directive is the path's management's mechanism, and the observation: theWORKDIR's directive creates the directory if it does not exist (themkdir -p's behavior). EXPOSE-
The port's documentation: the
EXPOSE 8080's directive documents the port that the container's application listens on, and thedocker run -p 8080:8080's option publishes the port to the host. TheEXPOSE's directive is the documentation's mechanism (the metadata), not the publishing's mechanism (the-p's option), and the observation: theEXPOSE's directive is the image's metadata's documentation. USER-
The user's specification: the
USER appuser's directive sets the user that the container's processes run as, and the observation: theUSER's directive is the security's best's practice (the non-root's user: the Lesson 12.6's and the Lesson 13.3's discussions). VOLUME-
The volume's declaration: the
VOLUME /data's directive declares the directory as the volume's mount's point, and the observation: theVOLUME's directive declares the volume's intent (the data's persistence's boundary), and the observation: thedocker run -v's option is the volume's actual's configuration. HEALTHCHECK-
The health's check: the
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/ || exit 1's directive specifies the command that the Docker runs to check the container's health, and the observation: theHEALTHCHECK's directive is the container's health's monitoring's mechanism, and the observation: the orchestrators (the Docker Swarm, the Kubernetes) use the health's checks for the container's lifecycle's management. LABEL-
The metadata's labels: the
LABEL maintainer="alice@example.com"'s directive adds the metadata's labels to the image, and the observation: the labels are the image's metadata's documentation (the author, the version, the license, the description). SHELL-
The shell's specification: the
SHELL ["/bin/bash", "-c"]'s directive sets the shell that theRUN's, theCMD's, and theENTRYPOINT's shell's forms use. TheSHELL's directive is the shell's customization's mechanism (the Windows's PowerShell, the Linux's bash). STOPSIGNAL-
The signal's specification: the
STOPSIGNAL SIGTERM's directive specifies the signal that thedocker stop's command sends to the container's process, and the observation: theSTOPSIGNAL's directive is the graceful's shutdown's mechanism. ONBUILD-
The deferred's instruction: the
ONBUILD COPY . /app's directive specifies the instruction that the Docker executes when the image is used as the base's image in a subsequent's build. TheONBUILD's directive is the base's image's customization's mechanism, and the observation: theONBUILD's directive is the deprecated's in the modern's practices (the explicit'sCOPY's directive in the child's Dockerfile is the recommended's practice).
The Dockerfile's layers and
the build's cache: each
Dockerfile's instruction produces a layer
(the RUN, the COPY,
the ADD, the ENV,
the WORKDIR, the
USER, the LABEL),
and the Docker's builder caches the layers:
when the builder re-executes the Dockerfile,
it reuses the cached's layers for the
instructions whose inputs are unchanged
(the same's base's image, the same's
commands, the same's copied files), and it
re-executes the instructions whose inputs
changed (the layer and the subsequent's
layers). The observation is the reason the
Dockerfile's instructions' order matters:
the instructions that change frequently
(the application's code's COPY)
should be the last (the cache's reuse of
the dependencies' layers), and the
instructions that change rarely (the
packages' installation's RUN)
should be the first (the cache's reuse
across the builds). The Dockerfile's best's
practice: the layer's ordering (the
dependencies' installation first, the
application's code last), the
combined's RUN's
directives (the fewer's layers: the
RUN apt-get update && apt-get
install -y ... && rm -rf
/var/lib/apt/lists/*'s pattern), the
.dockerignore's file
(the build's context's exclusion: the
unnecessary's files' exclusion), and the
multi-stage's builds (the
separation's of the build's and the
runtime's dependencies).
The multi-stage's builds: the
Dockerfile with the multiple's
FROM's directives, where the
earlier's stages are the build's stages
(the compilers, the build's tools) and the
later's stages are the runtime's stages
(the minimal's image with the compiled's
artifacts copied from the build's stages).
The multi-stage's builds' advantages: the
smaller's images (the runtime's stage's
image excludes the build's tools and the
source's code), the smaller's attack surface
(the fewer's packages), and the faster's
pulls (the smaller's layers). The
multi-stage's builds' design's example:
# The build's stage
FROM golang:1.21 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server ./cmd/server
# The runtime's stage
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
The multi-stage's builds' observation: the
build's stage (the golang:1.21's
image: the ~800 MB) contains the compiler
and the source's code, and the runtime's
stage (the distroless's image:
the ~2 MB) contains only the compiled's
binary. The observation: the multi-stage's
builds are the modern's images' best's
practice (the smaller's images, the
smaller's attack surface, the faster's
pulls), and the observation is the reason
the modern's Dockerfiles use the
multi-stage's builds for the compiled's
languages (the Go, the Rust, the C, the
Java) and for the interpreted's languages'
dependency's vendoring.
4.4 The Docker's images
The Docker's image is the read-only's template that contains the application and its dependencies. The image's structure and management:
- 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, and the image's metadata (the config's JSON's file) is the separate's artifact. 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 distribution's efficiency (the layers are the cached and the transferred the incrementally: only the changed's layers are the downloaded), and the reproducibility (the image's digest identifies the exact's contents).
-
The tags. 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 tag's design: the mutable's reference (the same's tag can point to the different's digests: thelatest's tag is the conventional's "the most recent's" tag), and the observation: the tags are the convenient's references (the human's readable), and the observation is the reason the production's deployments should use the digests, not the tags. -
The digests. The image's
content-addressable's identifier (the
sha256:...'s: the hash of the image's manifest), and the digest's design: the immutable's reference (the digest identifies the exact's contents: the same's digest is always the same's image), and the observation: the digests are the production's deployments' references (the immutable's guarantee), and the observation is the reason the supply chain's security (the images' signing, the attestation) uses the digests. -
The registries. The
images' repositories: the Docker Hub (the
public's: the
docker.io's registry), the private's registries (the Harbor, the Nexus, the Artifactory, the GitLab's Container Registry), and the cloud's providers' registries (the AWS's ECR, the Azure's ACR, the Google's Artifact Registry, the GitHub's Container Registry). The registries' commands: thedocker login(the authentication), thedocker pull(the download), thedocker push(the upload), thedocker search(the search), thedocker manifest(the multi-platform's manifests). -
The image's commands. The
docker build(the Dockerfile's construction), thedocker images(the listing), thedocker inspect(the inspection: the configuration, the layers, the metadata), thedocker history(the layers' history: the instructions and the sizes), thedocker tag(the image's naming), thedocker rmi(the removal), thedocker save(the image's export to a tar's file), thedocker load(the image's import from a tar's file), thedocker export(the container's file system's export), thedocker import(the file system's import as an image). -
The image's analysis's tools.
The
dive's tool (the image's layers' inspection: the each layer's contents, the wasted's space, the efficiency's score), thedocker history(the layers' history), thedocker inspect(the image's metadata), theskopeo inspect(the remote's image's inspection), and thebuildah inspect(the image's inspection with the buildah's tool). The observation: the image's analysis's tools are the image's optimization's and the security's auditing's mechanisms.
4.5 The Docker's containers
The Docker's container is the running's instance of the image: the image's layers plus the writable's layer (the container's changes), the processes (the entrypoint's command), the namespaces (the isolated's views), the cgroups (the resource's limits), the network's interfaces, and the volumes (the persistent's storage). The container's management:
-
The container's lifecycle. The
docker create(the creation: the container's state is thecreated), thedocker start(the start: the container's state is therunning), thedocker pause(the pause: the container's state is thepaused: the cgroup's freezer's controller suspends the processes), thedocker unpause(the resume), thedocker stop(the stop: the SIGTERM's signal, the grace's period, the SIGKILL's signal; the container's state is theexited), thedocker restart(the restart), thedocker kill(the forced's stop: the SIGKILL's signal), thedocker rm(the removal: the container's state is theremoved: the writable's layer is the deleted and the resources are the released). -
The container's inspection. The
docker ps(the running's containers's listing: the-a's option shows the stopped's containers), thedocker inspect(the container's configuration and the state: the JSON's output), thedocker logs(the container's logs: the stdout's and the stderr's streams), thedocker stats(the container's resource's usage: the CPU's, the memory's, the network's, the I/O's), thedocker top(the container's processes), thedocker diff(the container's file system's changes: the added, the modified, the deleted files), thedocker port(the container's port's mappings), thedocker events(the Docker's events's stream). -
The container's interaction. The
docker exec(the command's execution inside the running's container: the-it's options for the interactive's session), thedocker attach(the container's stdin's and stdout's attachment: the container's main's process), thedocker cp(the files' copying between the container and the host), thedocker commit(the new image from the container's changes: the writable's layer as the new's layer). -
The container's networking. The
network's drivers (the bridge's, the
host's, the overlay's, the macvlan's, the
none's), the port's mapping (the
-p host_port:container_port's option: the host's port forwarding to the container's port), the network's inspection (thedocker network inspect's output: the network's details, the containers' IP's addresses, the DNS's configuration), the container's DNS's resolution (the container's name's resolution within the user-defined's networks: the embedded's DNS's server), and the container's network's namespaces (the isolated's view). -
The container's storage. The
writable's layer (the container's file
system's changes: the
overlayfs's upper's directory), the volumes (the-v volume_name:/path's option: the Docker's managed's volume), the bind mounts (the-v /host/path:/container/path's option: the host's directory), the tmpfs's mounts (the--tmpfs /path's option: the memory's file system), and the container's file system's inspection (thedocker diff's output). -
The container's resource's limits.
The CPU's limits (the
--cpus 0.5's option: the 50% of the one CPU; the--cpu-shares 512's option: the relative's weight), the memory's limits (the--memory 128m's option: the hard's limit; the--memory-reservation 64m's option: the soft's limit), the I/O's limits (the--device-read-bps's and the--device-write-bps's options), the processes' count's limit (the--pids-limit 100's option), and the observation: the resource's limits are the cgroups' controllers' configuration (the Lesson 12.6's and the Lesson 13.3's discussions). -
The container's configuration. The
environment's variables (the
-e KEY=value's option, the--env-file .env's option), the container's name (the--name myapp's option), the restart's policy (the--restart always's,unless-stopped's, andon-failure's policies), the labels (the--label key=value's option), and the health's check (the--health-cmd's and the--health-interval's options).
4.6 The Docker Compose
The Docker Compose is the
Docker's tool for the multi-container
applications: the compose.yaml's
(or the docker-compose.yml's)
file declares the application's services, and
the docker compose up's command
creates and starts the services (the
containers, the networks, the volumes). The
Compose's components:
- The services
-
The application's components: the each's
service is the container (or the
containers' set: the
deploy.replicas's option), and the service's configuration (the image, the build's context, the ports, the volumes, the environment's variables, the dependencies, the networks, the resource's limits, the health's checks, the restart's policy). - The networks
- The application's networks: the Compose's networks (the default's: the single's network that the services share; the custom's: the multiple's networks with the services' isolation), and the observation: the Compose's networks are the Docker's networks (the bridge's, the overlay's).
- The volumes
- The application's persistent's storage: the Compose's volumes (the named's volumes, the bind mounts, the tmpfs), and the observation: the Compose's volumes are the Docker's volumes (the named's volumes are the Docker's managed's storage).
- The
compose.yaml's structure -
The file's sections: the
services's section (the application's services), thenetworks's section (the application's networks), thevolumes's section (the application's volumes), theconfigs's section (the configuration's files), thesecrets's section (the secrets' management), and thex-'s extension's fields (the custom's fields).
The Compose's example:
# The compose.yaml's file
services:
web:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
db:
condition: service_healthy
networks:
- app-network
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
networks:
app-network:
driver: bridge
The Compose's commands: the
docker compose up (the
services' creation and the start: the
-d's option for the detached's
mode), the docker compose down
(the services' stop and the resources'
removal: the -v's option
removes the volumes), the
docker compose ps (the
services' listing), the
docker compose logs (the
services' logs: the -f's
option for the follow's mode), the
docker compose exec (the
command's execution inside the service's
container), the docker compose build
(the services' images' building), the
docker compose pull (the
services' images' pulling), the
docker compose config (the
configuration's validation), the
docker compose scale (the
services' scaling: the deprecated in the
modern's Compose: the
deploy.replicas's option is
the modern's mechanism). The observation:
the Compose's design is the development's
and the testing's multi-container's
applications' tool (the
compose.yaml's file is the
application's definition), and the
observation: the Compose's design is the
Kubernetes's manifests' ancestor (the
Compose's services and the Kubernetes's
Deployments and Services are the
analogous's concepts).
4.7 The Docker's security
The Docker's security is the layered's design (the Lesson 12.6's and the Lesson 13.3's discussions applied to the Docker's configuration), and its considerations:
-
The Docker's daemon's security.
The
dockerd's daemon runs as the root (the default's configuration), and its attack surface is the significant (the REST's API, the images' management, the containers' management). The observation: the daemon's compromise is the host's compromise (the daemon has the root's privileges), and the observation is the reason the modern's practices include the daemon's protection (the Unix's socket's permissions, the TLS's certificates for the TCP's connections, the authorization's plugins, thedocker's group's membership's caution: thedocker's group is the root's equivalent), and the rootless's Docker (the daemon runs as the unprivileged's user). -
The container's security. The
containers' security's configurations (the
Lesson 13.3's discussions): 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--security-opt seccomp='s option), the LSM's profiles (the--security-opt label='s option), the--security-opt no-new-privileges's setting, the resource's limits (the--memory's, the--cpus's, the--pids-limit's options), the--privileged's flag's avoidance (the full's host's privileges: the dangerous's option), the host's namespaces' sharing's avoidance (the--pid=host's, the--net=host's, the--ipc=host's, the--uts=host's options), the sensitive's mounts' avoidance (the/var/run/docker.sock's mount, the host's root's mount), and the observation: the container's security is the configuration's discipline. -
The image's security. The
images' security (the Lesson 13.3's
discussions): the minimal's images (the
alpine's, thedistroless's, thescratch's: the smaller's attack surface), the images' scanning (the Trivy's, the Clair's, the Grype's, the Docker'sdocker scan), the images' signing (the Notary's, the Cosign's, the Docker Content Trust), the images' attestation (the SLSA's, the in-toto's), the SBOM's generation (the Syft's, the Trivy's), and the observation: the image's security is the containers' security's foundation (the image's vulnerabilities are the container's vulnerabilities). - The registry's security. The registries' security: the authentication (the Docker Hub's, the cloud's providers' registries' IAM), the authorization (the repositories' access's control), the images' signing and the verification (the content's trust: the Notary's, the Cosign's), the images' scanning (the registries' integrated's scanning: the Harbor's, the ECR's, the ACR's), and the observation: the registry's security is the images' supply chain's security's foundation.
-
The secrets' management. The
secrets' mechanisms: the
--env-file's option (the environment's variables: the limited's security: the variables are the visible in thedocker inspect's output), the Docker's secrets (thedocker secret's commands: the Swarm's secrets), the build's secrets (the BuildKit'sRUN --mount=type=secret's directive: the secrets are the mounted during the build, not the persisted in the layer), and the external's secret's stores (the HashiCorp's Vault, the Kubernetes's secrets, the cloud's providers' secret's managers), and the observation: the secrets' management is the containers' security's central's concern (the secrets' leakage is the common's vulnerability). - The runtime's security's tools. The runtime's security's monitoring: the Falco's (the runtime's security: the system calls' monitoring, the rules, the alerts), the Sysdig's (the system's inspection), the Tetragon's (the eBPF's security's observability: the Kubernetes's, the Cilium's), the Docker Bench's (the CIS's benchmark's checks for the Docker's configuration), and the observation: the runtime's security's tools are the containers' security's operations's mechanisms (the detection, the response).
-
The Docker Bench for Security.
The
docker-bench-security's tool: the CIS's Docker's benchmark's automated's checks (the host's configuration, the daemon's configuration, the container's configuration, the images, the runtime), and the observation: the Docker Bench's tool is the Docker's security's assessment's mechanism.
4.8 The Docker's ecosystem
The Docker's ecosystem's components:
-
The BuildKit. The modern's
builder (the Docker's, the 2018's): the
performance's and the security's
improvements (the parallel's builds, the
cache's improvements, the rootless's
builds, the secrets' mounts, the SSH's
mounts, the multi-platform's builds, the
RUN --mount's directives). The BuildKit's activation: theDOCKER_BUILDKIT=1's environment's variable (the older's versions) or the default (the modern's Docker's versions). -
The Buildx. The Docker's
CLI's plugin (the BuildKit's frontend):
the
docker buildx's commands (thebuildx build, thebuildx create, thebuildx inspect, thebuildx ls, thebuildx rm), and the multi-platform's builds (the--platform linux/amd64,linux/arm64's option: the single's command builds the images for the multiple's architectures), and the observation: the Buildx's tool is the modern's Docker's builders' interface. - The Docker Swarm. The Docker's native's orchestration (the Docker's, the 2014's): the swarm (the cluster's of the Docker's nodes), the services (the multi-container's applications: the replicated's tasks), the nodes (the managers and the workers), the overlay's networks (the multi-host's networks), the secrets and the configs (the sensitive's data's management), and the stacks (the Compose's files' deployment), and the observation: the Swarm's design is the Docker's native's orchestration, and the observation: the Swarm's adoption is the limited (the Kubernetes's dominance), but the Swarm's simplicity is the attractive for the small's deployments.
- The Docker Desktop. The developer's environment (the Docker's, the macOS's, the Windows's, the Linux's): the Docker's daemon, the CLI, the Compose, the Kubernetes's single-node's cluster, the Buildx, and the GUI (the Docker Dashboard), and the observation: the Docker Desktop's tool is the developers' local's environments' standard.
-
The Docker Hub. The public's
registry (the Docker's): the official's
images (the
ubuntu's, thepython's, thenode's, thealpine's, thenginx's, thepostgres's, theredis's, themongo's), the verified's publishers (the vendors' images), the community's images, the automated's builds (the linked's GitHub's and Bitbucket's repositories), the webhooks, and the organizations and the teams, and the observation: the Docker Hub's registry is the images' distribution's foundation. - The Docker's plugins. The Docker's plugins' ecosystem: the storage's plugins (the volumes' drivers: the cloud's storage's integrations: the AWS's EBS, the Azure's Disk, the GCP's Persistent Disk, the NetApp's, the Pure's), the network's plugins (the CNI's, the overlay's: the Calico's, the Cilium's, the Weave's), the logging's plugins (the logs' drivers: the syslog's, the fluentd's, the journald's, the splunk's, the gcplogs's), the authorization's plugins (the access's control), and the metrics's plugins (the monitoring's integrations).
- The Docker's alternatives. The Docker's alternatives: the Podman (the daemonless's containers: the rootless's default, the systemd's integration, the pods), the containerd (the standalone's runtime: the Kubernetes's), the CRI-O (the Kubernetes's runtime), the nerdctl (the containerd's CLI: the Docker's compatible's interface), the Buildah (the images' building without the daemon), the Kaniko (the images' building in the Kubernetes's clusters), the img (the rootless's images' builder), and the observation: the Docker's alternatives are the containers' ecosystem's diversity, and the observation is the reason the OCI's standards are the containers' interoperability's foundation.
4.9 The Docker's role in the containers' adoption
The Docker's role in the containers' adoption:
-
The Docker's contribution: the user
experience. The containers'
kernel's mechanisms (the namespaces, the
cgroups, the union file systems) existed
for the years before the Docker, and the
Docker's contribution was the user
experience: the images (the
application and its dependencies in the
single's artifact: the "it works on my
machine" problem's solution), the
Dockerfile (the declarative's
build's format: the reproducible's image's
construction), the registries
(the images' distribution: the Docker Hub's
and the private's registries), the
CLI (the simple's commands: the
docker run, thedocker build, thedocker pull, thedocker push), and the ecosystem (the Compose, the Swarm, the Desktop, the Hub, the plugins). - The Docker's impact: the containers' mainstream. The Docker's release (the 2013's) made the containers the mainstream: the developers' adoption (the images and the Dockerfile for the applications' packaging), the operations' adoption (the containers' deployment), the cloud's providers' adoption (the container's services: the AWS's ECS, the GKE, the AKS), and the ecosystem's explosion (the Compose, the Kubernetes, the Helm, the Istio, the Prometheus, the many's tools), and the observation: the Docker's impact was the containers' mainstream's driver.
-
The Docker's evolution: the OCI's
standards. The Docker's
contribution (the 2015's): the Docker
donated the
libcontainer's library to the OCI (therunc's project), and the Docker's design (the image's format, the runtime's interface) became the OCI's specifications (the Runtime's Specification, the Image's Specification, the Distribution's Specification), and the observation: the Docker's evolution was the standards' contribution (the containers' interoperability: the OCI's standards are the containers' ecosystem's foundation). -
The Docker's challenges: the
ecosystem's competition. The
Docker's challenges (the 2018's and the
after): the Kubernetes's dominance
(the orchestration's standard: the
Docker's Swarm's decline), the
containerd's and the CRI-O's adoption
(the runtimes' independence: the
Kubernetes's
dockershim's removal in the 1.24's), the Podman's and the Buildah's adoption (the daemonless's alternatives: the Red Hat's), and the observation: the Docker's challenges were the ecosystem's competition (the containers' ecosystem's diversity), and the observation: the Docker's response was the focus on the developers' experience (the Docker Desktop, the Docker Hub, the Compose) and the standards' compliance (the OCI's).
4.10 Common misconceptions
"Docker is the containers."
Docker is the platform (the images, the Dockerfile, the registries, the CLI, the ecosystem) 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 containers exist without the Docker (the Podman, the containerd, the CRI-O, the LXC, the Kubernetes's runtimes), and the observation is the reason the containers' ecosystem is the OCI's standards' (the interoperability, not the Docker's proprietary's formats).
"The Docker's image is the container."
The image is the read-only's template (the application and its dependencies: the layers and the metadata), and the container is the running's instance (the image's layers plus the writable's layer, the processes, the namespaces, the cgroups, the network, the volumes). 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 Docker's commands are the two's groups (the images' commands and the containers' commands).
"The Docker's containers are the secure by default."
The Docker's containers' security
depends on the configuration: the
default's configuration is the
reasonable (the dropped's capabilities,
the default's seccomp's profile, the
default's AppArmor's profile, the
non-root's user is the best's practice
but not the default), and the
dangerous's options (the
--privileged's flag, the
host's namespaces' sharing, the
sensitive's mounts, the
/var/run/docker.sock's
mount) are the common's
misconfigurations. The observation:
the Docker's containers' security is
the configuration's discipline, and
the Docker Bench's tool is the
configuration's assessment's mechanism.
"The Docker's daemon is the root's by necessity."
The Docker's daemon runs as the root in the default's configuration, but the rootless's Docker (the Docker's rootless's mode, the Podman's default) runs the daemon as the unprivileged's user with the user's namespace, and the observation: the rootless's Docker is the security's recommended's design for the unprivileged's users' workloads (the container's escape gives the attacker only the unprivileged's user's privileges).
"The Dockerfile's instructions are the the single's layer."
The Dockerfile's instructions produce
the multiple's layers (the
RUN, the COPY,
the ADD, the
ENV, the
WORKDIR, the
USER, the
LABEL's directives), and
the observation: the layers' design is
the build's cache's mechanism (the
instructions whose inputs are unchanged
reuse the cached's layers), and the
observation: the Dockerfile's
instructions' order matters (the
frequently's changing instructions
should be the last: the cache's
reuse).
"The Docker's volumes are the same as the bind mounts."
The volumes are the Docker's managed's
storage (the
/var/lib/docker/volumes/'s
directory: the Docker's lifecycle's
management, the backup's and the
restore's commands), and the bind
mounts are the host's directories or
files mounted into the container (the
-v /host/path:/container/path's
option: the host's file system's
dependency). The observation: the
volumes are the portable's and the
managed's (the recommended for the
persistent's data), and the bind mounts
are the host's specific's (the
development's and the configuration's
files).
"The Docker's containers are the persistent's by default."
The containers' writable's layer is the
ephemeral: the container's writes are
the lost when the container is removed
(the docker rm's command),
and the observation: the containers'
persistent's data requires the volumes
(the Docker's managed's storage) or the
bind mounts (the host's directories).
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).
"The Docker's Compose is the Kubernetes."
The Docker Compose is the
development's and the testing's
multi-container's applications' tool
(the compose.yaml's file,
the docker compose up's
command, the single's host's
deployment), and the Kubernetes is the
production's orchestration's platform
(the multi-host's clusters, the
declarative's manifests, the
self-healing, the scaling, the
service's discovery, the rolling's
updates). The observation: the
Compose's design is the Kubernetes's
manifests' ancestor (the Compose's
services and the Kubernetes's
Deployments and Services are the
analogous's concepts), and the
observation is the reason the modern's
practices use the Compose for the
development and the Kubernetes for the
production.
05 Worked example
The following example builds a container's image with the Dockerfile's and the multi-stage's builds, runs and inspects the container, and demonstrates the Docker Compose's multi-container's application.
Building an image with the multi-stage's Dockerfile
The following example builds a Go application's image with the multi-stage's Dockerfile:
# Create the application's directory
mkdir -p /tmp/goapp
cd /tmp/goapp
# Create the Go's application
cat > main.go << 'EOF'
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from the Go's container!\n")
})
http.ListenAndServe(":8080", nil)
}
EOF
# Create the go.mod's file
cat > go.mod << 'EOF'
module example.com/goapp
go 1.21
EOF
# Create the multi-stage's Dockerfile
cat > Dockerfile << 'EOF'
# The build's stage
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .
# The runtime's stage
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
EOF
# Create the .dockerignore's file
cat > .dockerignore << 'EOF'
.git
*.md
Dockerfile
.dockerignore
EOF
# Build the image
docker build -t goapp:v1.0 . 2>/dev/null
# Inspect the image's layers and size
docker images goapp:v1.0
docker history goapp:v1.0
docker inspect goapp:v1.0 2>/dev/null | head -40
# Run the container
docker run -d --name goapp-container \
-p 8080:8080 \
--memory 64m --cpus 0.5 \
--read-only --tmpfs /tmp \
--cap-drop=ALL \
--security-opt no-new-privileges \
goapp:v1.0 2>/dev/null
# Test the application
sleep 2
curl http://localhost:8080/ 2>/dev/null
# Inspect the container
docker ps 2>/dev/null
docker stats --no-stream goapp-container 2>/dev/null
docker inspect goapp-container 2>/dev/null | head -60
# The container's security's configuration
docker inspect goapp-container 2>/dev/null | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
c = data[0]
print('The user:', c['Config']['User'])
print('The capabilities:', c['HostConfig']['CapDrop'])
print('The read-only:', c['HostConfig']['ReadonlyRootfs'])
print('The security options:', c['HostConfig']['SecurityOpt'])
print('The memory limit:', c['HostConfig']['Memory'])
print('The CPU quota:', c['HostConfig']['NanoCpus'])
"
# The cleanup
docker rm -f goapp-container 2>/dev/null
docker rmi goapp:v1.0 2>/dev/null
rm -rf /tmp/goapp
The multi-stage's builds' findings:
| The stage | The base's image | The size | The contents |
|---|---|---|---|
| The build's stage | The golang:1.21-alpine's |
The ~250 MB | The Go's compiler, the source's code, the dependencies' cache |
| The runtime's stage | The distroless/static-debian12:nonroot's |
The ~2 MB | The compiled's binary (the static's, the stripped's) |
| The final's image | The runtime's stage | The ~2 MB | The compiled's binary (the build's stage's artifacts are the discarded) |
The multi-stage's builds' observation: the final's image is the ~2 MB (the runtime's stage: the compiled's binary only), and the build's stage's contents (the Go's compiler, the source's code) are the discarded. The observation is the reason the multi-stage's builds are the modern's images' best's practice: the smaller's images (the ~2 MB versus the ~250 MB with the single-stage's build), the smaller's attack surface (the fewer's packages: the Go's compiler's vulnerabilities are the excluded), and the faster's pulls (the smaller's layers).
The image's layers' analysis
The following commands analyze the image's
layers with the dive's tool (if
available) and the docker history's
command:
# The image's history (the layers and the instructions)
docker history goapp:v1.0
# The image's layers' sizes
docker inspect goapp:v1.0 2>/dev/null | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
c = data[0]
print('The layers:', len(c['RootFS']['Layers']))
for i, layer in enumerate(c['RootFS']['Layers']):
print(f' The layer {i}: {layer[:30]}...')
print('The image size:', c['Size'])
"
# The dive's tool (if available)
dive goapp:v1.0 --ci 2>/dev/null | head -40
# The image's config
docker inspect goapp:v1.0 2>/dev/null | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
c = data[0]
print('The entrypoint:', c['Config']['Entrypoint'])
print('The exposed ports:', c['Config']['ExposedPorts'])
print('The user:', c['Config']['User'])
print('The env:', c['Config']['Env'])
"
The image's layers' analysis's findings: the
final's image has the few's layers (the
distroless's base's layer and the compiled's
binary's layer), and the observation: the
Dockerfile's instructions' order and the
multi-stage's builds minimize the layers and
the sizes. The observation: the image's
analysis's tools (the dive's,
the docker history's) are the
image's optimization's and the security's
auditing's mechanisms.
The Docker Compose's multi-container's application
The following example demonstrates the Docker Compose's multi-container's application:
# Create the application's directory
mkdir -p /tmp/compose-app
cd /tmp/compose-app
# Create the compose.yaml's file
cat > compose.yaml << 'EOF'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
- app-net
depends_on:
- api
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 3
api:
image: python:3.11-alpine
command: python -m http.server 8000
working_dir: /data
volumes:
- ./data:/data:ro
networks:
- app-net
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:8000/"]
interval: 30s
timeout: 5s
retries: 3
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
networks:
app-net:
driver: bridge
EOF
# Create the content's directories
mkdir -p html data
echo "<h1>Hello from the Nginx's container!</h1>" > html/index.html
echo "Hello from the API's container!" > data/index.html
# Validate the configuration
docker compose config 2>/dev/null | head -40
# Start the application
docker compose up -d 2>/dev/null
# The application's state
docker compose ps 2>/dev/null
docker compose logs 2>/dev/null | head -20
# Test the application
sleep 3
curl http://localhost:8080/ 2>/dev/null
# The services' inspection
docker compose exec api ls -la /data 2>/dev/null
docker compose exec db psql -U user -d mydb -c '\l' 2>/dev/null
# The services' scaling (the demonstration)
docker compose up -d --scale api=2 2>/dev/null
docker compose ps 2>/dev/null
# The cleanup
docker compose down -v 2>/dev/null
rm -rf /tmp/compose-app
The Compose's findings: the
compose.yaml's file defines the
three's services (the web's
Nginx, the api's Python's HTTP
server, the db's PostgreSQL),
the networks (the app-net's
bridge's network that the services share),
and the volumes (the db-data's
volume for the database's data). The
observation: the Compose's design is the
multi-container's applications' definition
(the services, the networks, the volumes,
the dependencies, the health's checks), and
the observation: the Compose's services and
the Kubernetes's Deployments and Services
are the analogous's concepts (the
docker compose up's command
is the single's host's deployment, and the
kubectl apply's command is
the multi-host's cluster's deployment).
The Docker's security's inspection
The following commands inspect the Docker's security's configuration:
# The Docker's daemon's configuration
cat /etc/docker/daemon.json 2>/dev/null
ps -ef | grep dockerd | head -1
# The Docker's daemon's socket's permissions
ls -la /var/run/docker.sock
# The Docker's info
docker info 2>/dev/null | head -40
# The container's security's configuration
docker inspect goapp-container 2>/dev/null | \
python3 -c "
import sys, json
data = json.load(sys.stdin)
c = data[0]
print('=== The security ===')
print('The user:', c['Config']['User'])
print('The capabilities:', c['HostConfig']['CapDrop'])
print('The read-only:', c['HostConfig']['ReadonlyRootfs'])
print('The privileged:', c['HostConfig']['Privileged'])
print('The security options:', c['HostConfig']['SecurityOpt'])
print('The pid mode:', c['HostConfig']['PidMode'])
print('The network mode:', c['HostConfig']['NetworkMode'])
print('The ipc mode:', c['HostConfig']['IpcMode'])
"
# The Docker Bench for Security (if available)
which docker-bench-security 2>/dev/null
git clone https://github.com/docker/docker-bench-security.git /tmp/dbs 2>/dev/null
cd /tmp/dbs && sudo ./docker-bench-security.sh 2>/dev/null | head -60
rm -rf /tmp/dbs
# The image's scanning (if the trivy's tool is available)
trivy image goapp:v1.0 2>/dev/null | head -30
The Docker's security's inspection's
findings: the container's security's
configuration (the non-root's user, the
dropped's capabilities, the read-only's
file system, the security's options: the
no-new-privileges's setting,
the default's seccomp's profile), the
Docker's daemon's configuration (the
/var/run/docker.sock's
permissions: the docker's
group's members are the root's
equivalent), the Docker Bench's checks
(the CIS's benchmark's results), and the
image's scanning (the Trivy's
vulnerabilities' report). The observation:
the Docker's security's configuration's
inspection and the image's scanning are
the Docker's security's operations's
first steps, and the observation: the
Docker Bench's tool and the scanning's
tools (the Trivy's, the Grype's, the
Clair's) are the Docker's security's
assessment's mechanisms.
06 Interactive activity
Activity · explore Docker on a live system
The goal of this activity is to use the
Docker's (or the Podman's) command line to
build an image, to run and inspect a
container, to use the Docker Compose, and to
examine the Docker's security. Work through
the steps in your course VM. Note:
the Docker's daemon requires the root's
privileges (or the rootless's mode, or the
Podman's daemonless's tool); the activity's
commands use the docker's
command, and the podman's
command is the compatible's alternative.
-
Check the Docker's installation.
Verify the Docker's (or the Podman's)
installation and the daemon's status.
Is the Docker (or the Podman) installed? Is the daemon running? What are the Docker's version and the components? Write down what you find.# Check the tools which docker podman 2>/dev/null docker --version 2>/dev/null docker compose version 2>/dev/null # Check the daemon's status systemctl status docker 2>/dev/null | head -5 systemctl status podman 2>/dev/null | head -5 # The Docker's info docker info 2>/dev/null | head -40 # The Docker's daemon's socket ls -la /var/run/docker.sock 2>/dev/null # The Docker's version and the components docker version 2>/dev/null # The Docker's context docker context ls 2>/dev/null -
Pull and inspect an image.
Pull an image and inspect its layers,
its configuration, and its history.
How many layers does the image have? What is the image's size? What are the entrypoint, the command, the exposed ports, and the environment's variables? Write down your findings.# Pull the image docker pull nginx:alpine 2>/dev/null # The image's information docker images 2>/dev/null docker inspect nginx:alpine 2>/dev/null | head -40 # The image's layers docker history nginx:alpine 2>/dev/null # The image's configuration docker inspect nginx:alpine 2>/dev/null | \ python3 -c " import sys, json data = json.load(sys.stdin) c = data[0] print('The entrypoint:', c['Config']['Entrypoint']) print('The cmd:', c['Config']['Cmd']) print('The exposed ports:', c['Config']['ExposedPorts']) print('The env:', c['Config']['Env']) print('The size:', c['Size']) " # The image's layers' count docker inspect nginx:alpine 2>/dev/null | \ python3 -c " import sys, json data = json.load(sys.stdin) c = data[0] print('The layers:', len(c['RootFS']['Layers'])) " -
Run and inspect a container.
Run a container with the security's
options and inspect its state.
What is the container's state? What are the security's options (the non-root's user, the dropped's capabilities, the read-only's file system)? What does the container's# Run the container docker run -d --name nginx-container \ -p 8080:80 \ --memory 128m --cpus 0.5 \ --read-only --tmpfs /var/cache/nginx --tmpfs /var/run \ --cap-drop=ALL --cap-add=CAP_NET_BIND_SERVICE \ --security-opt no-new-privileges \ nginx:alpine 2>/dev/null # The container's state docker ps 2>/dev/null docker stats --no-stream nginx-container 2>/dev/null docker top nginx-container 2>/dev/null # The container's logs docker logs nginx-container 2>/dev/null | head -10 # Test the application curl http://localhost:8080/ 2>/dev/null | head -5 # The container's inspection docker inspect nginx-container 2>/dev/null | head -60 # The container's security's configuration docker inspect nginx-container 2>/dev/null | \ python3 -c " import sys, json data = json.load(sys.stdin) c = data[0] print('The user:', c['Config']['User']) print('The capabilities:', c['HostConfig']['CapDrop'], c['HostConfig']['CapAdd']) print('The read-only:', c['HostConfig']['ReadonlyRootfs']) print('The security options:', c['HostConfig']['SecurityOpt']) print('The memory limit:', c['HostConfig']['Memory']) print('The CPU quota:', c['HostConfig']['NanoCpus']) " # The container's file system's changes docker exec nginx-container ls / 2>/dev/null docker diff nginx-container 2>/dev/null | head -10 # The container's exec docker exec nginx-container ps aux 2>/dev/null docker exec nginx-container cat /etc/os-release 2>/dev/null # The cleanup docker rm -f nginx-container 2>/dev/null/etc/os-releaseshow? How do the Docker's options map to the kernel's mechanisms? Write down what you find. -
Build an image.
Build an image from a Dockerfile and
inspect the layers and the cache.
How many layers does the image have? What does the second's build's cache's output show? Is the container's health's check working? How does the Dockerfile's instructions' order affect the build's cache? Write down your observations.# Create the application's directory mkdir -p /tmp/myapp cd /tmp/myapp # Create a simple's application cat > 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 Docker\'s container!\n') if __name__ == '__main__': HTTPServer(('0.0.0.0', 8080), Handler).serve_forever() EOF # Create the Dockerfile cat > Dockerfile << 'EOF' FROM python:3.11-alpine WORKDIR /app COPY app.py . RUN adduser -D -u 1000 appuser USER appuser EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=3s \ CMD wget -q -O - http://localhost:8080/ || exit 1 CMD ["python", "app.py"] EOF # Build the image docker build -t myapp:v1.0 . 2>/dev/null # Inspect the image docker images myapp:v1.0 2>/dev/null docker history myapp:v1.0 2>/dev/null # The build's cache (the second's build) docker build -t myapp:v1.0 . 2>&1 | grep -i 'cached\|using cache' | head -10 # The image's layers docker inspect myapp:v1.0 2>/dev/null | \ python3 -c " import sys, json data = json.load(sys.stdin) c = data[0] print('The layers:', len(c['RootFS']['Layers'])) print('The size:', c['Size']) print('The user:', c['Config']['User']) " # Run the container docker run -d --name myapp-container -p 8080:8080 myapp:v1.0 2>/dev/null sleep 2 curl http://localhost:8080/ 2>/dev/null docker ps 2>/dev/null docker inspect myapp-container 2>/dev/null | grep -A5 Health # The cleanup docker rm -f myapp-container 2>/dev/null docker rmi myapp:v1.0 2>/dev/null rm -rf /tmp/myapp -
Use the Docker Compose.
Create and run a multi-container's
application with the Docker Compose.
What are the application's services? How do the services communicate (the network's inspection)? How does the Compose's design work? Write down what you find.# Create the application's directory mkdir -p /tmp/compose-app cd /tmp/compose-app # Create the compose.yaml's file cat > compose.yaml << 'EOF' services: web: image: nginx:alpine ports: - "8080:80" networks: - app-net depends_on: - api api: image: python:3.11-alpine command: python -m http.server 8000 working_dir: /data volumes: - ./data:/data:ro networks: - app-net networks: app-net: driver: bridge EOF # Create the content mkdir -p data echo "Hello from the API's container!" > data/index.html # Validate and start docker compose config 2>/dev/null | head -30 docker compose up -d 2>/dev/null # The application's state docker compose ps 2>/dev/null docker compose logs 2>/dev/null | head -20 # Test the application sleep 2 curl http://localhost:8080/ 2>/dev/null | head -5 # The network's inspection docker network ls 2>/dev/null docker network inspect compose-app_app-net 2>/dev/null | head -30 # The services' exec docker compose exec api ls -la /data 2>/dev/null # The cleanup docker compose down -v 2>/dev/null rm -rf /tmp/compose-app -
Inspect the Docker's security.
Inspect the Docker's daemon's
configuration and the containers'
security.
What are the Docker's daemon's configuration's security's settings? What are the container's namespaces, capabilities, seccomp's status, and cgroups' limits? What does the Docker Bench's output show? Write down what you find.# The Docker's daemon's configuration cat /etc/docker/daemon.json 2>/dev/null ps -ef | grep dockerd | head -1 ls -la /var/run/docker.sock # The Docker's info docker info 2>/dev/null | grep -E 'Security|Runtime|Storage|Cgroup' # The container's namespaces CONTAINER_PID=$(docker inspect -f '{{.State.Pid}}' myapp-container 2>/dev/null) ls -la /proc/$CONTAINER_PID/ns/ 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 cgroup's limits cat /sys/fs/cgroup/system.slice/docker-*.scope/memory.max 2>/dev/null cat /sys/fs/cgroup/system.slice/docker-*.scope/cpu.max 2>/dev/null # The Docker Bench for Security (if available) git clone https://github.com/docker/docker-bench-security.git /tmp/dbs 2>/dev/null cd /tmp/dbs && sudo ./docker-bench-security.sh 2>/dev/null | head -50 rm -rf /tmp/dbs -
Compare the Docker and the
Podman.
If both the Docker and the Podman are
available, compare their designs:
How do the Docker's and the Podman's architectures differ (the daemon vs. the daemonless)? How do the rootless's modes compare? Are the images the compatible (the OCI's standards)? Write down your observations.# The Docker's architecture docker version 2>/dev/null systemctl status docker 2>/dev/null | head -3 # The Podman's architecture podman version 2>/dev/null systemctl status podman 2>/dev/null | head -3 podman info 2>/dev/null | head -20 # The daemonless's comparison ps -ef | grep -E 'dockerd|containerd|podman' | grep -v grep # The rootless's comparison docker info 2>/dev/null | grep -i rootless podman info 2>/dev/null | grep -i rootless # The images' compatibility docker pull nginx:alpine 2>/dev/null podman pull docker.io/library/nginx:alpine 2>/dev/null docker images 2>/dev/null podman images 2>/dev/null -
Read the man pages.
Read the man pages that document the
Docker's interfaces.
For each, write a sentence describing its main contribution to your understanding of the Docker. Theman 1 docker man 1 docker-run man 1 docker-build man 1 docker-exec man 1 docker-ps man 1 docker-inspect man 1 docker-logs man 1 docker-network man 1 docker-volume man 1 docker-compose man 5 Dockerfile man 5 docker-compose.yml man 1 podman man 1 buildah man 1 skopeo man 1 nerdctlman 1 docker-runpage documents the containers' creation; theman 5 Dockerfilepage documents the images' construction; theman 1 docker-composepage documents the multi-container's applications.
Reflect: the Docker is the platform that made the containers the accessible to the developers, and its contribution was the user experience (the images, the Dockerfile, the registries, the CLI, the ecosystem) more than the kernel's mechanisms (the namespaces, the cgroups, the union file systems). The Docker's design's trade-offs (the daemon's root's privileges versus the rootless's mode, the convenience versus the security, the images' sizes versus the multi-stage's builds) are the engineering's and the operations's ongoing's concerns. Which of the Docker's features is the most important for the modern's workloads, and why? What would you change about the Docker's design if you could? How would you explain the Docker's images' layers and the build's cache to someone who has never thought about it? What is the right balance between the Docker's convenience and the containers' security?
07 Knowledge check
Check your understanding
Question 1
Define Docker and describe its architecture (the client, the daemon, the containerd, the runc, the BuildKit). What is each component's role, and how do the components interact?
Show answer
docker's CLI: the
user's interface that sends the
requests to the daemon over the
REST's API: the verbs (the
build, the
run, the
pull, the
push, the
ps, the
exec, the
logs, the
inspect, the
stop, the
rm, the
rmi, the
network, the
volume, the
compose); the
daemon (the
dockerd: the server
that receives the client's
requests (the REST's API) and
manages the Docker's objects (the
images, the containers, the
networks, the volumes, the
plugins)); the
containerd (the
high-level's runtime (the
Docker's contribution): the
component that manages 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 that delegates
the container's creation to the
low-level's runtime); the
runc (the
low-level's runtime (the OCI's
Runtime's Specification's
implementation): the component
that creates 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: the short-lived's
process); and the
BuildKit (the
modern's builder (the Docker's,
the 2018's): the component that
builds the images from the
Dockerfiles with the performance's
and the security's improvements
(the parallel's builds, the
cache's improvements, the
rootless's builds, the secrets'
mounts, the SSH's mounts, the
multi-platform's builds)). The
components' interaction: the
client sends the requests to the
daemon (the REST's API over the
Unix's socket or the TCP's
connection); the daemon manages
the Docker's objects and
delegates the container's
lifecycle to the containerd (the
gRPC's API); the containerd
pulls the images (the OCI's
Image's Specification's format)
from the registries (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 runc to
create and start the container;
the runc creates the
container (the namespaces, the
cgroups, the root's file system,
the capabilities, the seccomp's
filters) and exits (the
short-lived's process). The
observation: the components'
separation of the concerns
(the client's, the daemon's, the
runtime's, the builder's) is the
Docker's architecture's
flexibility's and the OCI's
standards' compliance's
foundation.
Question 2
Describe the Docker's objects (the images, the containers, the networks, the volumes, the plugins). What is each object's purpose, and what are the commands that manage them?
Show answer
docker build (the
Dockerfile's construction), the
docker pull (the
registry's download), the
docker push (the
registry's upload), the
docker tag (the naming),
the docker images (the
listing), the docker inspect
(the inspection), the
docker history (the
layers' history), the
docker rmi (the
removal)); the container
(the running's instance: the image's
layers plus the writable's layer,
the processes, the namespaces, the
cgroups, the network's interfaces,
the volumes; the commands: the
docker run (the creation
and the start), the
docker create (the
creation), the docker start
(the start), the docker stop
(the stop), the docker restart
(the restart), the docker pause
(the pause), the docker unpause
(the resume), the docker ps
(the listing), the docker exec
(the command's execution inside the
container), the docker logs
(the logs), the docker inspect
(the inspection), the
docker stats (the
statistics), the docker top
(the processes), the docker diff
(the file system's changes), the
docker commit (the new
image from the container), the
docker rm (the removal));
the network (the
isolated's network: the bridge's
networks (the default's: the private's
network on the host's bridge), the
overlay's networks (the multi-host's:
the Docker Swarm's), the macvlan's
networks (the containers get the MAC's
addresses), the host's networks (the
container shares the host's network),
and the none's networks; the commands:
the docker network create,
the docker network ls,
the docker network inspect,
the docker network connect,
the docker network disconnect,
the docker network rm);
the volume (the
persistent's storage: the volumes (the
Docker's managed's storage: the
/var/lib/docker/volumes/'s
directory), the bind mounts (the
host's directories or the files
mounted into the container: the
-v /host/path:/container/path's
option), and the tmpfs's mounts (the
memory's file systems); the commands:
the docker volume create,
the docker volume ls,
the docker volume inspect,
the docker volume rm, the
docker volume prune);
and the plugin (the
extensibility: the storage's plugins
(the volumes' drivers: the cloud's
storage's integrations), the network's
plugins (the CNI's, the overlay's),
the logging's plugins (the logs'
drivers: the syslog's, the fluentd's,
the journald's, the splunk's), the
authorization's plugins, and the
metrics's plugins; the commands: the
docker plugin install,
the docker plugin ls,
the docker plugin inspect,
the docker plugin rm).
Question 3
Describe the Dockerfile's directives (the
FROM, the RUN,
the COPY, the
CMD, the
ENTRYPOINT, the
ENV, the ARG,
the WORKDIR, the
EXPOSE, the
USER, the
VOLUME, the
HEALTHCHECK, the
LABEL, the
SHELL, the
STOPSIGNAL, the
ONBUILD) and the layers'
and the build's cache's mechanisms. What
is each directive's purpose, and what are
the Dockerfile's best's practices (the
instructions' order, the combined's
RUN's directives, the
.dockerignore's file, and
the multi-stage's builds)?
Show answer
FROM
(the base's image: the mandatory's
first's instruction); the
RUN
(the command's execution during the
build: the new's layer); the
COPY
(the files' and the directories'
copying from the build's context into
the image); the
ADD
(the COPY's superset:
the remote's URLs and the local's
archives' extraction); the
CMD
(the default's command: the
docker run's arguments
override it); the
ENTRYPOINT
(the container's executable: the
CMD's directive supplies
the default's arguments); the
ENV
(the environment's variables: the
persisted in the image); the
ARG
(the build-time's variables: the
not the persisted in the image); the
WORKDIR
(the working's directory: the
RUN, the CMD,
the ENTRYPOINT, the
COPY, the
ADD's directives' base);
the EXPOSE
(the port's documentation: the
metadata, not the publishing: the
-p's option); the
USER
(the user's specification: the
container's processes run as the
specified's user: the security's
best's practice: the non-root's
user); the
VOLUME
(the volume's declaration: the
data's persistence's boundary); the
HEALTHCHECK
(the health's check: the command
that the Docker runs to check the
container's health); the
LABEL
(the metadata's labels: the
author, the version, the license);
the SHELL
(the shell's specification: the
RUN, the CMD,
the ENTRYPOINT's
shell's forms' base); the
STOPSIGNAL
(the signal's specification: the
docker stop's signal);
and the ONBUILD
(the deferred's instruction: the
executed when the image is used as
the base's image). The Dockerfile's
layers and the
build's cache: each
instruction produces a layer (the
RUN, the
COPY, the
ADD, the
ENV, the
WORKDIR, the
USER, the
LABEL), and the
Docker's builder caches the layers:
the builder reuses the cached's
layers for the instructions whose
inputs are unchanged (the same's
base's image, the same's commands,
the same's copied files), and it
re-executes the instructions whose
inputs changed (the layer and the
subsequent's layers). The
Dockerfile's best's
practices: the
layer's ordering (the
dependencies' installation first,
the application's code last: the
cache's reuse across the builds);
the combined's
RUN's directives
(the fewer's layers: the
RUN apt-get update &&
apt-get install -y ... &&
rm -rf /var/lib/apt/lists/*'s
pattern); the .dockerignore's
file (the build's context's
exclusion: the unnecessary's files'
exclusion: the smaller's context,
the faster's builds, the secrets'
leakage's prevention); and the
multi-stage's builds (the
separation's of the build's and the
runtime's dependencies: the
smaller's images, the smaller's
attack surface, the faster's
pulls).
Question 4
Describe the Docker's images (the tags, the digests, the registries) and the Docker's containers (the lifecycle, the networking, the storage, the configuration). What are the key mechanisms, and how do the Docker's commands map to the kernel's mechanisms?
Show answer
registry/namespace/name:tag's
format: the latest's tag
is the conventional's "the most
recent's"; the tags are the
convenient's references, and the
production's deployments should use
the digests); the digests
(the content-addressable's identifiers:
the sha256:...'s hash of
the image's manifest: the immutable's
references: the same's digest is
always the same's image: the supply
chain's security's references); and
the registries (the images'
repositories: the Docker Hub (the
public's: the docker.io's
registry), the private's registries
(the Harbor, the Nexus, the
Artifactory), and the cloud's
providers' registries (the AWS's ECR,
the Azure's ACR, the Google's
Artifact Registry, the GitHub's
Container Registry); the commands:
the docker login, the
docker pull, the
docker push, the
docker search, the
docker manifest). The
Docker's containers:
the lifecycle (the
docker create: the
created's state; the
docker start: the
running's state; the
docker pause's and the
docker unpause's: the
paused's state: the
cgroup's freezer's controller; the
docker stop: the
SIGTERM's signal, the grace's period,
the SIGKILL's signal: the
exited's state; the
docker kill: the forced's
stop; the docker rm: the
removed's state); the
networking (the network's
drivers: the bridge's
(the default's: the private's network
on the host's bridge), the
host's (the container
shares the host's network's
namespace), the overlay's
(the multi-host's: the Docker
Swarm's), the macvlan's
(the containers get the MAC's
addresses), the none's
(the container has no network); the
port's mapping: the
-p host_port:container_port's
option; the DNS's resolution: the
container's name's resolution within
the user-defined's networks); the
storage (the writable's
layer: the container's file system's
changes (the overlayfs's
upper's directory); the volumes: the
-v volume_name:/path's
option; the bind mounts: the
-v /host/path:/container/path's
option; the tmpfs's mounts: the
--tmpfs /path's
option); and the
configuration (the
environment's variables: the
-e KEY=value's and the
--env-file .env's
options; the container's name: the
--name myapp's option;
the restart's policy: the
--restart always's,
the unless-stopped's,
and the on-failure's
policies; the labels: the
--label key=value's
option; the health's check: the
--health-cmd's and the
--health-interval's
options). The Docker's commands'
mapping to the kernel's mechanisms:
the --memory's and the
--cpus's options map to
the cgroups' memory's
and cpu's controllers;
the --cap-drop's and
the --cap-add's
options map to the Linux's
capabilities; the
--security-opt seccomp='s
option maps to the seccomp's BPF's
filters; the
--security-opt label='s
option maps to the SELinux's and the
AppArmor's profiles; the
--read-only's option
maps to the read-only's root's file
system (the mount's namespace); the
--user's option maps
to the user's namespace's mapping;
and the observation: the Docker's
commands are the kernel's
mechanisms' friendly's interfaces,
and the observation is the reason
the Docker's security's
configuration is the kernel's
mechanisms' configuration.
Question 5
Describe the Docker Compose (the multi-container's applications) and the Docker's security (the considerations, the best practices). What are the Docker's ecosystem's components (the BuildKit, the Buildx, the Swarm, the plugins), and what was the Docker's role in the containers' adoption?
Show answer
compose.yaml's (or the
docker-compose.yml's)
file declares the application's
services (the each's service is the
container: the image, the build's
context, the ports, the volumes, the
environment's variables, the
dependencies, the networks, the
resource's limits, the health's
checks, the restart's policy), the
networks (the Compose's networks: the
default's or the custom's), and the
volumes (the Compose's volumes: the
named's volumes, the bind mounts,
the tmpfs). The Compose's commands:
the docker compose up
(the services' creation and the
start: the -d's option
for the detached's mode), the
docker compose down
(the services' stop and the
resources' removal), the
docker compose ps, the
docker compose logs,
the docker compose exec,
the docker compose build,
the docker compose pull,
the docker compose config,
the docker compose scale
(the deprecated: the
deploy.replicas's
option is the modern's mechanism). The
Compose's design's observation: the
Compose is the development's and the
testing's multi-container's
applications' tool (the single's
host's deployment), and the Compose's
services and the Kubernetes's
Deployments and Services are the
analogous's concepts. The
Docker's security:
the daemon's security (the daemon
runs as the root: the attack surface
is the significant: the Unix's
socket's permissions, the TLS's
certificates, the authorization's
plugins, the rootless's Docker); the
container's security (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 resource's limits, the
--privileged's flag's
avoidance, the host's namespaces'
sharing's avoidance, the sensitive's
mounts' avoidance: the
/var/run/docker.sock's
mount); the image's security (the
minimal's images, the images'
scanning, the images' signing, the
images' attestation, the SBOM's
generation); the registry's security
(the authentication, the
authorization, the images' signing
and the verification, the images'
scanning); the secrets' management
(the --env-file's
option, the Docker's secrets, the
BuildKit's
RUN --mount=type=secret's
directive, the external's secret's
stores); and the runtime's security's
tools (the Falco's, the Sysdig's,
the Tetragon's, the Docker Bench's).
The Docker's ecosystem:
the BuildKit (the
modern's builder: the parallel's
builds, the cache's improvements,
the rootless's builds, the secrets'
mounts, the SSH's mounts, the
multi-platform's builds); the
Buildx (the
Docker's CLI's plugin (the
BuildKit's frontend): the
docker buildx's
commands, the multi-platform's
builds); the Docker Swarm
(the Docker's native's orchestration:
the swarm, the services, the nodes,
the overlay's networks, the secrets,
the configs, the stacks); the
Docker Desktop (the
developer's environment: the daemon,
the CLI, the Compose, the
Kubernetes's single-node's cluster,
the Buildx, the GUI); the
Docker Hub (the
public's registry: the official's
images, the verified's publishers,
the community's images, the
automated's builds); and the
plugins (the
storage's, the network's, the
logging's, the authorization's, the
metrics's plugins). The Docker's
role in the containers'
adoption: the Docker's
contribution was the user
experience (the images, the
Dockerfile, the registries, the CLI,
the ecosystem) more than the kernel's
mechanisms (the namespaces, the
cgroups, the union file systems
existed for the years before the
Docker); the Docker's release (the
2013's) made the containers the
mainstream (the developers' adoption,
the operations' adoption, the
cloud's providers' adoption, the
ecosystem's explosion); the Docker's
evolution was the standards'
contribution (the OCI's
specifications: the Runtime's, the
Image's, the Distribution's); and the
Docker's challenges (the Kubernetes's
dominance, the containerd's and the
CRI-O's adoption, the Podman's and the
Buildah's adoption) were the
ecosystem's competition, and the
Docker's response was the focus on
the developers' experience (the
Docker Desktop, the Docker Hub, the
Compose) and the standards'
compliance (the OCI's).
08 Unit discussion
Discussion prompt
Docker is the platform that made the containers the mainstream, and its history is the history of the containers' adoption. The containers' kernel's mechanisms (the namespaces, the cgroups, the union file systems) existed for the years before the Docker (the LXC's release was the 2008's), and the Docker's release (the 2013's) was the containers' explosion's driver. The Docker's contribution was the user experience: the images (the application and its dependencies in the single's artifact: the "it works on my machine" problem's solution), the Dockerfile (the declarative's build's format: the reproducible's image's construction), the registries (the images' distribution: the Docker Hub's and the private's registries), the CLI (the simple's commands), and the ecosystem (the Compose, the Swarm, the Desktop, the Hub, the plugins). Each contribution was the response to the developers' and the operators' needs, and each was shaped by the workloads of its time.
The trend is toward the standards and the ecosystem's diversity. The Docker's design (the image's format, the runtime's interface) became the OCI's specifications (the Runtime's Specification, the Image's Specification, the Distribution's Specification), and the observation: the Docker's evolution was the standards' contribution (the containers' interoperability: the OCI's standards are the containers' ecosystem's foundation). The ecosystem's diversity: the Docker (the engine, the CLI, the Desktop, the Hub), the Podman (the daemonless's containers: the rootless's default), the containerd (the standalone's runtime: the Kubernetes's), the CRI-O (the Kubernetes's runtime), the nerdctl (the containerd's CLI: the Docker's compatible's interface), the Buildah (the images' building without the daemon), the Kaniko (the images' building in the Kubernetes's clusters), and the observation: the ecosystem's diversity is the containers' maturity's evidence (the multiple's implementations of the same's standards).
The trend also raises a question about the developer's experience versus the operator's requirements. The Docker's design prioritized the developer's experience (the simple's commands, the images, the Dockerfile, the Docker Desktop), and the observation is the reason the Docker's adoption was the developers' driver. But the operators' requirements (the security, the observability, the orchestration, the multi-tenancy) are the different's concerns, and the observation is the reason the operators' platforms (the Kubernetes, the OpenShift, the cloud's providers) build on the OCI's standards (the images, the runtimes) rather than the Docker's specific's components (the daemon, the CLI, the Swarm). The observation: the Docker's developer's experience and the operators' requirements are the two sides of the containers' design, and the observation is the reason the modern's practices combine the Docker (the development) with the Kubernetes (the production).
Which of the Docker's contributions (the images, the Dockerfile, the registries, the CLI, the ecosystem) is the most important for the containers' adoption, and why? Should the containers' design prioritize the developer's experience (the Docker's simplicity) or the operator's requirements (the Kubernetes's features)? What is the right balance between the Docker's convenience and the containers' security? How should the Docker's design evolve for the serverless's platforms, the WebAssembly's containers, 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.5. Aim to spend 60–90 minutes on it.
-
Compare the Docker and the
alternatives.
Write a short essay (400–500 words)
comparing the Docker with the
alternatives (the Podman, the
containerd, the CRI-O, the nerdctl, the
Buildah, the Kaniko). Your essay should
address:
- Each tool's architecture (the daemon vs. the daemonless, the engine vs. the runtime).
- Each tool's advantages and disadvantages.
- Each tool's use's cases (the development, the production, the CI/CD, the Kubernetes's clusters).
- The tools' compatibility (the OCI's standards).
- The tools' relationships (the Docker's components: the containerd and the runc are the separate's projects).
-
Design a Dockerfile.
A Node.js application needs an image
for the production's deployment. The
application's requirements: the Node.js
runtime (the version 20), the
dependencies (the
npm ci's installation), the application's code, the non-root's user, the port 3000, the health's check (the HTTP's request to the/health's endpoint), and the minimal's image. Design the Dockerfile:- The multi-stage's build (the build's stage: the dependencies' installation, the build's commands; the runtime's stage: the minimal's base, the artifacts' copying).
- The instructions' order (the cache's optimization).
- The
.dockerignore's file. - The security's best's practices (the non-root's user, the read-only's file system, the health's check).
- The image's size's optimization
(the
node:20-alpine's base, thenpm ci --omit=dev's dependencies).
-
Build and analyze an image.
On your VM, build an image and analyze
its layers and its size:
What is the size's difference between the single-stage's and the multi-stage's images? What are the layers' differences? Write a report (500–750 words) that explains the differences and the multi-stage's builds' advantages.# Create the application's directory mkdir -p /tmp/analyze-app cd /tmp/analyze-app # Create the application's files cat > app.py << 'EOF' from http.server import HTTPServer, BaseHTTPRequestHandler class Handler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(b'Hello!\n') if __name__ == '__main__': HTTPServer(('0.0.0.0', 8080), Handler).serve_forever() EOF # The single-stage's Dockerfile cat > Dockerfile.single << 'EOF' FROM python:3.11 WORKDIR /app COPY app.py . RUN pip install requests CMD ["python", "app.py"] EOF # The multi-stage's Dockerfile cat > Dockerfile.multi << 'EOF' FROM python:3.11-alpine AS builder WORKDIR /app RUN pip install --target=/install requests FROM python:3.11-alpine WORKDIR /app COPY --from=builder /install /usr/local/lib/python3.11/site-packages COPY app.py . RUN adduser -D -u 1000 appuser USER appuser CMD ["python", "app.py"] EOF # Build the single-stage's image docker build -f Dockerfile.single -t analyze-single . 2>/dev/null docker images analyze-single # Build the multi-stage's image docker build -f Dockerfile.multi -t analyze-multi . 2>/dev/null docker images analyze-multi # Compare the sizes and the layers docker history analyze-single docker history analyze-multi # The analysis's tools docker inspect analyze-single 2>/dev/null | \ python3 -c "import sys, json; print('The single size:', json.load(sys.stdin)[0]['Size'])" docker inspect analyze-multi 2>/dev/null | \ python3 -c "import sys, json; print('The multi size:', json.load(sys.stdin)[0]['Size'])" # The cleanup docker rmi analyze-single analyze-multi 2>/dev/null rm -rf /tmp/analyze-app -
Design a Docker Compose's
application.
A web application needs the multi-container's
environment: the web's frontend (the
Nginx's), the API's backend (the
Python's FastAPI's), the database (the
PostgreSQL's), the cache (the Redis's),
and the message's queue (the RabbitMQ's).
Design the
compose.yaml's file:- The services' definitions (the images, the build's contexts, the commands, the environment's variables, the ports).
- The networks (the public's and the private's networks, the services' isolation).
- The volumes (the database's data, the upload's data, the cache's persistence).
- The dependencies
(the
depends_on's conditions, the health's checks). - The resource's limits (the CPU, the memory, the pids).
- The health's checks (the each's service).
- The restart's policies.
- The secrets' management (the environment's variables, the Docker's secrets, the external's secrets).
-
Analyze the Docker's security.
On your VM, inspect the Docker's
security's configuration and the
containers' security:
What are the Docker's daemon's and the containers' security's configurations? What does the Docker Bench's output show? What are the most important remediations? Write a report (500–750 words).# The Docker's daemon's configuration cat /etc/docker/daemon.json 2>/dev/null ls -la /var/run/docker.sock # The Docker's info docker info 2>/dev/null | grep -iE 'security|rootless|cgroup|storage' # The containers' security's configuration docker ps -a --format '{{.Names}}' 2>/dev/null | while read name; do echo "=== $name ===" docker inspect $name 2>/dev/null | \ python3 -c " import sys, json data = json.load(sys.stdin) c = data[0] print(' The user:', c['Config']['User']) print(' The privileged:', c['HostConfig']['Privileged']) print(' The read-only:', c['HostConfig']['ReadonlyRootfs']) print(' The capabilities:', c['HostConfig']['CapDrop']) print(' The security options:', c['HostConfig']['SecurityOpt']) " done # The Docker Bench for Security (if available) git clone https://github.com/docker/docker-bench-security.git /tmp/dbs 2>/dev/null cd /tmp/dbs && sudo ./docker-bench-security.sh 2>/dev/null | tail -40 rm -rf /tmp/dbs # The images' scanning (if the trivy's tool is available) trivy image nginx:alpine 2>/dev/null | head -30 -
Analyze the images' supply
chain.
Choose a popular's image (the
nginx:alpine's, thepostgres:16-alpine's, thenode:20-alpine's) and analyze its supply chain:- The image's base (the
alpine's, thedebian's). - The image's layers and their sizes.
- The image's vulnerabilities (the scanning's report).
- The image's signing and the attestation (the Docker Content Trust, the Cosign).
- The image's SBOM (the Syft's or the Trivy's report).
- The image's update's strategy.
- The image's base (the
-
Design a Docker's workflow.
A team is adopting the Docker for its
development's and deployment's
workflows. Design the workflow:
- The development's environments (the Docker Desktop, the Compose, the devcontainers).
- The images' strategy (the base's images, the minimal's images, the multi-stage's builds, the labels, the scanning).
- The CI/CD's pipelines (the images' building, the scanning, the signing, the pushing).
- The registry's strategy (the Docker Hub's, the private's, the cloud's providers').
- The deployment's strategy (the Compose's for the development, the Kubernetes's or the Docker Swarm's for the production).
- The security's practices (the non-root's users, the read-only's file systems, the dropped's capabilities, the secrets' management, the monitoring).
-
Compare the Docker's images'
optimization's techniques.
Write a short paragraph (200–250 words)
comparing the Docker's images'
optimization's techniques (the
multi-stage's builds, the
.dockerignore's file, the combined'sRUN's directives, the minimal's base's images, the BuildKit's cache's mounts, the images' layers' squashing). Your paragraph should address:- Each technique's mechanism.
- Each technique's benefits (the image's size, the build's speed, the cache's reuse, the security).
- Each technique's trade-offs.
- The techniques' combinations.
- Reflect. In your own words, explain the Docker's platform and its contribution to the containers' adoption, and describe the Docker's objects, the Dockerfile, the images, the containers, the Compose, and the ecosystem. Then describe one situation in your own experience — a Docker's image you have built, a Compose's application you have run, or a container's configuration you have administered — where the Docker's design affected the outcome. What was the situation, and how would a different configuration have changed the outcome?
10 Summary
- Docker is the open-source's platform for the containers' building, the sharing, and the running. Its purpose: the applications' packaging and the deployment's consistency (the "it works on my machine" problem's solution). Its contribution was the user experience and the ecosystem (the images, the Dockerfile, the registries, the CLI, the Compose, the Swarm, the Desktop, the Hub).
-
The Docker's architecture: the
client (the
docker's CLI), the daemon (thedockerd: the objects' management), the containerd (the high-level's runtime: the container's lifecycle), the runc (the low-level's runtime: the OCI's runtime), and the BuildKit (the modern's builder: the images' construction). - The Docker's objects: the images (the read-only's templates: the layers, the metadata, the digests), the containers (the running's instances: the image plus the writable's layer, the processes, the namespaces, the cgroups, the network, the volumes), the networks (the isolated's networks: the bridge's, the overlay's, the macvlan's, the host's, the none's), the volumes (the persistent's storage: the volumes, the bind mounts, the tmpfs), and the plugins (the extensibility: the storage's, the network's, the logging's).
-
The Dockerfile is the
declarative's format for the image's
construction: the directives (the
FROM, theRUN, theCOPY, theCMD, theENTRYPOINT, theENV, theARG, theWORKDIR, theEXPOSE, theUSER, theVOLUME, theHEALTHCHECK, theLABEL, theSHELL, theSTOPSIGNAL, theONBUILD). The layers' and the build's cache's mechanisms: each instruction produces a layer, and the builder reuses the cached's layers. The best's practices: the instructions' order, the combined'sRUN's directives, the.dockerignore's file, and the multi-stage's builds. - The Docker's images: the layers (the shared between the images), the tags (the mutable's references), the digests (the immutable's references), and the registries (the Docker Hub, the private's, the cloud's providers'). The Docker's containers: the lifecycle (the create, the start, the pause, the stop, the remove), the networking (the bridge's, the host's, the overlay's, the macvlan's drivers), the storage (the writable's layer, the volumes, the bind mounts), and the configuration (the environment's variables, the resource's limits, the health's checks, the restart's policies).
-
The Docker Compose is the
Docker's tool for the multi-container's
applications: the
compose.yaml's file (the services, the networks, the volumes), the commands (thedocker compose up, thedown, theps, thelogs, theexec, thebuild, theconfig). -
The Docker's security: the
daemon's security (the root's privileges,
the socket's permissions, the rootless's
Docker), the container's security (the
non-root's user, the read-only's file
system, the dropped's capabilities, the
seccomp's profiles, the LSM's profiles,
the
no-new-privileges's setting, the resource's limits), the image's security (the minimal's images, the scanning, the signing, the SBOM), the registry's security, the secrets' management, and the runtime's security's tools (the Falco's, the Sysdig's, the Tetragon's, the Docker Bench's). - The Docker's ecosystem: the BuildKit (the modern's builder), the Buildx (the CLI's plugin: the multi-platform's builds), the Docker Swarm (the native's orchestration), the Docker Desktop (the developer's environment), the Docker Hub (the public's registry), the plugins, and the alternatives (the Podman, the containerd, the CRI-O, the nerdctl, the Buildah, the Kaniko).
- The Docker's role in the containers' adoption: the Docker's contribution was the user experience (the images, the Dockerfile, the registries, the CLI, the ecosystem); the Docker's release made the containers the mainstream; the Docker's evolution was the standards' contribution (the OCI's specifications); and the Docker's challenges were the ecosystem's competition (the Kubernetes's dominance, the containerd's and the CRI-O's adoption, the Podman's and the Buildah's adoption).
Looking ahead
This lesson presented the Docker: the Docker's platform and its contribution, the Docker's architecture (the client, the daemon, the containerd, the runc, the BuildKit), the Docker's objects (the images, the containers, the networks, the volumes, the plugins), the Dockerfile's directives, the images, the containers, the Docker Compose, the security, and the ecosystem. It referred several times to the container orchestration — the platforms that manage the containers at scale — and to the Kubernetes's specific's components (the Deployments, the Services, the pods, the nodes, the kubelet, the CRI). Lesson 13.5 presents the container orchestration in detail: the orchestration's concept (the scheduling, the scaling, the self-healing, the service's discovery, the rolling's updates, the configuration's management), the Kubernetes's architecture (the control's plane, the worker's nodes, the API's server, the etcd, the scheduler, the controller's manager, the kubelet, the kube-proxy, the CRI, the CNI, the CSI), the Kubernetes's objects (the pods, the Deployments, the Services, the ConfigMaps, the Secrets, the StatefulSets, the DaemonSets, the Jobs, the namespaces, the RBAC), the alternatives (the Docker Swarm, the Nomad, the Mesos), and the orchestration's security's and the operations's concerns. The lesson is the fifth of the unit's presentation of the virtualization's continuum, and it is the bridge to the cloud operating systems (Lesson 13.6).
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.
- 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.
- Turnbull, J. The Docker Book: Containerize your applications with Docker. The comprehensive's practical's reference.
- Nickoloff, J., & Kuenzli, S. Docker in Action, 2nd ed. Manning, 2019.
- Mouat, A. Using Docker: Developing and Deploying Software with Containers. O'Reilly, 2016.
-
The Docker's documentation
(
docs.docker.com): the Dockerfile's reference, the Compose's reference, the Docker's CLI's reference, and the Docker's security's guides. - 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 1 docker,man 1 docker-run,man 1 docker-build,man 1 docker-exec,man 1 docker-ps,man 1 docker-inspect,man 1 docker-logs,man 1 docker-network,man 1 docker-volume,man 1 docker-compose,man 5 Dockerfile,man 5 docker-compose.yml,man 1 podman,man 1 buildah,man 1 skopeo,man 1 nerdctl.