Docker Networking

Now let's learn how containers communicate with each other and the outside world.


Part 1: Understanding Container Networking Basics

The Networking Challenge

Simple Question: How do containers talk to each other?

Scenario:

You have:
├── Web application container (needs to talk to database)
├── Database container (needs to receive connections)
└── Redis cache container (needs to be accessed)

How do they communicate? 🤔

Container Isolation

Remember: Containers are ISOLATED

By default:
├── Each container has its own network stack
├── Own IP address
├── Own network interface
├── Cannot see other containers
└── Like separate computers on a network

Visual:

┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│  Container 1    │  │  Container 2    │  │  Container 3    │
│                 │  │                 │  │                 │
│  IP: 172.17.0.2 │  │  IP: 172.17.0.3 │  │  IP: 172.17.0.4 │
│                 │  │                 │  │                 │
└─────────────────┘  └─────────────────┘  └─────────────────┘
        ↑                    ↑                    ↑
        └────────────────────┴────────────────────┘
                    Docker Network

How Containers Access the Outside World

Your Computer (Host):

Your Computer:
├── IP: 192.168.1.100 (on your home network)
├── Can access internet
└── Runs Docker

Container inside:
├── Has own IP: 172.17.0.2
├── Can access internet through host
└── Uses NAT (Network Address Translation)

Visual:

Internet
   ↕
Your Computer (192.168.1.100)
   ↕
Docker Network (172.17.0.0/16)
   ↕
Containers (172.17.0.2, 172.17.0.3, ...)

Part 2: Default Bridge Network

What is the Bridge Network?

Bridge Network = Default network Docker creates

When you run a container without specifying network:

docker run nginx

It automatically connects to the "bridge" network.


Viewing Networks

List all networks:

docker network ls

Output:

NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
f6e5d4c3b2a1   host      host      local
1a2b3c4d5e6f   none      null      local

Three default networks:

bridge:
├── Default network
├── Containers can communicate
└── Most commonly used

host:
├── Container uses host's network
├── No isolation
└── Advanced use case

none:
├── No network
└── Completely isolated

Inspecting the Bridge Network

docker network inspect bridge

Output (simplified):

[
    {
        "Name": "bridge",
        "Driver": "bridge",
        "Scope": "local",
        "IPAM": {
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Containers": {
            "abc123...": {
                "Name": "container1",
                "IPv4Address": "172.17.0.2/16"
            }
        }
    }
]

Key Information:

Subnet: 172.17.0.0/16
└── IP range for containers

Gateway: 172.17.0.1
└── Docker's network gateway

Containers:
└── Lists all containers on this network

Testing Default Network

Run two containers:

# Container 1
docker run -d --name web1 nginx

# Container 2
docker run -d --name web2 nginx

Check their IPs:

docker inspect web1 | findstr IPAddress

Output:

"IPAddress": "172.17.0.2"
docker inspect web2 | findstr IPAddress

Output:

"IPAddress": "172.17.0.3"

Containers have different IPs on same network! ✓


Trying to Communicate (Default Bridge)

Access web1 from web2:

docker exec -it web2 bash

Inside web2 container:

# Try to ping web1 by IP
apt-get update && apt-get install -y iputils-ping
ping 172.17.0.2

# Output:
# PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
# 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.073 ms
# ✓ Can reach by IP!

# Try to ping by name
ping web1

# Output:
# ping: web1: Name or service not known
# ✗ Cannot reach by name!

Important Discovery:

Default bridge network:
✓ Containers CAN communicate by IP address
✗ Containers CANNOT communicate by name
└── Must use IP addresses (not convenient!)

Part 3: Custom Bridge Networks

Why Create Custom Networks?

Custom networks provide:

✓ Automatic DNS resolution (use container names!)
✓ Better isolation
✓ More control
✓ Can create multiple networks
└── Best practice for multi-container apps

Creating a Custom Network

Syntax:

docker network create NETWORK_NAME

Example:

docker network create my-network

Output:

a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
↑
Network ID

Verify:

docker network ls

Output:

NETWORK ID     NAME         DRIVER    SCOPE
a1b2c3d4e5f6   bridge       bridge    local
b2c3d4e5f6a7   my-network   bridge    local  ← Your new network!
f6e5d4c3b2a1   host         host      local
1a2b3c4d5e6f   none         null      local

Using Custom Network

Run containers on custom network:

# Container 1
docker run -d --name app1 --network my-network nginx

# Container 2
docker run -d --name app2 --network my-network nginx

Now test communication:

docker exec -it app2 bash

Inside app2:

# Install curl
apt-get update && apt-get install -y curl

# Access app1 by NAME!
curl http://app1

# Output:
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# ...
# ✓ Works! Can use container name!

# Also try by IP
curl http://172.18.0.2

# ✓ Also works!

Magic! 🎉

Custom network provides:
✓ DNS resolution (container name → IP)
✓ No need to know IP addresses
✓ Use friendly names
└── Much easier to work with!

Real-World Example: Web App + Database

Scenario: Flask app needs to connect to MySQL

Step 1: Create custom network

docker network create app-network

Step 2: Run MySQL container

docker run -d \
  --name mysql-db \
  --network app-network \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=myapp \
  mysql:8.0

Step 3: Create Python app

app.py:

import mysql.connector
import time

# Wait for MySQL to be ready
time.sleep(10)

# Connect using container name!
db = mysql.connector.connect(
    host="mysql-db",  # ← Container name!
    user="root",
    password="secret",
    database="myapp"
)

cursor = db.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS users (id INT, name VARCHAR(50))")
cursor.execute("INSERT INTO users VALUES (1, 'Alice')")
db.commit()

cursor.execute("SELECT * FROM users")
for row in cursor:
    print(f"User: {row[1]}")

db.close()

requirements.txt:

mysql-connector-python

Dockerfile:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]

Step 4: Build and run app

docker build -t my-app .

docker run --network app-network my-app

Output:

User: Alice

✓ App connected to database using container name!


Part 4: Port Publishing (Port Mapping)

Understanding Port Publishing

Problem:

Container running web server on port 80
├── Port 80 INSIDE container
├── Your computer can't access it
└── External users can't access it

Solution: Port Publishing

Map container port to host port:
Container port 80 → Host port 8080

Now:
├── Access localhost:8080 on your computer
│       ↓
├── Traffic goes to container port 80
└── Web server accessible! ✓

Port Publishing Syntax

Syntax:

docker run -p HOST_PORT:CONTAINER_PORT IMAGE

Examples:

# Map port 8080 to 80
docker run -p 8080:80 nginx

# Map port 3000 to 3000
docker run -p 3000:3000 node-app

# Map port 5432 to 5432
docker run -p 5432:5432 postgres

Multiple Port Mappings

docker run -p 8080:80 -p 8443:443 nginx
#          ↑            ↑
#     HTTP port    HTTPS port

Viewing Port Mappings

docker ps

Output:

CONTAINER ID   IMAGE   PORTS                                   NAMES
abc123def456   nginx   0.0.0.0:8080->80/tcp                   web
                       ↑       ↑    ↑  ↑
                       │       │    │  └── Protocol
                       │       │    └── Container port
                       │       └── Host port
                       └── Listen on all interfaces

Port Binding to Specific Interface

Bind to all interfaces (default):

docker run -p 8080:80 nginx
# Accessible from anywhere

Bind to localhost only:

docker run -p 127.0.0.1:8080:80 nginx
# Only accessible from this computer
# Not accessible from network

Automatic Port Assignment

Let Docker choose the port:

docker run -P nginx
#          ↑
#     Capital P

Docker assigns random port:

docker ps

Output:

PORTS
0.0.0.0:32768->80/tcp
        ↑
   Random port assigned

Access: http://localhost:32768


Part 5: Network Types in Detail

1. Bridge Network (Default)

What it is:

Default network type
├── Software bridge
├── Containers on same bridge can communicate
└── Most common type

When to use:

✓ Single host
✓ Multiple containers need to communicate
✓ Standard web applications

Example:

docker network create --driver bridge my-bridge
docker run --network my-bridge nginx

2. Host Network

What it is:

Container uses host's network directly
├── No network isolation
├── Container shares host's IP
└── Better performance (no NAT)

Example:

docker run --network host nginx

What happens:

Container:
├── Uses host's IP address
├── Port 80 on container = Port 80 on host
├── No port mapping needed
└── Cannot run multiple containers on same port

When to use:

✓ Need maximum network performance
✓ Network debugging
✗ Less isolation (security concern)

3. None Network

What it is:

No network at all
├── Completely isolated
├── No internet access
└── No container communication

Example:

docker run --network none nginx

When to use:

✓ Maximum isolation
✓ Security-critical containers
✓ Batch processing (no network needed)

4. Overlay Network (Advanced)

What it is:

Connects containers across multiple Docker hosts
├── For Docker Swarm
├── Multi-host networking
└── Advanced orchestration

Example:

docker network create --driver overlay my-overlay

When to use:

✓ Docker Swarm mode
✓ Multiple servers
✓ Distributed applications

Part 6: Connecting Containers to Multiple Networks

Container on Multiple Networks

A container can be on multiple networks!

Example:

# Create two networks
docker network create frontend
docker network create backend

# Run database on backend only
docker run -d --name db --network backend mysql

# Run API on both networks
docker run -d --name api --network backend nginx
docker network connect frontend api

# Run web on frontend only
docker run -d --name web --network frontend nginx

Result:

frontend network:
├── api ✓
└── web ✓

backend network:
├── api ✓
└── db ✓

Communication:
├── web → api (via frontend) ✓
├── api → db (via backend) ✓
├── web → db ✗ (not on same network)
└── Isolation achieved! ✓

Connecting/Disconnecting Networks

Connect container to network:

docker network connect NETWORK_NAME CONTAINER_NAME

Disconnect container from network:

docker network disconnect NETWORK_NAME CONTAINER_NAME

Example:

# Connect web to backend
docker network connect backend web

# Now web can access db!

# Disconnect web from backend
docker network disconnect backend web

# web can no longer access db

Part 7: DNS and Service Discovery

Automatic DNS Resolution

Custom networks provide automatic DNS:

Container names = Hostnames

my-app container:
├── Can be reached at: my-app
├── Can be reached at: my-app.my-network
└── Automatic DNS resolution

Testing DNS Resolution

Run containers:

docker network create test-net
docker run -d --name server1 --network test-net nginx
docker run -it --name client --network test-net ubuntu bash

Inside client:

# Install tools
apt-get update && apt-get install -y dnsutils curl

# Test DNS resolution
nslookup server1

# Output:
# Server:         127.0.0.11
# Address:        127.0.0.11#53
# 
# Name:   server1
# Address: 172.18.0.2

# ✓ Container name resolved to IP!

# Access server
curl http://server1
# ✓ Works!

Network Aliases

Give containers additional names:

docker run -d \
  --name mysql-db \
  --network app-net \
  --network-alias database \
  --network-alias db \
  mysql

Now can access as:

mysql-db   (container name)
database   (alias)
db         (alias)

Useful for:

✓ Backward compatibility
✓ Multiple names for same service
✓ Clearer naming

Part 8: Network Isolation Patterns

Pattern 1: Multi-Tier Application

Structure:

┌─────────────────────────────────────┐
│         frontend network             │
│  ┌──────────┐      ┌──────────┐    │
│  │   Web    │──────│   API    │    │
│  └──────────┘      └──────────┘    │
└─────────────────────────┬───────────┘
                          │
┌─────────────────────────┴───────────┐
│         backend network              │
│  ┌──────────┐      ┌──────────┐    │
│  │   API    │──────│ Database │    │
│  └──────────┘      └──────────┘    │
└─────────────────────────────────────┘

Isolation:
├── Web can only talk to API
├── API can talk to both
└── Database is hidden from Web

Implementation:

# Create networks
docker network create frontend
docker network create backend

# Database (backend only)
docker run -d --name db --network backend postgres

# API (both networks)
docker run -d --name api --network backend my-api
docker network connect frontend api

# Web (frontend only)
docker run -d --name web --network frontend -p 80:80 nginx

Pattern 2: Microservices Isolation

Each service on own network:

┌────────────┐  ┌────────────┐  ┌────────────┐
│  Service A │  │  Service B │  │  Service C │
│  Network A │  │  Network B │  │  Network C │
└────────────┘  └────────────┘  └────────────┘
       ↓               ↓               ↓
   ┌────────────────────────────────────┐
   │       API Gateway Network          │
   │         (Common Network)           │
   └────────────────────────────────────┘

Part 9: Practical Multi-Container Application

Building a Complete App

Let's build: Web App + API + Database

Step 1: Create networks

docker network create frontend
docker network create backend

Step 2: Run PostgreSQL

docker run -d \
  --name postgres \
  --network backend \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=myapp \
  postgres:15

Step 3: Create API (api.py)

from flask import Flask, jsonify
import psycopg2
import os

app = Flask(__name__)

def get_db():
    return psycopg2.connect(
        host="postgres",  # Container name!
        database="myapp",
        user="postgres",
        password="secret"
    )

@app.route('/api/users')
def get_users():
    db = get_db()
    cursor = db.cursor()
    cursor.execute("SELECT version()")
    version = cursor.fetchone()
    return jsonify({"database": version[0]})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

requirements.txt:

flask
psycopg2-binary

Dockerfile:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY api.py .
CMD ["python", "api.py"]

Build and run:

docker build -t my-api .

docker run -d \
  --name api \
  --network backend \
  my-api

# Connect to frontend too
docker network connect frontend api

Step 4: Create Web Frontend (index.html)

<!DOCTYPE html>
<html>
<head>
    <title>My App</title>
</head>
<body>
    <h1>Multi-Container App</h1>
    <button onclick="fetchData()">Get Database Info</button>
    <pre id="result"></pre>
    
    <script>
        async function fetchData() {
            const response = await fetch('http://localhost:5000/api/users');
            const data = await response.json();
            document.getElementById('result').textContent = JSON.stringify(data, null, 2);
        }
    </script>
</body>
</html>

nginx.conf:

server {
    listen 80;
    
    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
    
    location /api/ {
        proxy_pass http://api:5000/api/;
    }
}

Dockerfile:

FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf

Build and run:

docker build -t my-web .

docker run -d \
  --name web \
  --network frontend \
  -p 8080:80 \
  my-web

Step 5: Test the application

Open browser: http://localhost:8080

Click button → Data from database! ✓

Architecture:

User Browser
     ↓
Web Container (frontend network)
     ↓
API Container (frontend + backend networks)
     ↓
Database Container (backend network)

✓ Web can't directly access database
✓ API bridges the networks
✓ Proper isolation!

Part 10: Network Commands Reference

Complete Network Commands

# List networks
docker network ls

# Create network
docker network create NETWORK_NAME

# Inspect network
docker network inspect NETWORK_NAME

# Remove network
docker network rm NETWORK_NAME

# Remove all unused networks
docker network prune

# Connect container to network
docker network connect NETWORK_NAME CONTAINER_NAME

# Disconnect container from network
docker network disconnect NETWORK_NAME CONTAINER_NAME

Creating Networks with Options

# Create with custom subnet
docker network create --subnet=192.168.1.0/24 my-net

# Create with custom gateway
docker network create --gateway=192.168.1.1 my-net

# Create with driver
docker network create --driver bridge my-net

# Create with labels
docker network create --label env=prod my-net

Summary

What We Learned:

✅ Container networking basics
✅ Default bridge network
✅ Custom bridge networks
✅ DNS resolution in custom networks
✅ Port publishing/mapping
✅ Network types (bridge, host, none)
✅ Multiple networks per container
✅ Network isolation patterns
✅ Multi-container applications
✅ Service discovery

Key Concepts:

1. Use custom networks for container communication
2. Container names = Hostnames (with custom networks)
3. Port publishing exposes containers to outside
4. Multiple networks = Network isolation
5. Frontend/Backend pattern for security

Best Practices:

✓ Always use custom networks (not default bridge)
✓ Use container names (not IP addresses)
✓ Separate frontend/backend networks
✓ Only publish ports that need external access
✓ Use network aliases for flexibility

Excellent! You now understand Docker networking!

This completes Phase 6: Docker Networking!

🎉 Congratulations! You've completed the Basic Docker Roadmap!

You now know:

  • ✅ Phase 1: Understanding Docker (Why, What, Architecture)
  • ✅ Phase 2: Installation & First Steps
  • ✅ Phase 3: Working with Images
  • ✅ Phase 4: Creating Your Own Images (Dockerfile)
  • ✅ Phase 5: Container Data Management (Volumes)
  • ✅ Phase 6: Docker Networking

No comments:

Post a Comment

Docker Compose

Docker Compose is one of the most powerful and useful Docker tools. Let's dive in! Part 1: What is Docker Compose? The Problem Docker Co...