第四章:Docker网络与存储

好,咱们进入第四章。这一章我打算把Docker里最让人头疼的两个东西讲透——网络和存储。说实话,我刚开始用Docker那会儿,网络模式看得眼花缭乱,数据卷也搞不清跟绑定挂载有啥区别。后来在项目里踩了几次坑,才慢慢摸清楚门道。

这一章咱们会聊:

  • 四种网络模式:bridge、host、none、overlay
  • 数据持久化的两种方式:Volume和Bind Mount
  • 最后拿MySQL容器化部署来练手

嗯,咱们开始吧。

4.1 网络模式:你该选哪个?

Docker的网络模式,说白了就是决定容器怎么跟外界通信。我见过不少新手一上来就用默认的bridge,结果发现容器之间互相访问不了,一脸懵。咱们一个个说。

4.1.1 bridge模式(默认)

这是Docker默认的网络模式。每个容器会分配一个独立的IP,容器之间可以通过IP通信。但有个坑——容器重启后IP会变。你想想看,如果A容器写死了B容器的IP,B一重启,A就找不到了。

核心特点:

  • 每个容器有独立网络命名空间
  • 通过docker0网桥通信
  • 容器间默认不能通过容器名通信(除非用--link或自定义网络)

我个人习惯,如果多个容器需要互相访问,我会创建一个自定义bridge网络。这样容器名就能当域名用了,IP变了也不怕。

# 创建自定义网络
docker network create my-net

# 启动容器时指定网络
docker run -d --name app1 --network my-net nginx
docker run -d --name app2 --network my-net nginx

# 在app1里可以直接ping app2
docker exec app1 ping app2

我在项目中遇到过一个问题:两个容器都在默认bridge网络里,用容器名互相ping不通。查了半天才发现,默认bridge不支持DNS解析。换成自定义网络就好了。这个坑我记了很久。

4.1.2 host模式

host模式,说白了就是容器直接使用宿主机的网络栈。容器没有自己的IP,端口也直接暴露在宿主机上。

注意:host模式有安全隐患。容器里的进程能访问宿主机所有网络接口。我在生产环境几乎不用,除非是性能要求极高的场景,比如用Docker跑网络抓包工具。

# 启动host模式容器
docker run -d --name nginx-host --network host nginx

# 此时访问宿主机80端口就等于访问容器80端口

嗯,这里要注意:host模式在Windows和Mac上表现不一样,因为Docker Desktop是用虚拟机跑的。我建议只在Linux上使用host模式。

4.1.3 none模式

none模式,就是容器没有网络。它只有一个loopback接口,完全隔离。什么时候用?我一般用它来做一些离线计算任务,或者安全要求极高的场景——容器不需要联网,只处理本地数据。

docker run -d --name isolated --network none alpine sleep 3600

我曾经在安全审计项目里,把所有敏感数据处理容器都设成none模式,数据通过Volume传入,处理完再通过Volume传出。这样网络层面绝对安全。

4.1.4 overlay模式

overlay模式是Swarm集群用的。它让不同宿主机上的容器能直接通信,就像在同一个局域网里。底层用的是VXLAN隧道技术。

小提示:如果你用Kubernetes,它有自己的网络方案(Calico、Flannel等),一般不会直接用Docker的overlay。但如果你在用Docker Swarm,overlay是跨主机通信的标配。

# 初始化Swarm集群
docker swarm init

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

# 启动服务时指定网络
docker service create --name web --network my-overlay --replicas 3 nginx

我记得第一次用overlay时,发现跨主机通信延迟比预期高。后来排查发现是宿主机之间的网络带宽不够。嗯,overlay毕竟有封装开销,对网络性能有要求。

4.2 数据持久化:Volume vs Bind Mount

容器默认是"无状态"的,容器一删,数据就没了。但数据库、日志这些数据得留着。Docker提供了两种持久化方式。

4.2.1 Volume(数据卷)

Volume是Docker管理的存储。数据存在宿主机的一个特定目录(/var/lib/docker/volumes/),你不需要关心具体路径。

优点:

  • 由Docker管理,备份和迁移方便
  • 支持Volume驱动,可以存在NFS、云存储上
  • 多个容器可以共享同一个Volume
# 创建Volume
docker volume create my-data

# 查看Volume信息
docker volume inspect my-data

# 挂载Volume到容器
docker run -d --name db -v my-data:/var/lib/mysql mysql:8.0

我个人习惯,生产环境优先用Volume。为什么?因为用docker volume ls就能看到所有数据卷,备份时直接打包/var/lib/docker/volumes/就行。Bind Mount的话,你得自己记住数据存在哪个目录。

4.2.2 Bind Mount(绑定挂载)

Bind Mount就是把宿主机上的一个目录直接挂到容器里。路径由你指定,灵活但需要你自己管理。

# 把当前目录的data文件夹挂到容器
docker run -d --name dev -v $(pwd)/data:/app/data nginx

# 或者用--mount语法(更明确)
docker run -d --name dev --mount type=bind,source=$(pwd)/data,target=/app/data nginx

避坑指南:我曾经在开发环境用Bind Mount,把宿主机代码目录挂到容器里。结果不小心在容器里删了文件,宿主机上的文件也没了。嗯,Bind Mount是双向同步的,操作要小心。

Bind Mount适合开发环境——代码改了,容器里立刻生效。但生产环境我建议用Volume,更安全,也更容易管理。

4.3 实战:MySQL容器化部署

好,理论说完了,咱们动手。拿MySQL来练手,把网络和存储都用上。

4.3.1 基础部署

先跑一个最简单的MySQL容器:

# 拉取镜像
docker pull mysql:8.0

# 启动MySQL
docker run -d \
  --name mysql-dev \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -p 3306:3306 \
  mysql:8.0

这样跑起来,但有个问题——容器一删,数据全没了。咱们得加持久化。

4.3.2 加数据卷

# 创建数据卷
docker volume create mysql-data

# 启动时挂载
docker run -d \
  --name mysql-prod \
  -e MYSQL_ROOT_PASSWORD=StrongPass!2024 \
  -e MYSQL_DATABASE=myapp \
  -e MYSQL_USER=appuser \
  -e MYSQL_PASSWORD=apppass \
  -v mysql-data:/var/lib/mysql \
  -p 3306:3306 \
  mysql:8.0

小技巧:MySQL的配置文件也可以挂载。我习惯把my.cnf放在宿主机上,用Bind Mount挂进去。这样调参数不用重新构建镜像。

# 准备配置文件
mkdir -p /opt/mysql/conf
cat > /opt/mysql/conf/my.cnf << 'EOF'
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=500
EOF

# 启动时挂载配置
docker run -d \
  --name mysql-prod \
  -e MYSQL_ROOT_PASSWORD=StrongPass!2024 \
  -v mysql-data:/var/lib/mysql \
  -v /opt/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf \
  -p 3306:3306 \
  mysql:8.0

4.3.3 加网络隔离

生产环境里,数据库不应该暴露到公网。咱们创建一个内部网络,只让应用容器访问MySQL。

# 创建内部网络
docker network create backend-net

# 启动MySQL,不暴露端口
docker run -d \
  --name mysql-prod \
  --network backend-net \
  -e MYSQL_ROOT_PASSWORD=StrongPass!2024 \
  -v mysql-data:/var/lib/mysql \
  mysql:8.0

# 启动应用容器,连接到同一网络
docker run -d \
  --name app \
  --network backend-net \
  -e DB_HOST=mysql-prod \
  -e DB_USER=root \
  -e DB_PASS=StrongPass!2024 \
  myapp:latest

这样,MySQL只在内网通信,外部访问不到。应用容器通过容器名mysql-prod就能连上数据库。我在项目中就是这么做的,安全又方便。

4.3.4 备份与恢复

数据卷的备份也很简单:

# 备份
docker run --rm \
  -v mysql-data:/var/lib/mysql \
  -v /backup:/backup \
  alpine tar czf /backup/mysql-backup-$(date +%Y%m%d).tar.gz -C /var/lib/mysql .

# 恢复
docker run --rm \
  -v mysql-data:/var/lib/mysql \
  -v /backup:/backup \
  alpine tar xzf /backup/mysql-backup-20240101.tar.gz -C /var/lib/mysql

总结一下:

  • 开发环境用Bind Mount,方便调试
  • 生产环境用Volume,安全可靠
  • 多容器通信用自定义网络
  • 数据库一定要挂载数据卷,否则删容器等于删库

嗯,这一章的内容就到这儿。网络和存储是Docker容器化的基石,搞懂了这两个,后面做CI/CD流水线就顺手多了。下一章咱们会聊Docker Compose,把多个容器编排起来。到时候MySQL、Redis、应用一起跑,那才叫真正的容器化部署。