Chapter 5: Data Volumes and Network Configuration

Haiyue
17min

Chapter 5: Data Volumes and Network Configuration

Learning Objectives
  • Master Docker volume creation and usage
  • Understand differences between bind mounts and named volumes
  • Learn Docker network creation and configuration
  • Become proficient in inter-container network communication

Key Concepts

The Need for Data Persistence

Containers themselves are ephemeral - when a container is deleted, data inside the container is also lost. To achieve data persistence, Docker provides multiple storage mechanisms:

Storage TypeCharacteristicsUse Cases
VolumesDocker-managed, good performance, shareableDatabase files, application data
Bind MountsDirect mapping of host directories, flexibleConfiguration files, development environments
tmpfs MountsStored in memory, high performanceTemporary files, cache

Docker Network Model

Docker uses the CNM (Container Network Model) architecture, which includes three core components:

  • Sandbox: Container’s network stack
  • Endpoint: Container’s network interface
  • Network: Collection of endpoints that can communicate

Volume Management

Creating and Managing Volumes

# Create volume
docker volume create my-volume
docker volume create --driver local web-data
docker volume create --name db-data

# List volumes
docker volume ls

# View volume details
docker volume inspect my-volume

# Example output:
[
    {
        "CreatedAt": "2023-01-15T10:30:45Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
        "Name": "my-volume",
        "Options": {},
        "Scope": "local"
    }
]

# Delete volume
docker volume rm my-volume

# Clean unused volumes
docker volume prune

# Force delete all volumes (dangerous operation)
docker volume rm $(docker volume ls -q)

Using Volumes

# Use named volume
docker run -d --name mysql-server \
    -v db-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=password \
    mysql:8.0

# Use anonymous volume
docker run -d --name web-server \
    -v /usr/share/nginx/html \
    nginx:latest

# Mount multiple volumes
docker run -d --name app-server \
    -v app-data:/opt/app/data \
    -v log-data:/opt/app/logs \
    -v config-data:/opt/app/config \
    myapp:latest

# Read-only volume
docker run -d --name readonly-app \
    -v config-data:/opt/app/config:ro \
    myapp:latest

Advanced Volume Features

# Pre-populate volume from container
docker run -d --name data-container \
    -v shared-data:/data \
    busybox sh -c "echo 'Hello World' > /data/hello.txt"

# Use same volume in other containers
docker run --rm -v shared-data:/data ubuntu cat /data/hello.txt

# Volume labels
docker volume create \
    --label environment=production \
    --label project=webapp \
    prod-data

# View volumes with labels
docker volume ls --filter label=environment=production

# Create volume with driver options
docker volume create \
    --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.100,rw \
    --opt device=:/path/to/share \
    nfs-volume

Volume Backup and Restore

# Backup volume
docker run --rm \
    -v db-data:/source:ro \
    -v $(pwd):/backup \
    ubuntu tar czf /backup/db-backup-$(date +%Y%m%d).tar.gz -C /source .

# Restore volume
docker volume create db-data-restored

docker run --rm \
    -v db-data-restored:/target \
    -v $(pwd):/backup \
    ubuntu tar xzf /backup/db-backup-20230115.tar.gz -C /target

# Volume migration
# Create backup container
docker create --name backup-container \
    -v source-volume:/source:ro \
    ubuntu

# Copy data to host
docker cp backup-container:/source/. ./backup/

# Restore to new volume
docker run --rm \
    -v target-volume:/target \
    -v $(pwd)/backup:/backup \
    ubuntu cp -r /backup/. /target/

Bind Mounts

Basic Bind Mounting

# Bind mount host directory
docker run -d --name web-dev \
    -p 8080:80 \
    -v /home/user/html:/usr/share/nginx/html \
    nginx:latest

# Use absolute path
docker run -d --name app-dev \
    -v /opt/myapp:/app \
    -w /app \
    python:3.9 python app.py

# Use relative path (current directory)
docker run -d --name node-dev \
    -p 3000:3000 \
    -v $(pwd):/usr/src/app \
    -w /usr/src/app \
    node:16 npm start

# Read-only bind mount
docker run -d --name config-app \
    -v /etc/myapp/config:/opt/app/config:ro \
    myapp:latest

Advanced Bind Mount Options

# Use mount syntax (recommended)
docker run -d --name advanced-app \
    --mount type=bind,source=/host/path,target=/container/path,readonly \
    myapp:latest

# Mount with permission control
docker run -d --name perm-app \
    --mount type=bind,source=/host/data,target=/app/data,bind-propagation=shared \
    myapp:latest

# Set SELinux labels (on SELinux-enabled systems)
docker run -d --name selinux-app \
    -v /host/data:/app/data:Z \
    myapp:latest

# Mount single file
docker run -d --name single-file \
    -v /host/config.json:/app/config.json:ro \
    myapp:latest

Development Environment Practice

# Frontend development environment
mkdir -p ~/projects/frontend-app
cd ~/projects/frontend-app

# Create package.json
cat > package.json << 'EOF'
{
  "name": "frontend-app",
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production"
  }
}
EOF

# Run development container
docker run -it --rm \
    --name frontend-dev \
    -p 3000:3000 \
    -v $(pwd):/app \
    -v /app/node_modules \
    -w /app \
    node:16 bash

# Inside container, install dependencies and start dev server
npm install
npm run dev

Docker Networking

Network Driver Types

Network DriverCharacteristicsUse Cases
bridgeDefault network, single-host communicationSingle-host multi-container apps
hostUses host network stackHigh-performance network applications
noneNo network connectionSecurity isolation scenarios
overlayCross-host communicationCluster environments
macvlanIndependent MAC address containersPhysical network integration

Viewing Default Networks

# View all networks
docker network ls

# Example output:
NETWORK ID     NAME      DRIVER    SCOPE
d8b6a3c7f4e2   bridge    bridge    local
f7a8b9c6d5e4   host      host      local
a1b2c3d4e5f6   none      null      local

# View network details
docker network inspect bridge

# View container network information
docker inspect container-name | jq '.[0].NetworkSettings'

Creating Custom Networks

# Create bridge network
docker network create my-network
docker network create --driver bridge app-network

# Create network with subnet
docker network create \
    --driver bridge \
    --subnet=172.20.0.0/16 \
    --ip-range=172.20.240.0/20 \
    --gateway=172.20.0.1 \
    custom-network

# Create overlay network (Swarm mode)
docker network create \
    --driver overlay \
    --subnet=10.1.0.0/24 \
    --gateway=10.1.0.1 \
    swarm-network

# View network details
docker network inspect my-network

Container Network Connections

# Specify network at runtime
docker run -d --name web-server \
    --network my-network \
    nginx:latest

# Connect existing container to network
docker network connect my-network existing-container

# Disconnect network
docker network disconnect my-network container-name

# Specify IP address for container
docker run -d --name static-ip \
    --network custom-network \
    --ip 172.20.0.10 \
    nginx:latest

# Set hostname and aliases
docker run -d --name web-server \
    --network my-network \
    --hostname webserver \
    --network-alias www \
    nginx:latest

Inter-Container Communication

Same Network Container Communication

# Create application network
docker network create app-net

# Start database container
docker run -d --name mysql-db \
    --network app-net \
    -e MYSQL_ROOT_PASSWORD=password \
    -e MYSQL_DATABASE=appdb \
    mysql:8.0

# Start application container
docker run -d --name web-app \
    --network app-net \
    -p 8080:80 \
    -e DATABASE_HOST=mysql-db \
    -e DATABASE_USER=root \
    -e DATABASE_PASSWORD=password \
    my-web-app:latest

# Test inter-container communication
docker exec web-app ping mysql-db
docker exec web-app nslookup mysql-db

Multi-Network Containers

# Create frontend and backend networks
docker network create frontend-net
docker network create backend-net

# Start database (backend network only)
docker run -d --name database \
    --network backend-net \
    postgres:13

# Start application server (connect to both networks)
docker run -d --name app-server \
    --network backend-net \
    my-app:latest

docker network connect frontend-net app-server

# Start web server (frontend network only)
docker run -d --name web-server \
    --network frontend-net \
    -p 80:80 \
    nginx:latest

Network Troubleshooting

# View container network configuration
docker exec container-name ip addr show
docker exec container-name ip route show

# Test network connectivity
docker exec container1 ping container2
docker exec container1 telnet container2 80

# View network connections
docker exec container-name netstat -tuln
docker exec container-name ss -tuln

# Packet capture analysis
docker exec container-name tcpdump -i eth0 -w /tmp/capture.pcap

Practical Cases

Complete LAMP Architecture

# 1. Create project network
docker network create lamp-network

# 2. Create volumes
docker volume create mysql-data
docker volume create web-data

# 3. Start MySQL database
docker run -d --name mysql \
    --network lamp-network \
    --restart unless-stopped \
    -v mysql-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=rootpassword \
    -e MYSQL_DATABASE=webapp \
    -e MYSQL_USER=webuser \
    -e MYSQL_PASSWORD=webpassword \
    mysql:8.0

# 4. Start Apache+PHP container
docker run -d --name apache-php \
    --network lamp-network \
    --restart unless-stopped \
    -p 8080:80 \
    -v web-data:/var/www/html \
    php:8.1-apache

# 5. Install PHP MySQL extensions
docker exec apache-php docker-php-ext-install mysqli pdo pdo_mysql

# 6. Commit modified image
docker commit apache-php php:8.1-apache-mysql

# 7. Restart Apache container
docker stop apache-php
docker rm apache-php
docker run -d --name apache-php \
    --network lamp-network \
    --restart unless-stopped \
    -p 8080:80 \
    -v web-data:/var/www/html \
    php:8.1-apache-mysql

# 8. Create test PHP file
docker exec apache-php bash -c 'cat > /var/www/html/test.php << "EOF"
<?php
$servername = "mysql";
$username = "webuser";
$password = "webpassword";
$dbname = "webapp";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully to MySQL database!<br>";

phpinfo();
?>
EOF'

# 9. Test application
curl http://localhost:8080/test.php

Microservices Architecture Example

# Create microservices network
docker network create microservices-net

# Service discovery and configuration center
docker run -d --name consul \
    --network microservices-net \
    -p 8500:8500 \
    consul:latest

# API gateway
docker run -d --name api-gateway \
    --network microservices-net \
    -p 80:8080 \
    -e CONSUL_URL=http://consul:8500 \
    my-api-gateway:latest

# User service
docker run -d --name user-service \
    --network microservices-net \
    -e CONSUL_URL=http://consul:8500 \
    -e DB_HOST=postgres \
    my-user-service:latest

# Order service
docker run -d --name order-service \
    --network microservices-net \
    -e CONSUL_URL=http://consul:8500 \
    -e DB_HOST=postgres \
    my-order-service:latest

# PostgreSQL database
docker run -d --name postgres \
    --network microservices-net \
    -v postgres-data:/var/lib/postgresql/data \
    -e POSTGRES_DB=microservices \
    -e POSTGRES_USER=admin \
    -e POSTGRES_PASSWORD=password \
    postgres:13

# Redis cache
docker run -d --name redis \
    --network microservices-net \
    redis:alpine

Development Environment Setup

# Create development project
mkdir ~/dev-environment
cd ~/dev-environment

# Create development network and volumes
docker network create dev-network
docker volume create postgres-dev-data
docker volume create redis-dev-data

# Start development database
docker run -d --name dev-postgres \
    --network dev-network \
    -p 5432:5432 \
    -v postgres-dev-data:/var/lib/postgresql/data \
    -e POSTGRES_DB=devdb \
    -e POSTGRES_USER=developer \
    -e POSTGRES_PASSWORD=devpass \
    postgres:13

# Start Redis
docker run -d --name dev-redis \
    --network dev-network \
    -p 6379:6379 \
    -v redis-dev-data:/data \
    redis:alpine

# Start development container (Node.js environment)
docker run -it --rm \
    --name dev-environment \
    --network dev-network \
    -p 3000:3000 \
    -v $(pwd):/workspace \
    -w /workspace \
    -e DATABASE_URL=postgresql://developer:devpass@dev-postgres:5432/devdb \
    -e REDIS_URL=redis://dev-redis:6379 \
    node:16 bash

# Initialize project in development container
npm init -y
npm install express pg redis

# Create simple application
cat > app.js << 'EOF'
const express = require('express');
const { Pool } = require('pg');
const redis = require('redis');

const app = express();
const port = 3000;

// Database connection
const pool = new Pool({
    connectionString: process.env.DATABASE_URL
});

// Redis connection
const redisClient = redis.createClient({
    url: process.env.REDIS_URL
});

app.get('/', async (req, res) => {
    try {
        const dbResult = await pool.query('SELECT NOW()');
        await redisClient.set('last_visit', new Date().toISOString());
        const lastVisit = await redisClient.get('last_visit');

        res.json({
            message: 'Development environment is working!',
            database_time: dbResult.rows[0].now,
            last_visit: lastVisit
        });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

app.listen(port, '0.0.0.0', () => {
    console.log(`Dev server running on port ${port}`);
});
EOF

# Start development server
node app.js

Network Security and Best Practices

Network Isolation Strategy

# Create networks for different environments
docker network create production-net
docker network create staging-net
docker network create development-net

# Database on backend network only
docker network create backend-net
docker network create frontend-net

# Sensitive services use dedicated network
docker network create secure-net \
    --internal  # Disable external access

Firewall Rules

# Restrict container access to external network
iptables -I DOCKER-USER -s 172.17.0.0/16 -d 8.8.8.8 -j DROP

# Allow specific container to access external API
iptables -I DOCKER-USER -s 172.20.0.10 -d api.example.com -p tcp --dport 443 -j ACCEPT

# Log network connections
iptables -I DOCKER-USER -j LOG --log-prefix "DOCKER-USER: "
Data and Network Best Practices
  1. Data Persistence: Use named volumes rather than bind mounts for important data
  2. Network Isolation: Use different networks for different environments and services
  3. Backup Strategy: Regularly backup important volumes
  4. Security Configuration: Use internal networks for sensitive services, restrict external access
  5. Monitoring and Alerts: Monitor network and storage usage
Important Notes
  • Volumes are not automatically deleted when containers are deleted, manual cleanup required
  • Bind mount file permission issues may prevent applications from running properly
  • Network configuration errors may prevent container communication
  • Use host network mode cautiously in production environments

Summary

Through this chapter, you should have mastered:

  • Data Persistence: Understanding volumes, bind mounts, and other storage mechanisms
  • Network Communication: Understanding Docker network model and inter-container communication
  • Practical Applications: Ability to build complex multi-container application architectures
  • Security Configuration: Understanding network isolation and security best practices

In the next chapter, we’ll learn Docker Compose for declarative orchestration and management of multi-container applications.