Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Understanding Docker: From Fundamentals to Practical Usage

Notes 1

Docker emerged to solve the long-standing problem of environment inconsistency between development and operations. For example, a application developed on Windows may behave differently when deployed on Linux. Docker addresses this by packaging applications along with their runtime environments into standardized units called images.

Inspired by shipping containers, Docker enforces strong isolation between applications. Each container runs independently, enabling efficient utilization of server resources.

Virtual Machine Docker
Full OS (e.g., CentOS) per instance Minimal base image with only required dependencies (e.g., JDK, MySQL)
Requires multiple full VMs Runs lightweight containers directly
Several gigabytes in size Typically just a few megabytes

Unlike traditional virtual machines that emulate entire operating systems, Docker containers share the host kernel. They do not include a guest OS or virtualized hardware, making them significantly lighter and faster to start.

Each container is isolated with its own filesystem, process space, and network stack, yet all run atop the same host kernel. This architecture enables:

  • Rapid application delivery and deployment via immutable images
  • Simplified scaling and updates through container orchestration
  • Consistent environments across development, testing, and production
  • High-density deployment—hundreds of containers can run on a single physical machine

Core Concepts

  • Image: A read-only template containing code, runtime, libraries, and configuration needed to run an application. Multiple containers can be instantiated from one image.
  • Container: A runnable instance of an image, which can be started, stopped, or deleted. It behaves like a lightweight Linux system.
  • Repository: A storage location for Docker images, either public (Docker Hub) or private.

Architecture

Docker follows a client-server model. The Docker daemon (dockerd) runs on the host and manages containers, images, and networks. Clients communicate with the daemon via REST API, CLI, or sockets.

Essential Commands

System Info

docker --help   # General help
docker info     # System-wide information

Image Management

docker search nginx      # Search public images
docker pull nginx        # Download an image
docker images            # List local images

Container Lifecycle

docker run -d --name web_server -p 8080:80 nginx

Common flags:

  • -d: Run in detached mode
  • --name: Assign a custom name
  • -p HOST:CONTAINER: Publish container port to host
  • -it: Interactive terminal mode

Runtime Operations

docker start|stop|restart container_name
docker logs -f --tail 50 container_name
docker exec -it container_name /bin/bash  # Open new shell in running container
docker inspect container_name             # View detailed metadata
docker cp container_name:/path/file ./local/  # Copy files between container and host

Practical Examples

Nginx Deployment

docker run -d --name my_nginx -p 3344:80 nginx
curl localhost:3344  # Should return Nginx welcome page

Tomcat Deployment

docker run -d --name tomcat_app -p 3355:8080 tomcat
docker exec -it tomcat_app /bin/bash
# Inside container: copy webapps.dist to webapps if empty

Elasticsearch + Kibana

docker run -d \
  --name es_node \
  -p 9200:9200 \
  -e "discovery.type=single-node" \
  -e "ES_JAVA_OPTS=-Xms64m -Xmx512m" \
  elasticsearch:7.6.2

curl localhost:9200  # Verify cluster health

Portainer (GUI Management)

docker run -d \
  -p 8088:9000 \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  portainer/portainer-ce

Access at http://localhost:8088.

Image Layering & UnionFS

Docker uses Union File System (UnionFS), which layers filesystem changes. Each instruction in a Dockerfile creates a new read-only layer. At runtime, a writable container layer is added on top.

Base layers (e.g., bootfs, rootfs) are shared across images, reducing redundancy. Modifications are stored in upper layers, enabling efficient image inheritance and caching.

To persist changes from a container into a new image:

docker commit -a "user@example.com" -m "added config" container_id new_image:tag

Data Persistence with Volumes

Containers are ephemeral—data inside is lost when deleted. Volumes solve this by decoupling data from container lifecycle.

Bind Mounts (Host Path)

docker run -v /host/path:/container/path image

Named Volumes

docker run -v my_volume:/app/data image
docker volume inspect my_volume  # Shows host path under /var/lib/docker/volumes/

Anonymous Volumes

docker run -v /app/data image  # Auto-generated volume name

Volumes enable data sharing between containers and survive container deletion.

Building Custom Images with Dockerfile

A Dockerfile defines image construction steps:

FROM openjdk:11-jre-slim
COPY app.jar /app/
WORKDIR /app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Build and run:

docker build -t myapp:1.0 .
docker run -d -p 8080:8080 myapp:1.0

Key instructions:

  • FROM: Base image
  • RUN: Execute commands during build
  • COPY/ADD: Include files
  • ENV: Set environment variables
  • VOLUME: Declare mount points
  • CMD/ENTRYPOINT: Define startup command

Each instruction creates a new layer, cached for faster rebuilds.

Orchestration with Docker Compose

docker-compose.yml defines multi-container applications:

version: '3.8'
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - db_data:/var/lib/mysql
  web:
    build: ./app
    ports:
      - "8080:8080"
    depends_on:
      - db
volumes:
  db_data:

Commands:

docker-compose up -d      # Start services
docker-compose logs -f    # Stream logs
docker-compose down       # Stop and remove

Services communicate using service names as hostnames (e.g., jdbc:mysql://db:3306/db).

Private Registry

Run a local registry:

docker run -d -p 5000:5000 --name reg registry:2

Tag and push:

docker tag myapp:1.0 localhost:5000/myapp:1.0
docker push localhost:5000/myapp:1.0

For insecure registries, configure /etc/docker/daemon.json:

{
  "insecure-registries": ["localhost:5000"]
}

Then reload: systemctl reload docker.

Networking

By default, containers connect to the bridge network (docker0), assigned IPs in 172.17.0.0/16. Containers on the same bridge can communicate directly.

Custom Networks

docker network create --driver bridge --subnet 172.20.0.0/16 mynet
docker run --network mynet --name app1 image
docker run --network mynet --name app2 image

Now app1 can resolve app2 by name.

To connect an existing container to a new network:

docker network connect mynet existing_container

Avoid legacy --link; use user-defined networks for DNS-based service discovery.

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.