COMP 3150Operating Systems

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, the RUN, the COPY, the CMD, the ENTRYPOINT, the ENV, the EXPOSE, the USER, the WORKDIR, the VOLUME, the LABEL, the ARG, the HEALTHCHECK, the SHELL, the STOPSIGNAL, the ONBUILD), 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 the docker push's commands), the images' versioning (the tags and the digests), and the images' sharing (the docker.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 (the build, the run, the pull, the push, the ps, the exec, the logs, the inspect, the stop, the rm, the rmi) 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, the RUN, the COPY, the CMD, the ENTRYPOINT, the ENV, the EXPOSE, the USER, the WORKDIR, the VOLUME, the HEALTHCHECK, the ARG, the LABEL, the SHELL, the STOPSIGNAL, the ONBUILD) 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 the docker 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 the compose.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 the docker 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: the docker'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 (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), 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: the dockerd'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: the containerd'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: the overlayfs's implementation), the btrfs, the zfs, the devicemapper (the deprecated), the vfs (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), the host (the container shares the host's network's namespace), the overlay (the multi-host's networks: the Docker Swarm's), the macvlan (the containers get the MAC's addresses on the physical's network), the none (the container has no network), and the ipvlan.

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), the docker pull (the registry's download), the docker push (the registry's upload), the docker tag (the image's 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 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), 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 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, 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 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: the docker volume create, the docker volume ls, the docker volume inspect, the docker volume rm, the docker 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, the docker plugin ls, the docker plugin inspect, the docker plugin rm.
The Docker's architecture and the Docker objects The diagram has two parts. The top part shows the Docker's architecture: the Docker's client (the docker CLI), the Docker's daemon (the dockerd), the containerd, the runc, and the containers, with the REST API between the client and the daemon. The bottom part shows the Docker's objects: the image (the read-only template: the layers and the metadata), the container (the running instance: the image plus the writable layer, the processes, the namespaces, the cgroups, the network, the volumes), the network (the isolated network: the bridge, the overlay, the macvlan, the host), the volume (the persistent storage: the volumes, the bind mounts, the tmpfs), and the plugin (the extensibility: the storage, the network, the logging). THE DOCKER'S ARCHITECTURE The Docker's client docker (the CLI) the REST's API's client the REST's API (the Unix's socket or the TCP) The Docker's daemon dockerd the objects' management the gRPC containerd the container's lifecycle the images' layers, the CRI the OCI runc the OCI's runtime The BuildKit (the images' builder) the Dockerfile's frontend, the LLB, the parallel's builds, the cache's improvements, the rootless's builds The host's kernel (the namespaces, the cgroups, the overlayfs) THE DOCKER'S OBJECTS The image the read-only's template the layer 3 (the application) the layer 2 (the dependencies) the layer 1 (the base) the metadata (the config, the entrypoint, the command, the env, the ports) The container the running's instance the image's layers (the read-only) the writable's layer (the changes) the processes, the namespaces the cgroups, the network, the volumes (the runtime's state) The network the isolated's network • the bridge (the default's) • the overlay (the multi-host's) • the macvlan (the MAC's) • the host (the shared's) • the none (the isolated's) the DNS's resolution, the port's mapping, the CNI's plugins The volume the persistent's storage • the volumes (the managed's) • the bind mounts (the host's) • the tmpfs (the memory's) the data's persistence: the container's writable's layer is the ephemeral The plugins (the extensibility) the storage's plugins (the volumes' drivers), the network's plugins (the CNI's, the overlay's), the logging's plugins (the syslog's, the fluentd's, the journald's, the splunk's), the authorization's and the metrics's plugins The Docker's workflow docker build docker push docker pull docker run the container's run The Docker's workflow: the build, the push, the pull, the run, and the container's execution
Figure 13.4 — The Docker's architecture and the Docker objects. The top diagram shows the Docker's architecture: the Docker's client (the 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 the ubuntu:22.04's image. The FROM's directive is the mandatory's first's instruction (except the ARG's directive before it), and the multi-stage's builds use the multiple's FROM'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. The RUN's directives are the image's construction's workhorses (the packages' installation, the build's commands, the configuration's commands), and the observation: the RUN'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 the app.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: the ADD'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 the COPY's directive (the explicit's behavior) over the ADD'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 the docker run's arguments override the CMD's directive. The Dockerfile's design: the CMD's directive has the three's forms (the exec's form CMD ["executable", "param1"], the shell's form CMD command param1, and the parameters' form CMD ["param1"] with the ENTRYPOINT), 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 the CMD's directive supplies the default's arguments. The ENTRYPOINT's directive is the container's "the application" (the container's design: the single's process's model), and the observation: the ENTRYPOINT's and the CMD'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. The ENV's directive is the configuration's mechanism, and the observation: the ARG's directive (the build-time's variables) and the ENV'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 the docker build --build-arg VERSION=1.0's option can override. The ARG's directive is the build's parameterization's mechanism, and the observation: the ARG'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 the ARG's directive is the secrets' mechanism's candidate (the with the BuildKit's secrets' mounts, the RUN --mount=type=secret's directive).
WORKDIR
The working's directory: the WORKDIR /app's directive sets the working's directory for the RUN, the CMD, the ENTRYPOINT, the COPY, and the ADD's directives, and the container's processes' working's directory. The WORKDIR's directive is the path's management's mechanism, and the observation: the WORKDIR's directive creates the directory if it does not exist (the mkdir -p's behavior).
EXPOSE
The port's documentation: the EXPOSE 8080's directive documents the port that the container's application listens on, and the docker run -p 8080:8080's option publishes the port to the host. The EXPOSE's directive is the documentation's mechanism (the metadata), not the publishing's mechanism (the -p's option), and the observation: the EXPOSE'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: the USER'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: the VOLUME's directive declares the volume's intent (the data's persistence's boundary), and the observation: the docker 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: the HEALTHCHECK'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 the RUN's, the CMD's, and the ENTRYPOINT's shell's forms use. The SHELL'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 the docker stop's command sends to the container's process, and the observation: the STOPSIGNAL'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. The ONBUILD's directive is the base's image's customization's mechanism, and the observation: the ONBUILD's directive is the deprecated's in the modern's practices (the explicit's COPY'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 (the docker.io/library/ubuntu:22.04's, the ghcr.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: the latest'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: the docker login (the authentication), the docker pull (the download), the docker push (the upload), the docker search (the search), the docker manifest (the multi-platform's manifests).
  • The image's commands. The docker build (the Dockerfile's construction), the docker images (the listing), the docker inspect (the inspection: the configuration, the layers, the metadata), the docker history (the layers' history: the instructions and the sizes), the docker tag (the image's naming), the docker rmi (the removal), the docker save (the image's export to a tar's file), the docker load (the image's import from a tar's file), the docker export (the container's file system's export), the docker 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), the docker history (the layers' history), the docker inspect (the image's metadata), the skopeo inspect (the remote's image's inspection), and the buildah 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 the created), the docker start (the start: the container's state is the running), the docker pause (the pause: the container's state is the paused: the cgroup's freezer's controller suspends the processes), the docker unpause (the resume), the docker stop (the stop: the SIGTERM's signal, the grace's period, the SIGKILL's signal; the container's state is the exited), the docker restart (the restart), the docker kill (the forced's stop: the SIGKILL's signal), the docker rm (the removal: the container's state is the removed: 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), the docker inspect (the container's configuration and the state: the JSON's output), the docker logs (the container's logs: the stdout's and the stderr's streams), the docker stats (the container's resource's usage: the CPU's, the memory's, the network's, the I/O's), the docker top (the container's processes), the docker diff (the container's file system's changes: the added, the modified, the deleted files), the docker port (the container's port's mappings), the docker 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), the docker attach (the container's stdin's and stdout's attachment: the container's main's process), the docker cp (the files' copying between the container and the host), the docker 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 (the docker 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 (the docker 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, and on-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), the networks's section (the application's networks), the volumes's section (the application's volumes), the configs's section (the configuration's files), the secrets's section (the secrets' management), and the x-'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, the docker's group's membership's caution: the docker'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=ALL and 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, the distroless's, the scratch's: the smaller's attack surface), the images' scanning (the Trivy's, the Clair's, the Grype's, the Docker's docker 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 the docker inspect's output), the Docker's secrets (the docker secret's commands: the Swarm's secrets), the build's secrets (the BuildKit's RUN --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: the DOCKER_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 (the buildx build, the buildx create, the buildx inspect, the buildx ls, the buildx 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, the python's, the node's, the alpine's, the nginx's, the postgres's, the redis's, the mongo'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, the docker build, the docker pull, the docker 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 (the runc'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.

  1. Check the Docker's installation. Verify the Docker's (or the Podman's) installation and the daemon's status.
    # 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
    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.
  2. Pull and inspect an image. Pull an image and inspect its layers, its configuration, and its history.
    # 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']))
    "
    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.
  3. Run and inspect a container. Run a container with the security's options and inspect its state.
    # 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
    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 /etc/os-release show? How do the Docker's options map to the kernel's mechanisms? Write down what you find.
  4. Build an image. Build an image from a Dockerfile and inspect the layers and the cache.
    # 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
    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.
  5. Use the Docker Compose. Create and run a multi-container's application with the Docker Compose.
    # 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
    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.
  6. Inspect the Docker's security. Inspect the Docker's daemon's configuration and the containers' security.
    # 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
    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.
  7. Compare the Docker and the Podman. If both the Docker and the Podman are available, compare their designs:
    # 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
    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.
  8. Read the man pages. Read the man pages that document the Docker's interfaces.
    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
    For each, write a sentence describing its main contribution to your understanding of the Docker. The man 1 docker-run page documents the containers' creation; the man 5 Dockerfile page documents the images' construction; the man 1 docker-compose page 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 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: the application and its dependencies in the single's artifact, the image). The Docker's platform's components: the client (the 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
The Docker's objects: the image (the read-only's template: 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 commands: the 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
The Dockerfile's directives: the 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
The Docker's images: the layers (the stack of the read-only's layers: each layer is the file system's changes relative to the previous's layer; the layers are the shared between the images: the same's base's layer is the stored the once; the layers are the cached and the transferred the incrementally); the tags (the mutable's references: the 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
The Docker Compose is the Docker's tool for the multi-container's applications: the 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.

  1. 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).
    Conclude with a recommendation for a developer's workflow and for a production's environment.
  2. 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, the npm ci --omit=dev's dependencies).
    Justify your design through the lesson's principles.
  3. Build and analyze an image. On your VM, build an image and analyze its layers and its size:
    # 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
    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.
  4. 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).
    Justify your design through the lesson's principles.
  5. Analyze the Docker's security. On your VM, inspect the Docker's security's configuration and the containers' security:
    # 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
    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).
  6. Analyze the images' supply chain. Choose a popular's image (the nginx:alpine's, the postgres:16-alpine's, the node:20-alpine's) and analyze its supply chain:
    • The image's base (the alpine's, the debian'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.
    Write a short report (500–750 words).
  7. 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).
    Justify your design through the lesson's principles.
  8. 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's RUN'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.
  9. 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 (the dockerd: 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, 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). 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's RUN'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 (the docker compose up, the down, the ps, the logs, the exec, the build, the config).
  • 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.
👤