4、Docker网络与存储:打通容器通信与数据持久化
说实话,刚接触Docker那会儿,我总觉得网络和存储是最容易踩坑的地方。容器跑起来了,但连不上数据库;数据写进去了,容器一删全没了。这些我都经历过。今天咱们就把这两块彻底捋清楚。
4.1 Docker网络模式:三种模式怎么选?
Docker的网络模式,说白了就是决定「容器怎么跟外界通信」。我常用的就三种:bridge、host、none。咱们一个一个看。
4.1.1 bridge模式(默认)
这是Docker默认的网络模式。每个容器都会分配一个独立的IP,通过docker0网桥跟宿主机通信。你想想看,这就像每个容器住在一个独立的小房间里,通过走廊(网桥)进出。
适用场景:单机多容器部署,比如一个Web应用连一个MySQL。
# 创建一个自定义bridge网络
docker network create my-bridge
# 启动两个容器,连到同一个网络
docker run -d --name web --network my-bridge nginx
docker run -d --name db --network my-bridge mysql:5.7
# 容器之间可以直接用容器名通信
docker exec web ping db # 能通!
我个人习惯用自定义bridge,而不是默认的。为什么?因为自定义网络支持DNS解析,容器名就是主机名,不用记IP。我在项目中遇到过,有人用默认bridge,结果容器重启后IP变了,整个服务都连不上。嗯,这个坑我替你们踩过了。
4.1.2 host模式
host模式就简单粗暴了——容器直接共享宿主机的网络栈。没有独立IP,端口也不用映射,容器里监听80,宿主机就监听80。
注意:host模式有安全隐患。容器里的进程跟宿主机进程在同一个网络空间,权限隔离会弱一些。
# 启动一个host模式的nginx
docker run -d --name nginx-host --network host nginx
# 直接访问宿主机IP:80即可,不用-p映射
什么时候用host?我一般在性能敏感的场景用,比如高并发的反向代理。少了一层NAT转发,延迟能低一点点。但说实话,大部分场景用bridge就够了。
4.1.3 none模式
none模式就是容器没有网络。它只有一个loopback接口,连不了外网,也连不了其他容器。
你可能会问:「这有什么用?」我刚开始也觉得没用。后来做安全审计时发现,有些离线计算任务根本不需要网络,用none模式反而更安全。说白了,就是「断网保平安」。
# 启动一个完全隔离的容器
docker run -d --name isolated --network none alpine sleep 3600
4.2 端口映射与容器互联
端口映射,就是把容器的端口暴露到宿主机上。格式是 宿主机端口:容器端口。
# 将容器的3306映射到宿主机的3306
docker run -d --name mysql -p 3306:3306 mysql:5.7
# 也可以映射到随机端口
docker run -d --name mysql -p 3306 mysql:5.7
# 用 docker port mysql 查看实际映射的端口
小技巧:我习惯把数据库端口映射到非标准端口,比如33061。这样能避免跟宿主机上已有的MySQL冲突,也能防一波扫描攻击。
容器互联呢?刚才说了,用自定义bridge网络,容器名就是主机名。但如果你用的是旧版Docker(1.9之前),可能还得用--link参数。现在基本不用了,我提一嘴,你了解就行。
# 旧方式(不推荐)
docker run -d --name web --link db:db nginx
# 新方式(推荐)
docker network create app-net
docker run -d --name web --network app-net nginx
docker run -d --name db --network app-net mysql:5.7
4.3 数据卷(Volume)与绑定挂载
容器是临时的,但数据得是永久的。Docker提供了两种持久化方式:Volume和Bind Mount。
4.3.1 Volume(数据卷)
Volume由Docker管理,存在宿主机的一个特定目录(通常是/var/lib/docker/volumes/)。我推荐优先用Volume,因为它更安全、更便携。
# 创建数据卷
docker volume create mysql-data
# 挂载到容器
docker run -d --name mysql \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
mysql:5.7
# 查看数据卷信息
docker volume inspect mysql-data
我在项目中遇到过,有人直接用Bind Mount挂载了一个目录,结果权限不对,MySQL启动不了。用Volume就没这问题,Docker会自动处理权限。
4.3.2 Bind Mount(绑定挂载)
Bind Mount就是把宿主机上的一个目录直接挂到容器里。适合开发环境,方便你改代码。
# 把当前目录挂载到容器的/app
docker run -d --name app \
-v $(pwd):/app \
node:14 npm start
注意:Bind Mount会覆盖容器内的目录。如果你挂载了一个空目录到/var/lib/mysql,MySQL会报错,因为数据文件没了。
4.4 数据持久化最佳实践
说了这么多,总结几条我自己的经验吧。
4.4.1 数据库一定要用Volume
数据库的数据丢了,那就真完了。我习惯给每个数据库容器创建一个独立的Volume,命名规范是项目名-服务名-data。
# 创建Volume
docker volume create myapp-mysql-data
docker volume create myapp-redis-data
# 启动容器
docker run -d --name mysql \
-v myapp-mysql-data:/var/lib/mysql \
mysql:5.7
docker run -d --name redis \
-v myapp-redis-data:/data \
redis:6
4.4.2 配置文件用Bind Mount
配置文件经常要改,用Bind Mount方便。我一般把配置文件放在宿主机的一个目录里,挂载到容器的对应位置。
# 挂载MySQL配置文件
docker run -d --name mysql \
-v /opt/mysql/conf:/etc/mysql/conf.d \
-v myapp-mysql-data:/var/lib/mysql \
mysql:5.7
4.4.3 日志文件要定期清理
容器日志如果不控制,能把磁盘撑爆。我一般用--log-opt限制日志大小。
# 限制日志文件最大10M,最多保留3个
docker run -d --name app \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx
避坑指南:我曾经遇到过,一个容器跑了半年,日志文件占了50G磁盘。从那以后,我所有容器都加上日志限制。你想想看,磁盘满了,整个服务都挂了,多冤啊。
4.4.4 备份Volume数据
Volume的数据在宿主机上,但宿主机也会坏。我习惯定期备份Volume到其他存储。
# 备份MySQL数据
docker run --rm \
-v myapp-mysql-data:/data \
-v /backup:/backup \
alpine tar czf /backup/mysql-$(date +%Y%m%d).tar.gz -C /data .
嗯,网络和存储这块就这些。说白了,网络选bridge,数据用Volume,日志要限制,备份不能少。你按这个套路来,基本不会出大问题。