Back to blog

Docker Networking & Volumes: Deep Dive

dockernetworkingdevopscontainerstutorial
Docker Networking & Volumes: Deep Dive

Bạn đã biết build và chạy container — giờ hãy tìm hiểu cách chúng giao tiếp với nhau và persist data. Networking và storage là hai lĩnh vực developer hay gặp behavior không ngờ tới, và hiểu sâu về chúng sẽ giúp bạn tiết kiệm hàng giờ debug.

Bài này cover mọi thứ bạn cần biết về Docker networking và storage cho production.

Time commitment: 3–4 ngày, 1–2 tiếng mỗi ngày
Prerequisites: Phase 1: Docker Fundamentals, Phase 2: Docker Compose & Multi-Container Apps


Phần 1: Docker Networking

Docker Networking Hoạt Động Như Thế Nào

Khi Docker khởi động, nó tạo một virtual network interface trên host machine. Mỗi container có network namespace riêng — một isolated view của network stack. Các container giao tiếp qua Docker-managed networks.

# Xem tất cả networks trên hệ thống
docker network ls
 
# Output
NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
b2c3d4e5f6a1   host      host      local
c3d4e5f6a1b2   none      null      local

Docker có ba built-in networks và cho phép bạn tạo custom networks.


Các Network Driver

Bridge Network (Mặc định)

Bridge driver là default cho standalone containers. Docker tạo một virtual Ethernet bridge (docker0) trên host, mỗi container kết nối vào đó sẽ nhận một IP trong subnet đó.

# Chạy container — tự động join default bridge
docker run -d --name web nginx
 
# Inspect default bridge network
docker network inspect bridge

Giới hạn của default bridge:

  • Containers chỉ giao tiếp được qua IP (không có DNS)
  • Không khuyến nghị cho production — dùng custom bridge networks

Custom bridge networks giải quyết vấn đề này:

# Tạo custom network
docker network create myapp-network
 
# Chạy containers trên đó
docker run -d --name backend --network myapp-network myapp:latest
docker run -d --name db --network myapp-network postgres:16
 
# backend có thể reach db bằng hostname
docker exec backend ping db   # ✅ Hoạt động qua DNS

Custom bridge networks cho bạn:

  • Tự động DNS resolution theo container name
  • Isolation tốt hơn với các container khác
  • Có thể connect/disconnect containers lúc runtime

Host Network

Container dùng chung network stack của host — không có network namespace isolation.

docker run --network host nginx
# nginx giờ listen trên host port 80 trực tiếp (không cần -p)

Khi nào dùng host networking:

  • Cần network performance tối đa (không có NAT overhead)
  • Tools cần truy cập host network interfaces (monitoring agents, packet sniffers)
  • Chỉ Linux — không hoạt động trên Docker Desktop cho Mac/Windows

Lưu ý bảo mật: Container thấy tất cả host network interfaces. Dùng cẩn thận.

None Network

Tắt hoàn toàn networking cho container.

docker run --network none alpine sh
# Không có network interfaces ngoài loopback

Hữu ích cho batch jobs xử lý local files và không cần network.

Overlay Network

Overlay networks trải dài qua nhiều Docker hosts (dùng với Docker Swarm hoặc Kubernetes). Cho phép containers trên các máy khác nhau giao tiếp như trên cùng một network.

# Chỉ có trong Swarm mode
docker network create --driver overlay my-overlay

Với single-host development, bạn không cần overlay networks — đó là territory của Kubernetes.


Container DNS và Service Discovery

Docker chạy embedded DNS server tại 127.0.0.11 cho custom networks. Khi container lookup DNS tên của container khác, Docker resolve thành IP của container đó.

# Tạo network và containers
docker network create backend-net
docker run -d --name postgres --network backend-net postgres:16
docker run -d --name redis --network backend-net redis:7
docker run -d --name api --network backend-net myapi:latest
 
# api có thể reach postgres và redis bằng tên
# postgres://postgres:5432  ✅
# redis://redis:6379         ✅

DNS aliases — đặt nhiều hostname cho một container:

docker run -d \
  --network backend-net \
  --network-alias db \
  --network-alias database \
  --name postgres \
  postgres:16
 
# Giờ có thể reach bằng "postgres", "db", hoặc "database"

Service discovery trong Docker Compose là tự động — mỗi service name trở thành DNS hostname:

services:
  api:
    build: .
    # Có thể reach "db" và "cache" bằng tên
  db:
    image: postgres:16
  cache:
    image: redis:7

Port Publishing

Containers mặc định bị isolate — bạn phải explicitly publish ports để chúng accessible từ host.

# -p host_port:container_port
docker run -p 8080:80 nginx             # host:8080 → container:80
docker run -p 127.0.0.1:8080:80 nginx  # chỉ bind vào localhost (an toàn hơn)
docker run -p 80 nginx                  # random host port → container:80
 
# Kiểm tra port được assign
docker port <container_id>

Best practice: Bind vào 127.0.0.1 trong development để port không bị expose ra local network:

docker run -p 127.0.0.1:5432:5432 postgres:16
# Chỉ accessible từ localhost, không phải các máy khác trong network

Network Security và Isolation

Tách networks theo từng tầng

# docker-compose.yml — network isolation kiểu production
services:
  nginx:
    image: nginx:alpine
    networks:
      - frontend
      - backend
 
  api:
    build: .
    networks:
      - backend
      - data
 
  db:
    image: postgres:16
    networks:
      - data   # db KHÔNG có trên frontend — nginx không reach được trực tiếp
 
networks:
  frontend:
  backend:
  data:

Architecture này có nghĩa:

  • nginx nói chuyện với api (cả hai trên backend)
  • api nói chuyện với db (cả hai trên data)
  • nginx không thể reach trực tiếp db — phải đi qua api

Internal networks (không có external access)

docker network create --internal secure-net
# Containers trong network này không thể ra internet

Hữu ích cho databases và internal services không nên có outbound internet access.


Các Lệnh Network Hữu Ích

# List networks
docker network ls
 
# Inspect một network (hiện containers đang kết nối và IP của chúng)
docker network inspect myapp-network
 
# Tạo network
docker network create --driver bridge --subnet 172.20.0.0/16 myapp-network
 
# Connect running container vào network
docker network connect myapp-network my-container
 
# Disconnect
docker network disconnect myapp-network my-container
 
# Xóa các networks không dùng
docker network prune
 
# Xem network settings của container
docker inspect --format='{{json .NetworkSettings}}' my-container | jq

Phần 2: Docker Storage

Vấn Đề Với Container Filesystem

Containers có một writable layer nằm trên các image layers. Mặc định, mọi thứ được ghi vào container đều lưu trong writable layer này:

┌─────────────────────────────┐
│   Writable Container Layer  │  ← bị xóa khi container bị remove
├─────────────────────────────┤
│     Image Layer 3 (RO)      │
├─────────────────────────────┤
│     Image Layer 2 (RO)      │
├─────────────────────────────┤
│     Image Layer 1 (RO)      │
└─────────────────────────────┘

Vấn đề với writable layer:

  • Data bị mất khi container bị remove
  • Data không thể share giữa các containers
  • Ghi dữ liệu dùng copy-on-write storage driver — chậm hơn native filesystem I/O

Docker có ba giải pháp: volumes, bind mounts, và tmpfs.


Volumes

Volumes là cơ chế được ưu tiên để persist data. Docker quản lý hoàn toàn — chúng sống trong /var/lib/docker/volumes/ trên Linux và độc lập với vòng đời container.

# Tạo named volume
docker volume create mydata
 
# Dùng khi chạy container
docker run -d \
  --name postgres \
  -v mydata:/var/lib/postgresql/data \
  postgres:16
 
# Data trong /var/lib/postgresql/data persist dù container bị remove

Volume trong Docker Compose:

services:
  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret
 
volumes:
  pgdata:   # Docker quản lý volume này

Các lệnh volume

# List volumes
docker volume ls
 
# Inspect volume (hiện mountpoint trên host)
docker volume inspect mydata
 
# Xóa volume
docker volume rm mydata
 
# Xóa tất cả volumes không dùng
docker volume prune
 
# Tạo với options
docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.100,rw \
  --opt device=:/path/to/dir \
  nfs-volume

Chia sẻ volumes giữa các containers

# Container đầu tiên ghi data
docker run -d --name writer -v shared-data:/data alpine \
  sh -c "echo 'hello' > /data/message.txt && sleep 3600"
 
# Container thứ hai đọc từ cùng volume
docker run --rm -v shared-data:/data alpine cat /data/message.txt
# Output: hello

Bind Mounts

Bind mounts map một đường dẫn trên host filesystem trực tiếp vào container. Không như volumes, bạn kiểm soát chính xác data nằm ở đâu trên host.

# Mount thư mục hiện tại vào container
docker run -v $(pwd):/app node:20 npm test
 
# Mount một đường dẫn cụ thể trên host
docker run -v /host/path:/container/path nginx

Bind mount trong Docker Compose (setup dev điển hình):

services:
  api:
    build: .
    volumes:
      - ./src:/app/src          # source code — live reload
      - ./config:/app/config    # config files
      - /app/node_modules       # anonymous volume — không overwrite node_modules

Trick anonymous volume (/app/node_modules) ngăn node_modules của host ghi đè các packages đã install trong container.

Bind mount options

# Read-only mount — container không thể sửa files
docker run -v $(pwd)/config:/app/config:ro myapp

So sánh Volumes vs Bind Mounts

VolumesBind Mounts
Quản lý bởiDockerBạn (host path)
Tốt nhất choPersistent data (databases)Development (live code reload)
Portable✅ Có❌ Phụ thuộc host path
Performance✅ Tốt hơn trên Linux✅ Tốt cho read-heavy
BackupQua docker volume commandsTruy cập trực tiếp filesystem
Production✅ Ưu tiên⚠️ Dùng cẩn thận

tmpfs Mounts

tmpfs mounts lưu data trong memory của host — không bao giờ ghi ra disk. Hoàn hảo cho sensitive data (tokens, passwords) không nên persist.

docker run -d \
  --tmpfs /tmp \
  --tmpfs /run:rw,noexec,nosuid,size=100m \
  nginx

Trong Docker Compose:

services:
  api:
    image: myapi
    tmpfs:
      - /tmp
      - /run

Use cases:

  • Temporary session data
  • Sensitive credentials không được ghi ra disk
  • High-speed scratch space để xử lý

Storage Drivers

Docker dùng union filesystem (storage driver) để layer images. Driver ảnh hưởng đến I/O performance và thường trong suốt với bạn, nhưng nên biết.

DriverOSGhi chú
overlay2LinuxMặc định, được khuyến nghị
aufsUbuntu (legacy)Cũ hơn, đang loại bỏ
devicemapperRHEL/CentOS (legacy)Tránh dùng trừ khi bắt buộc
vfsBất kỳChậm, dùng để test
# Kiểm tra storage driver của bạn
docker info | grep "Storage Driver"
# Storage Driver: overlay2

Storage driver chỉ ảnh hưởng writable container layer — volumes bypass nó hoàn toàn và dùng native filesystem I/O.


Production Storage Patterns

Pattern 1: Database với named volume

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: app
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    secrets:
      - db_password
 
volumes:
  pgdata:
 
secrets:
  db_password:
    file: ./secrets/db_password.txt

Pattern 2: Application với volumes riêng cho code và data

services:
  app:
    image: myapp:${VERSION}
    volumes:
      - uploads:/app/uploads      # user-uploaded files persist
      - logs:/app/logs            # logs persist để debug
    tmpfs:
      - /tmp                      # temp files trong memory
 
volumes:
  uploads:
  logs:

Pattern 3: Volume backup và restore

# Backup: tạo tar từ volume
docker run --rm \
  -v pgdata:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/pgdata-backup.tar.gz -C /data .
 
# Restore: giải nén tar vào volume
docker run --rm \
  -v pgdata:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/pgdata-backup.tar.gz -C /data

Pattern 4: NFS volume cho shared storage giữa nhiều hosts

docker volume create \
  --driver local \
  --opt type=nfs4 \
  --opt o=addr=nfs-server.example.com,rw \
  --opt device=:/exports/mydata \
  nfs-shared

Các Lỗi Storage Phổ Biến

Lỗi 1: Ghi database data vào writable layer của container

# ❌ SAI — data mất khi container bị remove
docker run -d postgres:16
 
# ✅ ĐÚNG — data persist
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16

Lỗi 2: Dùng bind mounts cho production databases

# ❌ SAI — phụ thuộc host path cụ thể, vấn đề permissions
volumes:
  - /home/ubuntu/postgres-data:/var/lib/postgresql/data
 
# ✅ ĐÚNG — Docker quản lý
volumes:
  - pgdata:/var/lib/postgresql/data

Lỗi 3: Quên exclude node_modules khỏi bind mounts

# ❌ SAI — node_modules của host ghi đè của container
volumes:
  - .:/app
 
# ✅ ĐÚNG — anonymous volume che node_modules
volumes:
  - .:/app
  - /app/node_modules

Lỗi 4: Không set volume permissions

# Trong Dockerfile — tạo thư mục và set ownership trước khi declare VOLUME
RUN mkdir -p /app/data && chown -R node:node /app/data
USER node
VOLUME /app/data

Ví Dụ Hoàn Chỉnh: Full-Stack App với Networking và Storage

# docker-compose.yml
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ssl-certs:/etc/nginx/certs:ro
    networks:
      - frontend
    depends_on:
      - api
 
  api:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    environment:
      DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/myapp
      REDIS_URL: redis://cache:6379
    volumes:
      - uploads:/app/uploads
    tmpfs:
      - /tmp
    networks:
      - frontend
      - backend
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_healthy
 
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - backend
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  cache:
    image: redis:7-alpine
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redisdata:/data
    networks:
      - backend
    healthcheck:
      test: ["CMD", "redis-cli", "--pass", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
 
networks:
  frontend:    # nginx ↔ api
  backend:     # api ↔ db ↔ cache (nginx KHÔNG thể reach db hay cache trực tiếp)
 
volumes:
  pgdata:      # postgres data
  redisdata:   # redis persistence
  uploads:     # user uploads
  ssl-certs:   # TLS certificates

Setup này:

  • nginx chỉ nói chuyện được với api (không phải db hay cache trực tiếp)
  • api nói chuyện với db và cache bằng tên qua DNS
  • Tất cả data volumes persist qua các lần restart container
  • Healthchecks đảm bảo các dependent services sẵn sàng trước khi api khởi động

Debug Networking

# Container có đang trên đúng network không?
docker inspect --format='{{json .NetworkSettings.Networks}}' my-container | jq
 
# Container A có thể reach container B không?
docker exec container-a ping container-b
docker exec container-a curl http://container-b:8080/health
 
# Container đang listen trên port nào?
docker port my-container
 
# DNS resolution từ bên trong container
docker exec my-container nslookup db
docker exec my-container cat /etc/resolv.conf

Debug Storage

# Container có những volumes nào?
docker inspect --format='{{json .Mounts}}' my-container | jq
 
# Có gì trong volume?
docker run --rm -v my-volume:/data alpine ls -la /data
 
# Dung lượng volume
docker system df -v
 
# Kiểm tra permissions bên trong container
docker exec my-container ls -la /app/uploads
 
# Copy files từ/vào container
docker cp my-container:/app/logs/error.log ./error.log
docker cp ./seed.sql my-container:/tmp/seed.sql

Tóm Tắt và Điểm Chính

✅ Custom bridge networks cung cấp automatic DNS — containers tìm nhau bằng tên, không phải IP
✅ Dùng các networks riêng theo từng tầng (frontend/backend/data) để isolate services
✅ Publish ports với prefix 127.0.0.1: trong development để tránh expose ra local network
✅ Volumes do Docker quản lý, portable, được ưu tiên cho production data persistence
✅ Bind mounts tốt cho development (live code reload) — tránh dùng cho production databases
✅ Dùng tmpfs cho sensitive in-memory data không được ghi ra disk
✅ Luôn khai báo named volumes cho stateful services (postgres, redis, elasticsearch)
✅ Trick anonymous volume (- /app/node_modules) ngăn bind mounts ghi đè dependencies trong container
✅ Dùng docker network inspect và docker exec ... ping để debug networking
✅ Volumes bypass storage driver — chúng dùng native filesystem I/O và nhanh hơn writable layer


Series: Docker & Kubernetes Learning Roadmap
Previous: Deep Dive: Dockerfile Best Practices & Multi-Stage Builds
Next: Deep Dive: Kubernetes Workloads (Coming Soon)


Có câu hỏi về Docker networking hay storage? Đừng ngại liên hệ hoặc để lại comment!

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.