Advanced Docker Concepts

Promise DashPromise Dash
5 min read

Docker has revolutionized how we build, deploy, and run applications. Beyond the basics, understanding advanced concepts like layers, caching strategies, networks, volumes, and Docker Compose can significantly improve your development workflow. This guide will break down these concepts step-by-step, with real-life examples to make them clearer.

Understanding Layers in Docker

What Are Docker Layers?

In Docker, layers are a foundational part of image architecture that enhances efficiency, speed, and portability. Each Docker image is built from a series of layers, where each layer represents changes made to the filesystem, such as installing a package or copying files.

How Are Layers Created?

  1. Base Layer: The first layer, often an operating system like Ubuntu or Alpine, is specified by the FROM command in a Dockerfile.

  2. Instruction Layers: Commands like RUN, COPY, and WORKDIR each create a new layer. These instructions modify the filesystem, such as by installing software or adding files.

  3. Reusable and Shareable: Layers are cached and can be reused across images, making builds faster and more efficient. If an image shares the same base or commands, it can reuse existing layers.

  4. Immutability: Once a layer is created, it cannot be changed. Changes create a new layer to capture the differences, maintaining immutability and reliability.

Example of Layer Creation

Consider the following Dockerfile:

FROM node:14  # Base layer
WORKDIR /app  # Creates a new layer
COPY package.json .  # Creates another layer
RUN npm install  # Another layer, installs dependencies
COPY . .  # Creates the final layer by copying all source code

Each command adds a layer. When building the image, Docker checks its cache to see if a layer has already been created. If so, Docker reuses it, speeding up the build process.

Why Do Layers Matter?

Layers help optimize builds. If you modify a file, only that layer and subsequent ones need to be rebuilt.

Case 1: Changing the Source Code If you modify a source file but not package.json, Docker will only rebuild the layer copying the source and any following instructions.

Case 2: Modifying package.json If you add a dependency and change package.json, Docker will rebuild from the RUN npm install layer onward.

Insight: Dependencies don't change as often as source code, so structure your Dockerfile to run npm install before copying the entire codebase to leverage caching effectively.

Optimizing Dockerfiles for Better Caching

To maximize caching benefits, structure your Dockerfile like this:

# Base layer
FROM node:14

# Copy only files needed for dependencies
WORKDIR /app
COPY package.json .
RUN npm install  # Cached if package.json doesn't change

# Copy the rest of the code and build
COPY . .
CMD ["npm", "start"]

By copying package.json and running npm install first, you cache the dependency installation step, reducing build time for frequent code changes.

Docker Networks and Volumes

Why Use Networks and Volumes?

When running multiple containers, networks allow them to communicate, and volumes ensure data persistence across restarts.

Volumes: Persisting Data

Containers are ephemeral by design, meaning data stored inside them is lost when they stop. Volumes store data outside the container, enabling persistence.

Example: Running MongoDB with and without a volume.

Without Volumes:

docker run -p 27017:27017 -d mongo
  • Add data using MongoDB Compass.

  • Kill and restart the container:

docker kill <container_id>
docker run -p 27017:27017 -d mongo
  • Data will not persist.

With Volumes:

docker volume create volume_database
docker run -v volume_database:/data/db -p 27017:27017 -d mongo
  • Data will now persist across restarts because it's stored in volume_database.

Networks: Connecting Containers

Docker Networks allow containers to communicate securely. By default, containers are isolated and need to be attached to the same network to communicate.

Example Workflow:

  1. Create a Network:

     docker network create my_custom_network
    
  2. Run a Backend Service:

     docker run -d -p 3000:3000 --name backend --network my_custom_network backend_image
    
  3. Run MongoDB on the Same Network:

     docker run -d -v volume_database:/data/db --name mongo --network my_custom_network -p 27017:27017 mongo
    

Checking Communication: Use logs to verify that the backend can connect to MongoDB:

docker logs <container_id>

Tip: Port mapping for MongoDB can be optional if it's only accessed by other containers in the network.

Types of Docker Networks

  • Bridge: Default network that provides a private internal network. Containers on the same bridge network can communicate with each other.

  • Host: Removes isolation between the container and the Docker host, using the host's network. Suitable for services needing direct host communication.

Docker Compose: Simplifying Multi-Container Management

Docker Compose is a tool that allows you to define and manage multi-container applications with a single command. You configure your services, networks, and volumes in a docker-compose.yml file.

Before Docker Compose

Starting a multi-container setup requires multiple commands:

docker network create my_custom_network
docker volume create volume_database
docker run -d -v volume_database:/data/db --name mongo --network my_custom_network mongo
docker run -d -p 3000:3000 --name backend --network my_custom_network backend_image

Using Docker Compose

With Docker Compose, this process is simplified:

  1. Install Docker Compose: Installation guide.

  2. Create docker-compose.yml:

     version: '3'
     services:
       mongo:
         image: mongo
         volumes:
           - volume_database:/data/db
         networks:
           - my_custom_network
    
       backend:
         image: backend_image
         ports:
           - "3000:3000"
         networks:
           - my_custom_network
    
     networks:
       my_custom_network:
    
     volumes:
       volume_database:
    
  3. Start All Services:

     docker-compose up
    
  4. Stop All Services:

     docker-compose down --volumes
    

Note: All containers in a docker-compose setup run on the same network by default.

These advanced Docker concepts—layers, caching, networks, volumes, and Docker Compose—help streamline development, make builds faster, and improve data management. Mastering them will enhance your ability to create efficient, scalable, and maintainable applications.

1
Subscribe to my newsletter

Read articles from Promise Dash directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Promise Dash
Promise Dash

Frontend developer with some backend knowledge. Learning new libraries and frameworks. Looking forward to contributing to open source