第五章

数据卷:容器删了数据还在

第四层 · 数据卷/绑定挂载 容器删了,数据库数据还在吗

一、血的教训:容器删了,用户头像全没了

小陈在测试环境验证了一个"经典事故":

# 场景:用户上传头像的服务
docker run -d --name upload01 upload-service:latest
# 用户传了 1000 张头像,都存在容器里……

# 要升级版本了,先删旧容器
docker rm -f upload01
# 再起新容器
docker run -d --name upload01 upload-service:v2

# 然后……用户头像全没了!!

"小陈,你知道问题出在哪吗?"老周问。

"数据写在容器里了……容器一删,数据跟着没了。"

"对。容器的文件系统是'临时'的——容器创建时从镜像生成,容器删除时全部销毁。容器适合跑'无状态'的应用,但数据库、文件上传、配置这些'有状态'的数据,必须放到容器外面。 怎么放?用数据卷(Volume)。"


二、三种"数据放外面"的方式

老周画了一张对比:

容器数据管理的三种方式:

① 数据卷(Volume)—— 官方推荐
   docker volume create mydata
   docker run -v mydata:/data ...
   数据存在 Docker 管理的区域(/var/lib/docker/volumes/)
   ✅ 持久化、好备份、跨容器共享
   ✅ 数据与容器解耦(删容器,数据还在)

② 绑定挂载(Bind Mount)—— 直接映射主机目录
   docker run -v /opt/upload:/data ...
   数据存在主机指定路径(如 /opt/upload)
   ✅ 方便(能直接看主机上的文件)
   ❌ 依赖主机路径(可移植性差一点)

③ tmpfs —— 只存内存(临时)
   docker run --tmpfs /tmp ...
   数据只存在内存里,容器停就没了
   ✅ 快(内存),适合临时缓存
   ❌ 重启即失(适合放临时文件,不适合数据)

"生产数据用 Volume(推荐)或 Bind Mount,临时缓存用 tmpfs。 记住这个选择逻辑就行。"


三、数据卷实战:让 MySQL 数据"永生"

老周带小陈把云间书店的 MySQL 容器化,并配上数据卷:

# 1. 创建一个数据卷
docker volume create mysql-data

# 2. 跑 MySQL,把数据目录挂到数据卷上
docker run -d --name db01 \
  -v mysql-data:/var/lib/mysql \    # 数据库文件放数据卷(关键!)
  -e MYSQL_ROOT_PASSWORD=YunJian2026 \
  -e MYSQL_DATABASE=bookstore \
  mysql:8.0

# 3. 写点数据
docker exec -it db01 mysql -uroot -pYunJian2026
# CREATE TABLE ...; INSERT INTO books VALUES ('三体', 29.9);

# 4. 模拟"容器删了"
docker rm -f db01

# 5. 用同一个数据卷再起一个容器
docker run -d --name db02 \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=YunJian2026 \
  mysql:8.0

# 6. 查一下:数据还在!!
docker exec -it db02 mysql -uroot -pYunJian2026 -e "SELECT * FROM books;"
# → 三体 | 29.9   (数据没丢!)

"看到了吗?容器可以随便删、随便换,但数据卷是'传家宝'——数据跟着卷走,不跟容器走。 这就是生产数据库容器化的正确姿势。"

数据卷的关键命令:
  docker volume ls                   # 查看数据卷
  docker volume create mysql-data    # 创建卷
  docker volume inspect mysql-data   # 查看卷详情(挂载点)
  docker volume rm mysql-data        # 删除卷(数据也没了!)
  docker run -v mysql-data:/path     # 挂载卷到容器

"注意:删除数据卷 = 删除数据! 删容器没事,删卷要三思。"


四、绑定挂载实战:开发时"改代码即生效"

"数据卷适合数据,绑定挂载适合开发场景。"老周说,"开发时改代码要立即生效,不用反复 build 镜像:"

# 开发模式:把本地代码目录直接映射进容器
docker run -d -p 8000:8000 \
  -v /home/chen/app:/app \       # 本地代码 → 容器 /app
  -v /home/chen/upload:/upload \ # 本地上传目录 → 容器
  yunjian/ai-recommend:dev

# 效果:
#   本地改代码 → 容器里立即生效(不用重新 build!)
#   容器里写的上传文件 → 直接出现在 /home/chen/upload

"绑定挂载是'开发神器',数据卷是'生产标准'——开发图方便用绑定挂载,生产要稳定用数据卷。"


五、匿名卷与容器共享:进阶玩法

"最后两个进阶:匿名卷和共享卷。"

匿名卷(Anonymous Volume):
  docker run -v /var/lib/mysql ...   # 没给名字的卷
  → Docker 自动生成一个随机名的卷
  → 好处:容器删了卷不会自动删(还有机会找回)
  → 坏处:不好管理(一堆随机名字)
  生产建议:总是给卷起名字

容器间共享数据卷:
  docker run -v shared-data:/data --name a01 ...
  docker run -v shared-data:/data --name a02 ...
  → 两个容器通过同一个卷共享数据(如 nginx 和 php-fpm 共享代码)

六、章末:老周的第四层总结

第四层:数据管理
├── 血的教训:容器文件系统是临时的,删容器=删数据
├── 数据卷 Volume:Docker 管理、持久化、可备份(生产标准)
├── 绑定挂载 Bind Mount:映射主机目录(开发神器)
├── tmpfs:只存内存(临时缓存)
└── 经验:数据库/上传文件必须挂卷;删卷=删数据要三思

"小陈,数据保住了。但下一个问题马上来了:你起了 web 容器、db 容器、cache 容器……它们之间怎么通信? 前端容器怎么找到后端容器?容器有 IP 吗?外面的人怎么访问容器?——下一章,容器网络,这是容器世界里最容易把人绕晕的部分。"

✌ 语言