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

PHASE 1 — Topic 4: How Socket.IO Works Internally (Engine.IO, Transports, and Fallback)

We now understand what Socket.IO is and why it is useful. In this post, we go one level deeper and look at what actually happens behind the ...