Understanding Containerization
Docker provides an open platform for packaging applications along with their runtime environment into portable units known as containers. Think of a container as a lightweight, isolated execution environment that behaves like a compact virtual machine.
Key characteristics of Docker containers:
- Strict process and filesystem isolation between workloads
- Minimal overhead compared to traditional virtualization
- Horizontal scalability—spin up thousands of instances rapidly
- Consistent runtime across development, testing, and production stages
Containers vs. Virtual Machines
Examining the architectural stack of each approach reveals the core difference: virtual machines include a full guest operating system per instance, leading to stronger isolation but heavier resource consumption. Containers, in contrast, share the host kernel and use operating‑system‑level virtualization to enforce boundaries. Both models share the underlying hardware, yet containers launch in seconds because starting one is essentially starting a process.
Core Docker Architecture
The platform comprises several major components:
- Docker daemon – Manages images, containers, networks, and volumes.
- Client – Sends commands to the daemon; the primary interface for day‑to‑day image and container operations.
- Image – A read‑only template from which containers are spawned. Images are layered and can inherit from one another, typically defined via a
Dockerfile. - Registry – Stores and distributes images, similar to how a Git repository stores code.
- Container – A runnable instance of an image, analogous to the relationship between a class and an object in object‑oriented programming.
Installing Docker Engine
The following steps install Docker Community Edition on a CentOS host:
# Add required utilities and the Docker repository
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# Install the engine, CLI, and containerd
yum install docker-ce docker-ce-cli containerd.io
# Start Docker
systemctl start docker
# Verify the installation
docker run hello-world
A successful run prints "Hello from Docker!" in the terminal.
Working with Containers
A First Example: Static Web site
Consider a static site that only requires Nginx to serve its content. The project structure might look like this:
.
├── Dockerfile
└── site
└── index.html
Defining an Image with a Dockerfile
The following Dockerfile describes how to assemble the image:
# Base image
FROM nginx:1.15.6
# Establish a working directory
WORKDIR /workspace
# Copy the site into the default Nginx HTML location
COPY ./site/* /usr/share/nginx/html
# Clean up the temporary working directory
RUN set -ex \
&& rm -rf /workspace
WORKDIR /usr/share/nginx/html
# The Nginx base image already declares EXPOSE and CMD,
# so repeating them is unnecessary. The snippet below is for illustration.
# EXPOSE 80
# CMD ["nginx", "-g", "daemon off;"]
Building the Image
Run the build command in the project root:
docker build -t demo1:v1.0.0 .
After pulling the base image and executing the instructions, the new demo1:v1.0.0 image appears in the local image list.
Launching the Container
docker run -p 8080:80 demo1:v1.0.0
Open http://localhost:8080 to see the site. The -p flag maps host port 8080 to the container's port 80.
Essential Dockerfile Instructions
-
FROM – Sets the base image (mandatory first instruction).
FROM nginx:1.15.6 -
COPY – Transfers files from the build context in to the image.
COPY --chown=nginx:nginx ./site/* /usr/share/nginx/htmlThe
--chownflag sets ownership on the copied content. -
RUN – Executes commands during the image build phase.
RUN apt-get update && apt-get install -y curl -
CMD – Specifies the default command executed when a container starts.
CMD ["executable", "param1", "param2"] -
ENV – Defines environment variables.
ENV APP_ENV=production LOG_LEVEL=info -
USER – Sets the user for
RUN,CMD, andENTRYPOINT.USER appuser:appgroup -
WORKDIR – Changes the working directory for subsequent instructions.
WORKDIR /app
The .dockerignore File
Before uploading the build context to the daemon, Docker checks for a .dockerignore file in the context root. It follows .gitignore semantics:
# Ignore all .log files
**/*.log
# Exclude the data directory
/data
# Ignore markdown files except README.md
*.md
!README.md
Managing Images
A registry solves the problem of versioning and distributing images across teams and clusters. Docker Hub (hub.docker.com) is the public default registry. Official images (marked "Official Image") are maintained by the respective open‑source projects.
Common registry operations:
# Log in to a registry (optional for public pulls)
docker login --username=your_user registry.example.com
# Pull an image
docker pull nginx:latest
# Push an image (requires login)
docker push registry.example.com/myapp:v1.0.0
Private registries—hosted on‑premises or through cloud providers—use the same workflow.
Handling Container Data
Image Layering
A Docker image is constructed from multiple read‑only layers, each representing the delta from the previous one—similar to Git commits. When a container starts, a thin writable layer is added on top. Data stored in this layer vanishes when the container is removed.
Storage Options
-
Volumes – Managed by Docker under
/var/lib/docker/volumes/. Create a volume and mount it:docker volume create app_data docker run -d --name app -v app_data:/mnt/data myimage -
Bind mounts – Mount an arbitrary host path. Useful for sharing configuration files:
docker run -d --name web \ -v /host/nginx.conf:/etc/nginx/nginx.conf:ro \ nginx -
tmpfs – Stores data in the host’s memory (non‑persistent):
docker run -d --name cache --tmpfs /mnt/data myimage
Container Networking
Containers can communicate via regular TCP/IP, and on the same host they can also use the legacy --link mechanism:
# Start a Redis instance
docker run -d --name redis redis
# Connect another container to it
docker run -d --name worker --link redis myworker
In side the worker container, the hostname redis resolves to the Redis container.
To expose a container port to the host:
docker run -d --name web -p 8080:80 nginx
The host’s port 8080 forwards traffic to port 80 inside the container. The host and container ports do not need to match.
Common CLI Commands
# List running containers
docker ps
# Start/stop/remove containers
docker start redis
docker stop redis
docker rm redis
# Open an interactive shell inside a container
docker exec -it redis bash
# Remove an image
docker image rm myimage
# Tag an image
docker tag <image-id> myregistry/myimage:v2.0.1
# Clean up dangling images
docker image prune
You can reference containers by name or ID interchangeably.
Operational Best Practices
Keep Images Lean
Avoid installing unnecessary packages and remove temporary build artifacts. Since instructions like RUN produce layers, chain related commands with &&:
RUN yum -y install gcc automake autoconf libtool make gcc-c++ \
&& ./configure \
&& make \
&& make install \
&& rm -rf /app \
&& yum clean all
Avoid Storing Critical Data Inside Containers
Containers are ephemeral. Persist data using volumes, bind mounts, external databases, or managed cloud services.
One Process per Container
Treat each container as a single‑purpose unit. For a PHP application, for example, run separate containers for Nginx+PHP‑FPM, MySQL, and Redis. This simplifies updates, scaling, and troubleshooting.
Build Reusable Base Images
When an organization shares the same stack (e.g., Nginx, PHP‑FPM, and common extensions), build an internal base image once. Subsequent application images inherit from it, reducing duplication and build time.
Keep the Main Process in the Foreground
The container’s lifecycle is tied to its main process (PID 1). If the main process daemonizes itself, the container exits immediately. Run the application in the foreground, and use docker run -d to detach the container itself if needed.