Welcome to from-docker-to-kubernetes

Containers

Understanding Docker containers and their fundamentals

What are Containers?

Containers are lightweight, standalone, and executable software packages that include everything needed to run a piece of software, including the code, runtime, system tools, libraries, and settings.

Containers solve the "it works on my machine" problem by packaging applications with their dependencies, ensuring consistent execution across different environments. They provide an isolated execution environment that:

  • Ensures consistent behavior across development, testing, and production
  • Allows applications to run reliably when moved from one computing environment to another
  • Enables microservices architecture by isolating individual components
  • Facilitates DevOps practices with repeatable deployments
  • Maximizes resource utilization compared to traditional deployment methods

At their core, containers rely on Linux kernel features like namespaces for isolation and cgroups for resource limitations. Docker adds a layer of tooling that makes these capabilities accessible and manageable.

Container vs Virtual Machines

Unlike virtual machines, containers virtualize the operating system instead of hardware. This makes them more lightweight and portable.

Key Differences:

FeatureContainersVirtual Machines
VirtualizationOS-level virtualizationHardware virtualization
SizeTypically MBsTypically GBs
Startup timeSecondsMinutes
Resource overheadMinimalSignificant
IsolationProcess isolationComplete isolation
Operating systemShares host OS kernelRuns complete OS
DensityHigh (dozens to hundreds per host)Low (typically 5-10 per host)

Containers provide near-native performance with minimal overhead, while VMs offer stronger isolation but with greater resource requirements. Many modern infrastructures use both technologies together, leveraging containers for applications and VMs for infrastructure segmentation.

Key Concepts

Images

Docker images are read-only templates used to create containers. They contain:

  • Application code
  • Runtime environment
  • Dependencies
  • System tools
  • System libraries

Images are built in layers, with each layer representing a set of filesystem changes. This layered approach enables efficient storage and transfer, as identical layers can be shared across multiple images. Images are defined by Dockerfiles, which contain instructions for building the image.

Containers

Containers are running instances of Docker images that can be:

  • Started
  • Stopped
  • Moved
  • Deleted

Each container has its own:

  • Filesystem (with a writable layer on top of the read-only image)
  • Network interface with its own IP address
  • Process namespace (isolation from other processes)
  • Resource allocation (CPU and memory limits)

Containers are ephemeral by design, meaning they can be created and destroyed without affecting the application's functionality.

Registry

A registry stores Docker images. Docker Hub is the default public registry.

Registries serve as centralized repositories for storing and distributing Docker images. They enable:

  • Version control for container images
  • Collaboration among team members
  • Continuous integration and deployment workflows
  • Distribution of public and private images

Besides Docker Hub, there are many registry options including:

  • GitHub Container Registry
  • Amazon Elastic Container Registry (ECR)
  • Google Container Registry (GCR)
  • Azure Container Registry (ACR)
  • Self-hosted options like Harbor or Nexus

Basic Container Commands

# Run a container
docker run <image-name>

# Run with interactive terminal and remove when stopped
docker run -it --rm <image-name> /bin/bash

# Run container in background (detached mode)
docker run -d <image-name>

# Run with port mapping (host-port:container-port)
docker run -p 8080:80 <image-name>

# Run with volume mount (host-path:container-path)
docker run -v /host/path:/container/path <image-name>

# Run with environment variables
docker run -e VARIABLE_NAME=value <image-name>

# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

# Stop a container
docker stop <container-id>

# Force stop a container (like kill -9)
docker kill <container-id>

# Remove a container
docker rm <container-id>

# Remove all stopped containers
docker container prune

# View container logs
docker logs <container-id>

# Follow container logs (like tail -f)
docker logs -f <container-id>

# Execute command in running container
docker exec -it <container-id> /bin/bash

# Inspect container details
docker inspect <container-id>

Each of these commands supports additional options that allow for fine-grained control of container behavior. Use docker <command> --help to explore the full range of options for any command.

Best Practices

Container Lifecycle

Understanding the container lifecycle is essential for effective containerization:

  1. Creation: Container is created from an image using docker create or docker run
  2. Running: Container is executing its main process
  3. Paused: Container can be temporarily paused with docker pause
  4. Stopped: Container has exited but still exists and can be restarted
  5. Removed: Container is permanently deleted with docker rm

Each state transition can trigger events that can be monitored and acted upon, enabling sophisticated container orchestration workflows.

Container Networking

Containers can communicate through several network modes:

Proper network configuration is crucial for containerized applications, especially in microservices architectures.