03:Docker数据管理与网络

03. Docker 数据管理与网络

3.1 Docker 数据管理

3.1.1 为什么需要数据持久化?

容器的特性:

  • 容器是临时的无状态的
  • 容器删除后,内部数据会丢失
  • 容器重启后,数据会重置到镜像初始状态

需要持久化的场景:

  • 数据库数据(MySQL、PostgreSQL、MongoDB)
  • 应用日志
  • 上传的文件
  • 配置文件
  • 开发时的代码热重载

3.1.2 三种数据挂载方式

Docker 提供三种数据挂载方式:

复制代码
┌─────────────────────────────────────────────┐
│              Docker Host                    │
│                                             │
│  ┌────────────┐  ┌──────────────┐  ┌──────┐│
│  │   Volume   │  │  Bind Mount  │  │tmpfs││
│  │ (推荐方式)  │  │  (主机目录)   │  │(内存)││
│  └──────┬─────┘  └──────┬───────┘  └───┬──┘│
│         │               │               │   │
│  ┌──────▼───────────────▼───────────────▼──┐│
│  │            Container                    ││
│  │  /var/lib/mysql   /app/code   /tmp     ││
│  └─────────────────────────────────────────┘│
└─────────────────────────────────────────────┘

对比表:

特性 Volume Bind Mount tmpfs
存储位置 Docker管理目录 主机任意路径 内存
管理方式 Docker命令管理 直接文件系统 自动清理
性能 优秀 依赖主机FS 最快
适用场景 数据库、日志 开发、配置文件 临时数据、缓存
可移植性

3.2 Volume(数据卷)

3.2.1 Volume 的优势

  • Docker 统一管理,易于备份和迁移
  • 可以在多个容器间共享
  • 性能优秀(不经过 UnionFS)
  • 可以使用 Volume 驱动程序(支持远程主机、云存储)
  • Windows 和 Linux 容器都支持
  • 新建 Volume 可以预填充数据

3.2.2 创建和管理 Volume

创建数据卷:

bash 复制代码
# 创建一个命名 Volume
docker volume create my-volume

# 创建并指定驱动
docker volume create --driver local my-volume

# 创建时设置选项
docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.1,rw \
  --opt device=:/path/to/dir \
  my-nfs-volume

查看 Volume:

bash 复制代码
# 列出所有数据卷
docker volume ls

# 查看数据卷详细信息
docker volume inspect my-volume

# 输出示例:
[
    {
        "CreatedAt": "2024-01-01T10:00:00Z",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
        "Name": "my-volume",
        "Options": {},
        "Scope": "local"
    }
]

删除 Volume:

bash 复制代码
# 删除指定数据卷
docker volume rm my-volume

# 删除多个数据卷
docker volume rm volume1 volume2

# 清理未使用的数据卷(危险操作!)
docker volume prune

# 查看将被删除的数据卷
docker volume prune --dry-run

3.2.3 使用 Volume

方式 1:运行时创建匿名 Volume

bash 复制代码
# -v 指定容器内路径,Docker自动创建匿名卷
docker run -d --name mysql-test \
  -v /var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

# 查看自动创建的卷
docker volume ls
# DRIVER    VOLUME NAME
# local     a1b2c3d4e5f6...  (匿名卷)

方式 2:运行时指定命名 Volume

bash 复制代码
# -v volume-name:container-path
docker run -d --name mysql-prod \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

# 推荐:使用 --mount(更明确)
docker run -d --name mysql-prod \
  --mount source=mysql-data,target=/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

方式 3:在 Dockerfile 中声明

dockerfile 复制代码
FROM mysql:8.0

# 声明 Volume 挂载点
VOLUME /var/lib/mysql

# 运行时 Docker 会自动创建匿名卷

3.2.4 Volume 实战案例

案例 1:MySQL 数据持久化

bash 复制代码
# 1. 创建数据卷
docker volume create mysql-data

# 2. 运行 MySQL 容器
docker run -d \
  --name mysql \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=password123 \
  -e MYSQL_DATABASE=myapp \
  -p 3306:3306 \
  mysql:8.0

# 3. 连接并创建数据
docker exec -it mysql mysql -uroot -ppassword123 -e "
  USE myapp;
  CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100));
  INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob');
"

# 4. 删除容器(数据不会丢失)
docker rm -f mysql

# 5. 用同样的数据卷重新创建容器
docker run -d \
  --name mysql-new \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=password123 \
  -p 3306:3306 \
  mysql:8.0

# 6. 验证数据依然存在
docker exec -it mysql-new mysql -uroot -ppassword123 -e "
  SELECT * FROM myapp.users;
"
# +----+-------+
# | id | name  |
# +----+-------+
# |  1 | Alice |
# |  2 | Bob   |
# +----+-------+

案例 2:容器间共享数据

bash 复制代码
# 1. 创建共享数据卷
docker volume create shared-data

# 2. 容器 A 写入数据
docker run -d --name writer \
  -v shared-data:/data \
  ubuntu \
  bash -c "echo 'Hello from container A' > /data/message.txt && sleep 3600"

# 3. 容器 B 读取数据
docker run --rm \
  -v shared-data:/data \
  ubuntu \
  cat /data/message.txt
# 输出:Hello from container A

案例 3:备份和恢复数据

bash 复制代码
# 备份数据卷
docker run --rm \
  -v mysql-data:/source:ro \
  -v $(pwd):/backup \
  ubuntu \
  tar czf /backup/mysql-backup.tar.gz -C /source .

# 恢复数据卷
# 1. 创建新的数据卷
docker volume create mysql-data-restored

# 2. 解压备份到新卷
docker run --rm \
  -v mysql-data-restored:/target \
  -v $(pwd):/backup \
  ubuntu \
  tar xzf /backup/mysql-backup.tar.gz -C /target

3.3 Bind Mount(绑定挂载)

3.3.1 什么是 Bind Mount?

Bind Mount 将主机的任意目录或文件直接挂载到容器中。

特点:

  • 直接使用主机文件系统
  • 主机和容器实时同步
  • 适合开发环境(代码热重载)
  • 依赖主机目录结构(可移植性差)
  • 容器可以修改主机文件(安全风险)

3.3.2 使用 Bind Mount

基本语法:

bash 复制代码
# -v 主机路径:容器路径[:选项]
docker run -v /host/path:/container/path myimage

# --mount(推荐,更明确)
docker run --mount type=bind,source=/host/path,target=/container/path myimage

示例:

bash 复制代码
# 挂载当前目录到容器
docker run -d --name nginx-dev \
  -v $(pwd)/html:/usr/share/nginx/html:ro \
  -p 8080:80 \
  nginx:alpine

# Windows PowerShell 使用
docker run -d --name nginx-dev `
  -v ${PWD}/html:/usr/share/nginx/html:ro `
  -p 8080:80 `
  nginx:alpine

# 使用 --mount(更安全,目录不存在会报错)
docker run -d --name nginx-dev \
  --mount type=bind,source=$(pwd)/html,target=/usr/share/nginx/html,readonly \
  -p 8080:80 \
  nginx:alpine

选项说明:

  • ro (readonly):只读模式
  • rw (read-write):读写模式(默认)
  • z / Z:SELinux 标签(Linux)

3.3.3 Bind Mount 实战

场景 1:Web 开发实时预览

bash 复制代码
# 项目结构
my-website/
├── index.html
├── style.css
└── app.js

# 运行 NGINX 并挂载代码
docker run -d \
  --name dev-server \
  -v $(pwd):/usr/share/nginx/html:ro \
  -p 8080:80 \
  nginx:alpine

# 修改本地文件,浏览器刷新即可看到变化

场景 2:Node.js 开发热重载

bash 复制代码
# 运行 Node.js 应用,挂载代码目录
docker run -d \
  --name node-dev \
  -v $(pwd):/app \
  -v /app/node_modules \
  -w /app \
  -p 3000:3000 \
  node:18 \
  npm run dev

# 注意:-v /app/node_modules 防止覆盖容器内的依赖

场景 3:挂载配置文件

bash 复制代码
# 自定义 NGINX 配置
docker run -d \
  --name nginx-custom \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  -p 8080:80 \
  nginx:alpine

场景 4:挂载 Docker Socket(高级用法)

bash 复制代码
# 允许容器控制 Docker(如 Portainer、Docker-in-Docker)
docker run -d \
  --name portainer \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -p 9000:9000 \
  portainer/portainer-ce

3.4 tmpfs Mount(临时文件系统)

3.4.1 tmpfs 特点

  • 存储在内存中,不写入磁盘
  • 性能极快
  • 容器停止后数据消失
  • 安全:敏感数据不持久化

适用场景:

  • 临时缓存
  • 敏感数据(密码、Token)
  • 高频读写的临时文件

3.4.2 使用 tmpfs

bash 复制代码
# 基本用法
docker run -d \
  --tmpfs /app/cache \
  myimage

# 指定大小和权限
docker run -d \
  --tmpfs /app/cache:rw,size=100m,mode=1777 \
  myimage

# 使用 --mount(推荐)
docker run -d \
  --mount type=tmpfs,target=/app/cache,tmpfs-size=100m \
  myimage

实战示例:

bash 复制代码
# Redis 使用 tmpfs 存储临时数据
docker run -d \
  --name redis-cache \
  --tmpfs /data:rw,size=500m \
  -p 6379:6379 \
  redis:alpine \
  redis-server --save ""

3.5 Docker 网络

3.5.1 Docker 网络模型

Docker 提供五种网络模式:

复制代码
1. bridge(桥接网络)- 默认,容器间可通信
2. host(主机网络)  - 与主机共享网络栈
3. none(无网络)    - 容器无网络连接
4. container        - 共享其他容器的网络
5. overlay(覆盖网络)- 跨主机容器通信(Swarm)

3.5.2 Bridge 网络(默认)

架构图:

复制代码
┌──────────────────────────────────────────┐
│          Docker Host (192.168.1.100)     │
│                                          │
│  ┌────────────────────────────────────┐  │
│  │     docker0 Bridge (172.17.0.1)    │  │
│  │                                    │  │
│  │  ┌──────────┐      ┌──────────┐   │  │
│  │  │Container1│      │Container2│   │  │
│  │  │172.17.0.2│      │172.17.0.3│   │  │
│  │  └──────────┘      └──────────┘   │  │
│  └────────────────────────────────────┘  │
│                  │                       │
│            ┌─────▼──────┐                │
│            │   eth0     │                │
│            │ (物理网卡)  │                │
│            └────────────┘                │
└──────────────────────────────────────────┘

查看默认网络:

bash 复制代码
# 列出所有网络
docker network ls

# 输出:
# NETWORK ID     NAME      DRIVER    SCOPE
# abc123         bridge    bridge    local
# def456         host      host      local
# ghi789         none      null      local

# 查看 bridge 网络详情
docker network inspect bridge

容器如何使用 bridge:

bash 复制代码
# 默认连接到 bridge 网络
docker run -d --name web1 nginx

# 查看容器 IP
docker inspect web1 | grep IPAddress
#     "IPAddress": "172.17.0.2"

# 容器间可以通过 IP 通信(但不能通过容器名)
docker run --rm alpine ping 172.17.0.2

3.5.3 自定义 Bridge 网络(推荐)

为什么要自定义网络?

  • 容器间可通过容器名通信(自动 DNS 解析)
  • 更好的隔离性
  • 容器可动态连接/断开网络
  • 可配置网络参数

创建自定义网络:

bash 复制代码
# 创建网络
docker network create my-network

# 创建并指定子网
docker network create \
  --driver bridge \
  --subnet 192.168.100.0/24 \
  --gateway 192.168.100.1 \
  my-custom-network

# 查看网络详情
docker network inspect my-network

使用自定义网络:

bash 复制代码
# 创建时指定网络
docker run -d --name web1 --network my-network nginx
docker run -d --name web2 --network my-network nginx

# 容器间通过名称通信
docker exec web1 ping web2
# PING web2 (192.168.100.3): 56 data bytes
# 64 bytes from 192.168.100.3: seq=0 ttl=64 time=0.123 ms

动态连接网络:

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

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

# 连接时指定 IP
docker network connect --ip 192.168.100.10 my-network container-name

3.5.4 Host 网络

容器直接使用主机的网络栈,没有网络隔离

bash 复制代码
# 使用 host 网络
docker run -d --network host nginx

# 容器内的 80 端口直接映射到主机 80 端口
# 不需要 -p 参数

特点:

  • 性能最佳(无 NAT 损耗)
  • 端口冲突风险高
  • 无网络隔离

使用场景:

  • 性能要求极高的应用
  • 监控工具(需要访问主机网络接口)

3.5.5 None 网络

容器没有网络接口(除了 loopback)。

bash 复制代码
docker run -d --network none alpine sleep 3600

# 容器内无法访问外部网络

使用场景:

  • 高度安全的隔离环境
  • 只需本地计算的任务

3.5.6 Container 网络

共享另一个容器的网络命名空间。

bash 复制代码
# 创建容器 A
docker run -d --name container-a nginx

# 容器 B 共享容器 A 的网络
docker run -d --name container-b --network container:container-a alpine sleep 3600

# container-b 可以通过 localhost 访问 container-a 的服务
docker exec container-b wget -O- http://localhost:80

使用场景:

  • 边车模式(Sidecar Pattern)
  • 服务网格(Service Mesh)

3.5.7 端口映射

基本映射:

bash 复制代码
# 映射单个端口(主机端口:容器端口)
docker run -d -p 8080:80 nginx

# 映射到随机主机端口
docker run -d -p 80 nginx

# 查看映射的端口
docker port container-name
# 80/tcp -> 0.0.0.0:32768

# 映射多个端口
docker run -d -p 8080:80 -p 8443:443 nginx

# 指定协议
docker run -d -p 8080:80/tcp -p 53:53/udp nginx

# 绑定到特定 IP
docker run -d -p 127.0.0.1:8080:80 nginx

3.6 网络实战案例

案例 1:Web 应用 + 数据库

bash 复制代码
# 1. 创建自定义网络
docker network create webapp-network

# 2. 运行 MySQL 数据库
docker run -d \
  --name mysql \
  --network webapp-network \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=webapp \
  mysql:8.0

# 3. 运行 Web 应用
docker run -d \
  --name web \
  --network webapp-network \
  -p 8080:8080 \
  -e DB_HOST=mysql \
  -e DB_USER=root \
  -e DB_PASSWORD=secret \
  -e DB_NAME=webapp \
  my-web-app:latest

# Web 应用通过容器名 "mysql" 连接数据库

案例 2:多层应用架构

bash 复制代码
# 创建前端网络和后端网络
docker network create frontend
docker network create backend

# 数据库(仅在后端网络)
docker run -d \
  --name db \
  --network backend \
  mysql:8.0

# API 服务(连接两个网络)
docker run -d \
  --name api \
  --network backend \
  my-api:latest

docker network connect frontend api

# Web 前端(仅在前端网络)
docker run -d \
  --name web \
  --network frontend \
  -p 80:80 \
  nginx

# 结果:
# - web 可以访问 api
# - web 不能直接访问 db
# - api 可以访问 db

案例 3:服务发现

bash 复制代码
# 创建网络
docker network create app-network

# 启动多个后端服务
docker run -d --name api-1 --network app-network my-api
docker run -d --name api-2 --network app-network my-api
docker run -d --name api-3 --network app-network my-api

# 启动负载均衡器(使用容器名进行负载均衡)
docker run -d \
  --name lb \
  --network app-network \
  -p 80:80 \
  my-load-balancer

3.7 网络故障排查

常用排查命令

bash 复制代码
# 1. 查看容器网络配置
docker inspect container-name

# 2. 查看容器 IP
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container-name

# 3. 进入容器测试网络
docker exec -it container-name sh

# 在容器内执行
ping google.com
curl http://other-container
nslookup other-container

# 4. 查看端口映射
docker port container-name

# 5. 查看网络详情
docker network inspect network-name

常见问题

问题 1:容器无法通过名称通信

bash 复制代码
# 原因:使用默认 bridge 网络
# 解决:使用自定义网络
docker network create my-network
docker run --network my-network ...

问题 2:端口映射不生效

bash 复制代码
# 检查容器是否正在监听端口
docker exec container-name netstat -tuln

# 检查防火墙规则
sudo iptables -L -n | grep 8080

问题 3:容器无法访问外网

bash 复制代码
# 检查 DNS 配置
docker exec container-name cat /etc/resolv.conf

# 手动指定 DNS
docker run --dns 8.8.8.8 --dns 8.8.4.4 myimage

相关推荐
Irene19912 小时前
使用 Docker 的 Node.js(附:三种 Node.js 环境详细对比)
docker·容器·node.js
超龄超能程序猿11 小时前
Docker GPU插件(NVIDIA Container Toolkit)安装
运维·docker·容器
岳来12 小时前
docker 从 Path 值看容器启动命令
运维·docker·容器
IT利刃出鞘15 小时前
Docker Compose--解决容器时间不正确的问题
运维·docker·容器
eight *17 小时前
docker部署elk+filebeat日志收集分析系统
elk·docker·容器
自己的九又四分之三站台19 小时前
docker安装pgvector、age和postgis
运维·docker·容器
幺零九零零20 小时前
Docker底层-IPC Namespace(进程间通信隔离)
运维·docker·容器
V胡桃夹子21 小时前
Docker快速部署apollo
运维·docker·容器
oMcLin21 小时前
如何利用 Podman 替代 Docker:无 root 权限的容器管理实践
docker·dubbo·podman