前言:容器间通信与数据管理的核心

在使用 Docker 部署应用程序时,最重要的两个主题就是网络。在由多个容器组成的微服务架构中,容器间的通信是必不可少的,而且容器被删除后数据仍需保留的情况也非常常见。

本第5篇将深入介绍 Docker 的网络模型和卷系统,并一起学习可以在实际运营环境中使用的实用示例。

1. Docker 网络概述

1.1 Docker 网络的必要性

容器基本上在隔离的环境中运行。但在实际应用程序中,Web 服务器需要与数据库通信,API 网关需要与多个微服务连接。Docker 网络可以安全高效地管理这种容器间通信。

1.2 网络相关基本命令

# 查看网络列表
docker network ls

# 查看网络详细信息
docker network inspect [网络名]

# 创建网络
docker network create [网络名]

# 删除网络
docker network rm [网络名]

# 批量删除未使用的网络
docker network prune

2. Docker 网络类型

2.1 Bridge 网络(默认)

Bridge 网络是 Docker 的默认网络驱动,允许同一主机内的容器相互通信。安装 Docker 时会自动创建名为 docker0 的桥接网络。

# 使用默认 bridge 网络运行容器
docker run -d --name web1 nginx

# 查看默认 bridge 网络信息
docker network inspect bridge

特点:

  • 支持同一主机上容器间的通信
  • 主机与容器之间使用 NAT(网络地址转换)
  • 通过端口映射(-p 选项)允许外部访问
  • 默认 bridge 中无法通过容器名通信(只能通过 IP)

2.2 Host 网络

Host 网络让容器直接使用主机的网络栈。由于没有网络隔离,性能更好但需要注意安全性。

# 使用 host 网络运行容器
docker run -d --network host --name web-host nginx

# 无需端口映射即可直接通过主机的 80 端口访问

特点:

  • 使用与主机相同的网络环境
  • 无 NAT 开销,网络性能更好
  • 无需端口映射(直接使用主机端口)
  • 仅在 Linux 上完全支持(macOS、Windows 有限制)

2.3 None 网络

None 网络完全禁用容器的网络。用于完全不需要网络的批处理作业或安全性要求高的环境。

# 使用 none 网络运行容器
docker run -d --network none --name isolated alpine sleep 3600

# 检查网络接口(只有 lo)
docker exec isolated ip addr

特点:

  • 无网络接口(只有 loopback)
  • 完全阻断外部通信
  • 最高级别的网络隔离

2.4 Overlay 网络

Overlay 网络可以实现跨多个 Docker 主机的容器间通信。在 Docker Swarm 或 Kubernetes 等集群环境中是必需的。

# 需要启用 Swarm 模式
docker swarm init

# 创建 overlay 网络
docker network create -d overlay my-overlay-network

# 在 overlay 网络中创建服务
docker service create --name web --network my-overlay-network nginx

特点:

  • 支持多主机间容器通信
  • 使用 VXLAN 技术的虚拟网络
  • Swarm 模式下自动服务发现
  • 内置负载均衡功能

3. 自定义网络

3.1 用户自定义 Bridge 网络

使用用户自定义 bridge 网络而非默认 bridge 网络有多种优势。

# 创建用户自定义网络
docker network create my-app-network

# 指定子网和网关
docker network create \
    --driver bridge \
    --subnet 172.20.0.0/16 \
    --gateway 172.20.0.1 \
    my-custom-network

# 将容器连接到网络
docker run -d --name db --network my-app-network mysql:8.0
docker run -d --name web --network my-app-network nginx

用户自定义网络的优势:

  • 自动 DNS 解析:可以通过容器名进行通信
  • 更好的隔离:与其他网络的容器自动隔离
  • 动态连接:可以将运行中的容器连接/断开网络
  • 可配置:可以自定义子网、网关、IP 范围等

3.2 通过容器名通信

# 创建用户自定义网络
docker network create app-net

# 运行 MySQL 容器
docker run -d \
    --name mysql-db \
    --network app-net \
    -e MYSQL_ROOT_PASSWORD=secret \
    mysql:8.0

# Web 应用程序可以通过 mysql-db 这个名称访问
docker run -d \
    --name webapp \
    --network app-net \
    -e DB_HOST=mysql-db \
    my-webapp:latest

4. 容器间通信

4.1 同一网络内通信

# 创建网络
docker network create backend

# 将两个容器连接到同一网络
docker run -d --name server1 --network backend alpine sleep 3600
docker run -d --name server2 --network backend alpine sleep 3600

# 从 server1 ping server2 测试
docker exec server1 ping -c 3 server2

4.2 连接到多个网络

一个容器可以连接到多个网络。这对于充当代理或网关的容器很有用。

# 创建两个网络
docker network create frontend
docker network create backend

# 数据库只连接到 backend
docker run -d --name db --network backend mysql:8.0

# Web 服务器连接到 frontend
docker run -d --name web --network frontend nginx

# API 服务器连接到两个网络
docker run -d --name api --network frontend node:18
docker network connect backend api

4.3 网络连接和断开

# 将运行中的容器连接到网络
docker network connect my-network container-name

# 从网络断开容器
docker network disconnect my-network container-name

# 以特定 IP 地址连接
docker network connect --ip 172.20.0.100 my-network container-name

5. DNS 和服务发现

5.1 内置 DNS 服务器

Docker 在用户自定义网络中提供内置 DNS 服务器(127.0.0.11)。通过它可以使用容器名或网络别名查找其他容器。

# 检查 DNS 配置
docker exec webapp cat /etc/resolv.conf
# nameserver 127.0.0.11
# options ndots:0

5.2 网络别名 (Alias)

# 设置网络别名
docker run -d \
    --name mysql-primary \
    --network app-net \
    --network-alias db \
    --network-alias database \
    mysql:8.0

# 也可以通过 db 或 database 这些名称访问
docker run --rm --network app-net alpine ping -c 2 db

5.3 轮询 DNS

如果有多个具有相同网络别名的容器,Docker DNS 会以轮询方式返回 IP。

# 以相同别名运行多个 Web 服务器
docker run -d --name web1 --network app-net --network-alias webserver nginx
docker run -d --name web2 --network app-net --network-alias webserver nginx
docker run -d --name web3 --network app-net --network-alias webserver nginx

# 请求 webserver 会连接到三个容器之一
docker run --rm --network app-net alpine nslookup webserver

6. 数据持久化的重要性

6.1 容器的易失性

容器本质上是易失的(ephemeral)。当容器被删除时,存储在容器内部的所有数据也会一起消失。在处理数据库、用户上传文件、配置文件等重要数据时,这会成为严重问题。

# 在容器内创建文件
docker run -d --name temp-container alpine sh -c "echo 'important data' > /data.txt && sleep 3600"
docker exec temp-container cat /data.txt  # important data

# 删除容器
docker rm -f temp-container

# 新容器中没有数据
docker run --rm alpine cat /data.txt  # 错误:文件不存在

6.2 需要数据持久化的情况

  • 数据库:MySQL、PostgreSQL、MongoDB 等的数据文件
  • 用户上传:图片、文档等用户上传的文件
  • 日志文件:应用程序日志、访问日志
  • 配置文件:应用程序配置、证书
  • 会话数据:缓存、会话存储

7. Docker 卷类型

7.1 Volumes(托管卷)

由 Docker 管理的卷,是最推荐的方式。卷创建在 Docker 的存储区域(/var/lib/docker/volumes/)中。

# 创建卷
docker volume create my-data

# 查看卷列表
docker volume ls

# 卷详细信息
docker volume inspect my-data

# 使用卷运行容器
docker run -d \
    --name db \
    -v my-data:/var/lib/mysql \
    mysql:8.0

# 或使用 --mount 标志(更明确)
docker run -d \
    --name db \
    --mount source=my-data,target=/var/lib/mysql \
    mysql:8.0

Volumes 的优势:

  • 可以通过 Docker CLI 轻松管理(备份、迁移)
  • 在 Linux 和 Windows 容器上都可以工作
  • 可以在多个容器之间安全共享
  • 通过卷驱动支持远程存储、加密
  • 独立于主机的文件系统结构

7.2 Bind Mounts

将主机的特定路径挂载到容器中。在开发环境中将源代码实时反映到容器中时很有用。

# 将主机目录挂载到容器
docker run -d \
    --name web \
    -v /host/path:/container/path \
    nginx
docker run -d \
    --name web \
    -v $(pwd)/html:/usr/share/nginx/html \
    nginx

# 只读挂载
docker run -d \
    --name web \
    -v /host/path:/container/path:ro \
    nginx

# 使用 --mount 标志(更明确)
docker run -d \
    --name web \
    --mount type=bind,source=/host/path,target=/container/path \
    nginx

Bind Mounts 的特点:

  • 需要指定主机的准确路径
  • 直接访问主机文件系统
  • 适合开发环境中源代码的实时反映
  • 如果主机目录不存在会自动创建(需要注意)

7.3 tmpfs Mounts

tmpfs 将数据存储在主机的内存中。用于临时存储敏感信息或需要高性能时使用。

# 使用 tmpfs 挂载
docker run -d \
    --name cache \
    --tmpfs /app/cache \
    my-app:latest

# 使用 --mount 标志
docker run -d \
    --name secure-app \
    --mount type=tmpfs,destination=/run/secrets,tmpfs-size=64m \
    my-app:latest

tmpfs 的特点:

  • 只存储在内存中,非常快
  • 容器停止时数据自动删除
  • 适合临时存储敏感数据(密码、API 密钥)
  • 仅在 Linux 上支持

8. 卷创建和管理

8.1 卷创建和查看

# 创建基本卷
docker volume create app-data

# 创建带标签的卷
docker volume create \
    --label project=myapp \
    --label env=production \
    myapp-data

# 查看所有卷
docker volume ls

# 按标签过滤
docker volume ls --filter label=project=myapp

# 查看特定卷详细信息
docker volume inspect app-data

8.2 删除卷

# 删除特定卷(仅当未使用时)
docker volume rm app-data

# 删除所有未使用的卷
docker volume prune

# 带过滤器删除
docker volume prune --filter label=env=development

8.3 卷共享

# 创建卷
docker volume create shared-data

# 多个容器使用同一卷
docker run -d --name writer -v shared-data:/data alpine sh -c "while true; do date >> /data/log.txt; sleep 5; done"
docker run -d --name reader -v shared-data:/data:ro alpine tail -f /data/log.txt

9. 卷驱动

9.1 本地驱动选项

# 创建 NFS 卷
docker volume create \
    --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.100,rw \
    --opt device=:/path/to/share \
    nfs-volume

# 以特定文件系统类型创建卷
docker volume create \
    --driver local \
    --opt type=ext4 \
    --opt device=/dev/sdb1 \
    ext4-volume

9.2 第三方卷驱动

可以使用各种第三方卷驱动来利用云存储或分布式文件系统。

  • Amazon EBS:AWS 的 Elastic Block Store
  • Azure File Storage:Microsoft Azure 的文件存储
  • GlusterFS:分布式文件系统
  • Portworx:企业级容器存储
  • REX-Ray:支持多种存储平台

10. 数据备份和恢复

10.1 卷备份

# 使用临时容器备份卷
docker run --rm \
    -v my-data:/source:ro \
    -v $(pwd):/backup \
    alpine tar cvf /backup/my-data-backup.tar -C /source .

# 压缩备份
docker run --rm \
    -v my-data:/source:ro \
    -v $(pwd):/backup \
    alpine tar czvf /backup/my-data-backup.tar.gz -C /source .

10.2 卷恢复

# 创建新卷
docker volume create my-data-restored

# 从备份恢复
docker run --rm \
    -v my-data-restored:/target \
    -v $(pwd):/backup \
    alpine tar xvf /backup/my-data-backup.tar -C /target

10.3 容器间卷复制

# 从源卷复制到目标卷
docker run --rm \
    -v source-volume:/source:ro \
    -v target-volume:/target \
    alpine cp -av /source/. /target/

10.4 数据库备份示例

# MySQL 数据库备份
docker exec mysql-container mysqldump -u root -p'password' --all-databases > backup.sql

# PostgreSQL 数据库备份
docker exec postgres-container pg_dumpall -U postgres > backup.sql

# MongoDB 数据库备份
docker exec mongo-container mongodump --archive --gzip > backup.gz

11. 实战示例:Web 应用程序栈

11.1 利用网络和卷的完整栈

# 创建网络
docker network create webapp-network

# 创建卷
docker volume create mysql-data
docker volume create webapp-uploads

# MySQL 数据库
docker run -d \
    --name mysql \
    --network webapp-network \
    -v mysql-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=rootpass \
    -e MYSQL_DATABASE=webapp \
    -e MYSQL_USER=appuser \
    -e MYSQL_PASSWORD=apppass \
    mysql:8.0

# Web 应用程序
docker run -d \
    --name webapp \
    --network webapp-network \
    -v webapp-uploads:/app/uploads \
    -e DB_HOST=mysql \
    -e DB_USER=appuser \
    -e DB_PASSWORD=apppass \
    -e DB_NAME=webapp \
    -p 3000:3000 \
    my-webapp:latest

# Nginx 反向代理
docker run -d \
    --name nginx \
    --network webapp-network \
    -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
    -p 80:80 \
    nginx:alpine

11.2 使用 Docker Compose 管理

# docker-compose.yml
version: '3.8'

services:
  mysql:
    image: mysql:8.0
    networks:
      - webapp-network
    volumes:
      - mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: webapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppass

  webapp:
    image: my-webapp:latest
    networks:
      - webapp-network
    volumes:
      - webapp-uploads:/app/uploads
    environment:
      DB_HOST: mysql
      DB_USER: appuser
      DB_PASSWORD: apppass
      DB_NAME: webapp
    depends_on:
      - mysql

  nginx:
    image: nginx:alpine
    networks:
      - webapp-network
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "80:80"
    depends_on:
      - webapp

networks:
  webapp-network:
    driver: bridge

volumes:
  mysql-data:
  webapp-uploads:

12. 网络和卷最佳实践

12.1 网络最佳实践

  • 使用用户自定义网络:使用自定义网络而非默认 bridge,利用 DNS 解析和隔离优势
  • 只暴露必要的端口:用 -p 选项最小化暴露给外部的端口
  • 按服务分离网络:将前端、后端、数据库等分离到不同的网络
  • 利用网络别名:使用别名实现灵活的服务发现

12.2 卷最佳实践

  • 使用命名卷:使用命名卷而非匿名卷以便于管理
  • 开发使用 bind mount,生产使用 volume:根据环境选择合适的挂载方式
  • 定期备份:重要数据必须定期备份
  • 只读挂载:对于配置文件等不需要更改的数据使用 :ro 选项
  • 清理未使用的卷:使用 docker volume prune 定期清理

结论

Docker 网络和卷是基于容器的应用程序的核心组成部分。通过适当的网络配置可以确保容器间安全高效的通信,通过正确的卷策略可以确保数据的持久性和安全性。

本篇学习的内容总结:

  • Docker 网络类型(bridge、host、none、overlay)及各自的用途
  • 用户自定义网络的优势和基于 DNS 的服务发现
  • 卷、bind mount、tmpfs 的区别和适当的使用场景
  • 数据备份和恢复策略

下一篇第6篇将介绍 Kubernetes 基础。我们将了解为什么需要 Kubernetes、它的架构是如何构成的、核心对象是什么。