Introduction To Docker Containers

Introduction To Docker Containers

Reading time1 min
#Cloud#DevOps#Containers#Docker#Containerization#Microservices

Docker Containers: Engineering Fundamentals by Example

Docker containers have become foundational across cloud-native development, microservices architectures, and modern CI/CD workflows. Understanding why containers—specifically Docker’s model—work the way they do can prevent major operational headaches.


Problem: “But It Works On My Machine…”

A senior developer ships a patch, all tests green on their MacBook, but deployment fails on the production cluster. Same codebase, different runtime environment. This is not just folklore—it’s the genesis for modern containerization.

Docker addresses this by packaging applications and their dependencies in a lightweight, consistent execution unit: the container.


What Is a Docker Container (Exactly)?

A Docker container encapsulates an application—including binaries, libraries, configuration, and environment variables—inside a user-space instance that shares the host OS kernel. Unlike virtual machines, a container's overhead is minimal: no guest OS, rapid instantiation, tight resource footprint. Containers isolate processes using kernel primitives (cgroups, namespaces).

  • Analogy: Not a suitcase but a standard shipping container—contents vary, form factor is universal.

Core Benefits (with Trade-Offs)

Consistency

  • Identical images run across laptops, staging, bare-metal, or Kubernetes. “Works everywhere”—true, provided you aren’t relying on hardware-specific features or non-portable binaries.

Portability

  • Move containers between Windows, Linux, Mac (multiplying dev velocity). Realistically, non-Linux platforms add a LinuxKit VM as a shim—watch resource usage on developer machines.

Resource Efficiency

  • Share the host kernel instead of spinning up a full guest OS. Typical overhead: tens of MB, not GBs. Fast startup (milliseconds), minimal cold start.

Scalability

  • Orchestrators (e.g., Kubernetes, Nomad, ECS) replicate container instances horizontally, handling ephemeral workloads or high availability patterns.

Deployment Simplification

  • Artefact = single immutable container image. Rollback, promotion, or blue-green all become a docker pull or deployment YAML change.

Trade-off: Security Boundary

Containers are process isolation, not strong VM boundaries. Kernel exploits pierce all containers on a host; patch OS aggressively.


Basic Workflow: Docker By Command Line

1. Install Docker Engine

  • macOS/Windows: Use Docker Desktop, currently Docker Desktop 4.28+ recommended for M1/M2 support. Note: Consumes considerable RAM on heavy workloads.

  • Linux: Prefer native packages. Example for Ubuntu 22.04:

    sudo apt-get update && \
    sudo apt-get install ca-certificates curl gnupg && \
    sudo install -m 0755 -d /etc/apt/keyrings && \
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null && \
    sudo apt-get update && \
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    

    After install, verify:

    docker --version
    # Docker version 24.0.2, build cb74dfc
    

    Note: Add your user to the docker group for non-root use (sudo usermod -aG docker $USER), then re-login.


2. Launch an Ephemeral Container

Start a root shell inside Ubuntu 22.04 with internet access:

docker run --rm -it ubuntu:22.04 bash
  • --rm: auto-remove container after exit (avoids orphaned containers).
  • -it: interactive tty.
  • ubuntu:22.04: explicit version tag—always specify for reproducibility.

Inside the container:

apt update && apt install -y curl
curl -I https://www.google.com
exit

Gotcha: Exiting loses all changes unless you re-build the image. Stateless by default.


3. Images vs. Containers

TermDescriptionCommand Example
ImageImmutable, versioned OS+app snapshotdocker images
ContainerWritable, runtime process started from an imagedocker ps -a
  • Delete stopped containers with docker rm <id>.
  • Remove unused images: docker image prune.

4. Build and Run a Custom App

Suppose you want to ship a minimal Node.js HTTP server in a container. Practical reasons: reproducing builds, onboarding, avoiding “works-for-me” dependency hell.

Directory structure:

myapp/
 ├── Dockerfile
 ├── index.js
 └── package.json

index.js

const http = require('http');
const port = process.env.PORT || 3000;
http.createServer((req, res) => {
  res.writeHead(200,{'Content-Type': 'text/plain'});
  res.end('Hello from my first Docker app\n');
}).listen(port);

package.json

{
  "name": "myapp",
  "version": "1.0.0",
  "main": "index.js"
}

Dockerfile

FROM node:18.16-alpine
WORKDIR /usr/src/app
COPY . .
CMD ["node", "index.js"]
EXPOSE 3000

Build

docker build -t mynodeapp:1.0 .
  • Always tag with :version for traceability.

Run

docker run --rm -p 3000:3000 mynodeapp:1.0
  • Map container:host ports.
  • Test in another terminal:
    curl http://localhost:3000
    
    Output:
    Hello from my first Docker app
    

Non-obvious Tip: Adjust the EXPOSE and CMD in your Dockerfile as your app grows—don’t overexpose or override entrypoints (breaks orchestration).


Maintenance & Clean-Up

  • Stop with Ctrl+C.
  • List containers: docker ps -a
  • Remove unused resources:
    docker container prune   # removes all stopped containers
    docker image prune      # removes dangling (unreferenced) images
    
    Danger: Don’t script these in production environments—risk of accidental deletion.

Common Pitfalls

  • File permissions: Files created as root in containers may clutter your local directory if you mount volumes. Always adjust USER in Dockerfile for production-grade apps.
  • Resource limits: Default containers can eat all available CPU/RAM. Use --memory and --cpus flags to constrain dev/test workloads.
  • Networking: Docker bridge mode isn’t the same as host networking—test distributed workloads accordingly.

What to Explore Next

  • Bind mounts & named volumes (-v) for persistent state.
  • Container networking models (bridge, host, overlay).
  • Pushing images to registries (docker push), CI/CD best practices.
  • Moving from Docker Compose to Kubernetes for orchestration.

Note: Even experienced teams get bitten by subtle differences between base images (alpine, buster, etc.), default shells (sh vs bash), or network bridges on corporate VPNs. Always document your known issues and image versions.


For further reading: official Docker Docs, the “Node.js Docker Best Practices” guide, or real-world CI pipeline samples.


Published June 2024, revised for accuracy and current Docker versions.