Introduction to Docker

SHOW CONTENTS

Docker was developed to address the challenges of software deployment across various environments by providing a lightweight, consistent, and portable solution. Prior to Docker, developers often faced issues like “it works on my machine, but not in production,” caused by inconsistencies in dependencies, libraries, and configurations. Docker resolves this by containerizing applications, ensuring they run uniformly across any infrastructure. Its primary goal is to simplify development, testing, and deployment by encapsulating applications and their dependencies into isolated, reproducible containers. These containers are user-space instances that package code, runtime, system tools, and libraries, ensuring consistency across environments.

The key difference between Docker and Virtual Machines (VMs) lies in their architecture. VMs emulate entire hardware stacks, requiring a full operating system for each instance, which results in significant overhead. In contrast, Docker uses containers that share the host OS kernel, offering a more lightweight and efficient solution by isolating applications at the process level. Virtual Machines vs. Docker

Docker has three core components: images, containers, and registries. Images are read-only templates that contain application code and dependencies. Containers are runnable instances of these images; they are isolated, lightweight, and ephemeral. Registries are repositories that store and share Docker images. Docker Core Components


Docker Architecture

SHOW CONTENTS

Docker follows a Client/Server (C/S) architecture with a loosely coupled backend where various components perform specific functions. The basic workflow of Docker is as follows: Docker Architecture

  1. Docker Client: The user interacts with the Docker Client, which communicates with the Docker Daemon to send requests.
  2. Docker Daemon: This is the core component of Docker’s architecture. It provides the Docker Server functionality, enabling it to accept requests from the Docker Client.
  3. Docker Engine: The Docker Engine executes a series of internal tasks. Each task is represented as a “Job.”
  4. Job Execution: Each Job performs a specific function, such as building an image, starting a container, or managing storage.
  5. Graph Driver: If a Job requires a container image, the Engine fetches it from a Docker Registry. The Graph driver stores the downloaded image as a Graph (a layered filesystem) and manages its dependencies.
  6. Network Creation: When a network environment needs to be created for Docker, the Network Driver is used to create and configure the network for Docker containers.
  7. Resource Management and Execution: If Docker needs to limit resources or execute user commands within containers, the Exec Driver is responsible for completing these operations.
  8. Libcontainer: Libcontainer is an independent container management package. Both the Network Driver and the Exec Driver rely on Libcontainer to perform specific container management operations.


Common Docker Commands

Basic Commands

SHOW CONTENTS
  • Start/Stop/Restart Docker:
    sudo systemctl start docker
    sudo systemctl stop docker
    sudo systemctl restart docker
    
  • View Docker Status:
    sudo systemctl status docker
    
  • View Docker Info:
    docker info
    
  • View Docker Help Documentation:
    docker --help
    
  • View Docker Version:
    docker -v
    


Docker Image Commands

SHOW CONTENTS
  • Search for a Docker Image: docker search <image_name>
    • docker search nginx
  • Pull a Docker Image: docker pull <image_name>
    • docker pull nginx
  • List Docker Images: docker images
    • docker image ls
  • Inspect a Docker Image: docker inspect <image_name_or_id>
    • docker inspect nginx
  • Tag a Docker Image: docker tag <image_id_or_name> <new_image_name>:<new_tag>
    • docker tag nginx:latest nginx:1.27.4
  • Remove a Docker Image: docker rmi <image_name_or_id>
    • docker rmi nignx:1.27.4
  • Build a Docker Image: docker build -t <image_name>:<tag> <path_to_dockerfile>
    • docker build -t myapp:latest .
  • Save a Docker Image to a File: docker save -o <output_file_name>.tar <image_name>:<tag>
    • docker save -o myapp.tar myapp:latest
  • Load a Docker Image from a File: docker load -i <file_name>.tar
    • docker load -i myapp.tar
  • Prune Unused Docker Images: docker image prune
    • docker image prune -a


Docker Container Commands

SHOW CONTENTS
  • docker run: Create and start a new container
    • docker run -it -p 8080:8080 --name tomcat9 tomcat:9-jre17 /bin/bash: Start a new container from the tomcat:9-jre17 image, name it tomcat9, map port 8080 inside the container to port 8080 on the host, and open an interactive bash shell inside the container.
  • docker ps: List all currently running containers
    • docker ps: List containers that are currently running.
    • docker ps -a: List all containers.
  • docker start <container_id_or_name>: Start a stopped container
    • docker start my_container: Start a container with the name my_container.
  • docker restart <container_id_or_name>: Restart a container
    • docker restart my_container: Restart the container named my_container.
  • docker stop <container_id_or_name>: Stop a running container
    • docker stop my_container: Stop the container named my_container.
  • docker kill <container_id_or_name>: Force stop a running container
    • docker kill my_container: Force stop the container named my_container.
  • docker rm <container_id_or_name>: Remove a stopped container
    • docker rm my_container: Remove the container named my_container after it has stopped.
  • docker run -d <container_id_or_name>: Start a container in detached mode (background)
    • docker run -d nginx: Run the Nginx container in the background.
  • docker logs <container_id_or_name>: View logs of a container
    • docker logs my_container: View logs for the container named my_container.
  • docker top <container_id_or_name>: View the running processes inside a container
    • docker top my_container: Display the processes running inside my_container.
  • docker inspect <container_id_or_name>: Get detailed information about a container
    • docker inspect my_container: Show detailed metadata about the container named my_container.
  • docker exec -it <container_id_or_name> <command>: Execute a command inside a running container (interactive mode)
    • docker exec -it my_container bash: Open an interactive bash shell inside my_container.
  • exit: Exit from an interactive session inside a container
    • exit: Exit the interactive shell of the container and return to the host.
  • docker cp <container_id_or_name>:<path_in_container> <path_on_host>: Copy files from a container to the host
    • docker cp my_container:/app/file.txt /host/directory/: Copy file.txt from the container to the host.


Docker Images

SHOW CONTENTS

A Docker image is a read-only template used to create containers. It is composed of several layers stacked on top of each other, with each layer representing a snapshot of the filesystem at a specific point in time. These snapshots capture modifications made by Docker instructions (such as installing a package, copying files, or modifying configurations). Layers are created during the execution of commands in a Dockerfile or when using a pre-existing image to build a new one.

Docker leverages UnionFS to efficiently manage these layers, allowing it to present a unified view of the container’s filesystem. This approach helps optimize storage and performance, as common layers can be shared across different images. Docker layered Image

Here is a simple example of creating an image using a Dockerfile:

FROM ubuntu  # This layer contains the base Ubuntu image
RUN apt-get update  # This layer contains the updated package list
COPY . /app  # This layer contains the application files copied into /app
CMD ["python", "/app/app.py"]  # This layer contains the command to run the application when the container starts

Docker provides the docker commit command, which allows creating a new image based on the changes made to a running container. It is similar to taking a snapshot of the container’s current state. Below is a real-world example:

# Run a container from a base image
docker run -it ubuntu /bin/bash  

# Make changes inside the container
apt-get update
apt-get install -y vim

# Commit the container to create a new image
docker commit <container_id> my-ubuntu-image


Docker Volumes

SHOW CONTENTS

A Docker volume is a persistent storage mechanism designed to store data outside of a container’s filesystem. Volumes are stored on the host machine and can be shared and reused across multiple containers. They are particularly useful for overcoming the ephemeral nature of container filesystems, ensuring that data persists even when containers are stopped, removed, or replaced. Docker Volumes Below are some example use cases:

# Create a named volume (stored in /var/lib/docker/volumes/mysql_data on the host)
docker volume create mysql_data  

# Run a MySQL container, mounting the volume to store data persistently (default is rw for read-write)
docker run -d --name mysql1 -v mysql_data:/var/lib/mysql mysql  

# Run a container with a read-only volume mount, preventing modifications to the config files
docker run -d --name myapp -v /tmp/config:/app/config:ro myapp  

# Use --volumes-from to mount the volume from mysql1 into mysql2, sharing data between the containers
docker run -d --volumes-from mysql1 --name mysql2 mysql


Installing Software in Docker

Search Image Versions

SHOW CONTENTS
  1. vim ~/.bashrc:
    # Search docker image versions
    dkr_image_versions() {
    # Usage: dkr_image_versions <image_name> [limit]
    # Fetches version tags from Docker Hub for a given image, sorted by version number
    # Default shows all versions, optional limit controls output count
    
    local image_name=$1
    local limit=${2:-0}  # Default 0 (show all), if not provided
    
    # Validate input
    if [ -z "$image_name" ]; then
       echo "Error: Image name not specified" >&2
       echo "Usage: dkr_image_versions <image_name> [limit]" >&2
       return 1
    fi
    
    # Fetch and process versions
    versions=$(curl -s "https://hub.docker.com/v2/repositories/library/$   {image_name}/tags/?page_size=100" | \
       jq -r '.results[].name' | \
       grep -E '^[0-9]+\.*' | \
       sort -rV)  # Reverse version sort (newest first)
    
    # Handle empty results
    if [ -z "$versions" ]; then
       echo "No version tags found for image: ${image_name}" >&2
       return 1
    fi
    
    # Apply limit if specified
    if [ "$limit" -gt 0 ]; then
       echo "$versions" | head -n "$limit"
    else
       echo "$versions"
    fi
    }
    
  2. source ~/.bashrc
  3. dkr_image_versions ubuntu [number]


Tomcat

SHOW CONTENTS

Host Docker Port Mapping

  1. Run the Tomcat container with interactive shell: docker run -p 8080:8080 --name tomcat2 -it tomcat:9-jre17 /bin/bash
  2. Remove the webapps directory: rm -r webapps
  3. Rename webapps.dist to webapps: mv webapps.dist webapps
  4. Start Tomcat in the background: ./bin/catalina.sh run &
  5. Open port 8080 in the firewall: sudo firewall-cmd --permanent --add-port=8080/tcp
  6. Reload the firewall configuration to apply changes: sudo firewall-cmd --reload

MySQL

SHOW CONTENTS
  1. Create Volumes for Log, Data, and Config:
    docker volume create mysql_data      # Volume for MySQL data
    docker volume create mysql_log       # Volume for MySQL logs
    docker volume create mysql_conf      # Volume for MySQL configuration
    
  2. Run MySQL Container with Volumes:
    docker run -d \
      --name mysql \
      -e MYSQL_ROOT_PASSWORD=my-secret-pw \
      -p 3306:3306 \
      -v mysql_data:/var/lib/mysql \
      -v mysql_log:/var/log/mysql \
      -v mysql_conf:/etc/mysql/conf.d \
      mysql:latest
    
  3. Configure MySQL (Optional)
    # Optional: Copy custom config file to the volume 
    cp my.cnf /var/lib/docker/volumes/mysql_conf/
    
  4. Access MySQL:
    docker exec -it mysql mysql -u root -p
    

Redis

SHOW CONTENTS
  1. Create named Docker volumes:
    docker volume create redis_conf
    docker volume create redis_data
    
  2. Edit the Redis config file /var/lib/docker/volumes/redis_conf/redis.conf:
    # /app/redis/redis.conf
    requirepass yourpassword
    # bind 127.0.0.1        ← comment this out
    daemonize no            # ensure Redis runs in foreground
    
  3. Start the Redis container:
    docker run -d \
    --name redis7 \
    --privileged=true \
    -p 6379:6379 \
    -v redis_conf:/etc/redis \
    -v redis_data:/data \
    redis:7.4.3 redis-server /etc/redis/redis.conf