Docker: Fundamentals

Master the core concepts of containerization including images, containers, networking, and the Docker architecture.

Why Docker? The Problems It Solves

Consider the following scenario: You have built an application that works perfectly on your machine. You hand it to a colleague, and it fails immediately. The culprit? Different Python versions, missing libraries, or conflicting configurations. Docker eliminates this entire class of problems.

Common Development Challenges

Dependency Hell

Different projects require different versions of libraries, languages, and tools, leading to conflicts and complex virtual environment management.

Environment Parity

Code that works perfectly on a developer's laptop fails in production due to OS differences, missing dependencies, or configuration mismatches.

Onboarding Time

New team members spend days setting up development environments, installing tools, and troubleshooting configuration issues.

Resource Efficiency

Traditional VMs consume significant resources, limiting the number of applications that can run on a single server.

How Docker Solves These Problems

Isolated Environments

Each container has its own filesystem, network, and process space, eliminating conflicts between applications.

Reproducible Builds

Dockerfiles define exact steps to build an environment, ensuring consistency across all stages of development.

Instant Setup

New developers can start with a simple `docker run` command, eliminating complex installation procedures.

Efficient Layering

Docker's layer system shares common components between containers, dramatically reducing disk usage and memory overhead.

Essential Docker Commands

Before running any Docker commands, it helps to understand the mental model: Docker images are like recipes (blueprints), while containers are the actual dishes you create from those recipes. You can make many containers from the same image, and each one runs independently.

Core Operations

The following examples demonstrate the core concepts. Start with simple commands and build up to more complex workflows.

Running Containers

When to use: Start here when learning Docker or when you need to quickly test something in a clean environment.

# Run an interactive Ubuntu container
docker run -it ubuntu:22.04 bash

# You are now inside a minimal Linux system
cat /etc/os-release && exit

The -it flags create an interactive terminal session. When you type exit, the container stops.

Web Server Deployment

When to use: When you need to run a service in the background, such as a web server, database, or API.

# Run Nginx web server in the background
docker run -d -p 8080:80 --name my-web nginx

# Visit http://localhost:8080, then clean up
docker stop my-web && docker rm my-web

The -d flag runs the container in the background. The -p 8080:80 maps your machine's port 8080 to the container's port 80.

Building Custom Images

When to use: When you need to package your own application with its specific dependencies and configuration.

# Dockerfile - save this file, then build with: docker build -t my-app .
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
# Build and run your custom image
docker build -t my-app .
docker run -d -p 5000:5000 my-app

A Dockerfile defines your environment step by step. Docker caches each step, so rebuilds are fast when only your code changes.

Using Docker Compose

When to use: When your application needs multiple services (web server + database, for example) that work together.

# docker-compose.yml
version: '3.8'
services:
  web:
    build: .
    ports:
      - "5000:5000"
    depends_on:
      - redis
  redis:
    image: redis:alpine
# Start all services with one command
docker-compose up -d

# Stop everything
docker-compose down

Compose automatically creates a network where services can find each other by name. Your web service can connect to redis without knowing its IP address.

Key Takeaways

  • Images are blueprints; containers are running instances
  • Dockerfiles define how to build images reproducibly
  • Port mapping connects container services to your host
  • Docker Compose manages multi-container applications
  • Volumes persist data beyond container lifecycle

Understanding Container Technology

Before diving into Docker’s specific implementation, it helps to understand how containers differ from traditional virtualization. This context will help you make informed decisions about when to use each approach.

Containers vs Virtual Machines

Consider the following when choosing between containers and VMs:

Factor Choose Containers Choose VMs
Startup time matters Yes - seconds vs minutes Boot time acceptable
Running many instances Yes - minimal overhead Fewer, larger workloads
Need different OS No - share host kernel Yes - run Windows on Linux
Security isolation Process-level sufficient Need hardware-level isolation
Legacy applications May need refactoring Run as-is

Containers are lightweight, resource-efficient, and portable, making them suitable for modern, scalable applications. Virtual machines provide strong isolation, full OS support, and hardware emulation but can be resource-intensive and slower to start up.

Container Architecture Hardware Host OS + Kernel Container Runtime App A Bins/Libs App B Bins/Libs App C Bins/Libs Virtual Machine Architecture Hardware Host OS Hypervisor App A Bins/Libs Guest OS App B Bins/Libs Guest OS App C Bins/Libs Guest OS

Container Pros/Cons

Pros

Lightweight

Share the host OS kernel, minimal overhead

Fast startup

Start in seconds for rapid deployment

Resource efficiency

Higher density on single host

Portability

Consistent deployment across environments

Process Isolation

Applications run without interference

Cons

Kernel dependency

Limited cross-platform compatibility

Security boundaries

Weaker isolation than VMs

Limited applications

Not suitable for kernel modifications

Virtual Machine Pros/Cons

Pros

Strong isolation

Complete OS separation for security

Full OS support

Run any OS version or distribution

Hardware emulation

Support legacy and platform-specific apps

Mature ecosystem

Extensive tooling and management

Cons

Resource-intensive

Full OS stack overhead

Slow startup

Minutes to boot and initialize

Storage overhead

Duplicated OS and libraries

Deployment complexity

Manual dependency management

What Containers Guarantee

Containers provide strong guarantees in several areas:

  • Application dependencies: All libraries and tools are bundled together, eliminating “missing dependency” errors
  • Configuration: Environment settings travel with the container, making deployments reproducible
  • Isolation: Each container has its own filesystem and process space, preventing conflicts between applications
  • Portability: The same container runs on any machine with Docker installed

What Containers Cannot Guarantee

However, some factors remain outside container control:

  • Kernel features: Containers share the host kernel, so a container expecting Linux 5.x features will not work on a host running Linux 4.x
  • Hardware access: GPU acceleration, specialized devices, and host-specific resources may behave differently across machines
  • Resource limits: CPU and memory constraints vary by host, affecting performance consistency
  • Platform differences: A Linux container cannot run natively on Windows without a Linux VM layer

Practical guidance: For maximum portability, avoid dependencies on specific kernel versions or hardware features. When these are unavoidable, document the requirements clearly.

Now that we understand what containers do, let’s briefly look at how Docker implements them. You do not need to memorize these details to use Docker effectively, but understanding the architecture helps when troubleshooting or optimizing performance.

Docker Architecture Overview

Docker uses a layered architecture where each component has a specific responsibility:

Component Role When You Interact With It
Docker CLI User interface Every docker command you run
Docker Daemon Manages containers, images, networks Runs in background
containerd Container lifecycle management Rarely directly
runc Actually runs containers Never directly

Container Runtime Basics

At its core, a container is defined by:

  • Filesystem: What files the container can see (its “root filesystem”)
  • Namespaces: Isolation boundaries for processes, network, users, and more
  • Cgroups: Resource limits for CPU, memory, and I/O
  • Security: Capabilities, seccomp profiles, and mandatory access controls

The Open Container Initiative (OCI) standardizes these specifications, allowing containers to run on any compliant runtime.

Note: Most users never interact with these low-level components directly. Docker’s CLI abstracts away this complexity while giving you control when needed.

Docker Network Architecture

Networking is often the trickiest part of containerization. Containers need to communicate with each other, with the host, and with external services, all while maintaining isolation. Docker provides several network drivers for different scenarios.

Network Driver Quick Reference

Driver Use Case Isolation Performance
bridge Default for standalone containers Good Good
host Maximum performance needed None Best
overlay Multi-host communication (Swarm) Good Good
macvlan Container needs real IP on network Varies Good
none Complete network isolation Maximum N/A

How Bridge Networking Works

When you run a container without specifying a network, Docker uses the default bridge network. Here is what happens:

  1. Docker creates a virtual network interface pair (veth)
  2. One end attaches to the container, the other to a bridge on the host
  3. The container gets an IP address from Docker’s internal DHCP
  4. Containers on the same bridge can communicate by IP
  5. For external access, Docker uses NAT and port mapping

Key insight: Containers on the same user-defined bridge network can find each other by container name (automatic DNS). The default bridge does not have this feature, which is why creating custom networks is recommended.

When to Use Each Network Type

  • bridge: Development, single-host deployments, isolated applications
  • host: Performance-critical applications, when container port must match host port
  • overlay: Docker Swarm services spanning multiple hosts
  • macvlan: When container must appear as physical device on network (legacy integration)
  • none: Security-sensitive workloads that should have no network access