大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
Docker 运行 MongoDB
这一课你将能做到
- 用官方
mongo:7镜像启动带数据卷与 root 用户的容器,验证「重建容器数据仍在」 - 编写可与 backend 服务并列的
docker-compose片段,说明端口、环境变量、卷与 healthcheck 的含义 - 严格区分 MySQL 与 Mongo 的配置键与初始化机制,说清
/docker-entrypoint-initdb.d的生效时机
为什么要学
安装课用一次性容器跑通了连通性,但「删容器就丢数据」显然不能接受。本课补齐生产习惯的三块拼图:数据卷(数据活过容器生命周期)、compose 编排(团队一键复现环境)、初始化机制(首次建库自动化)。这些同样是 Docker 面试里数据库类容器的标准考点。
人话:容器随时可以扔,卷里的 /data/db 不能丢;compose 把「MongoDB 7 + root 用户 + 数据卷」写成一份可提交的 YAML。
先建立图像
docker compose
└─ mongo 服务
image: mongo:7
ports: 本机 27017 → 容器 27017
env: MONGO_INITDB_ROOT_USERNAME / PASSWORD(仅首次初始化生效)
volume: mongo_data → /data/db(数据真正的家)
healthcheck: ping 探活
↑
mongosh / Spring Boot / Node 全部通过 URI 连进来
| MongoDB 容器 | MySQL 容器 | |
|---|---|---|
| 镜像 | mongo:7 | mysql:8 |
| 默认端口 | 27017 | 3306 |
| 数据目录 | /data/db | /var/lib/mysql |
| 初始化目录 | /docker-entrypoint-initdb.d | 同名机制 |
| 客户端 | mongosh | mysql |
跟着做
步骤 1:单容器 + 命名卷
# 若安装课已有同名容器:docker rm -f mongo-dev
docker run -d --name mongo-dev \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secret \
-v mongo_data:/data/db \
mongo:7
docker volume ls # 应看到 mongo_data(或带项目前缀)
docker inspect mongo-dev --format '{{ .Mounts }}' # 确认挂载
步骤 2:写入数据,验证「重建容器数据仍在」
docker exec -it mongo-dev mongosh -u admin -p secret --authenticationDatabase admin
use coderoadmap_lab
db.persist_demo.insertOne({ ok: true, note: "重启也不应丢" })
exit
删容器、用同一卷重建:
docker rm -f mongo-dev
docker run -d --name mongo-dev \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=secret \
-v mongo_data:/data/db \
mongo:7
docker exec -it mongo-dev mongosh -u admin -p secret --authenticationDatabase admin \
--eval "db.getSiblingDB('coderoadmap_lab').persist_demo.find()"
应看到:文档还在。这就是「容器可弃、卷长存」的核心实验——没挂卷的安装课容器做这个实验,数据必丢。
步骤 3:编写 compose 文件
项目根目录建 docker-compose.mongo.yml(密码建议从 .env 注入,勿提交明文):
services:
mongo:
image: mongo:7 # 钉版本,不用 latest
container_name: mongo-dev
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: secret
volumes:
- mongo_data:/data/db
# - ./mongo-init:/docker-entrypoint-initdb.d:ro # 可选:初始化脚本
healthcheck: # 探活,编排依赖与 CI 判定都靠它
test: ["CMD", "mongosh", "--quiet", "--eval", "db.runCommand({ ping: 1 }).ok"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mongo_data:
docker compose -f docker-compose.mongo.yml up -d
docker compose -f docker-compose.mongo.yml ps # 状态 healthy
用安装课的 URI 连接验证即可。日常停止用 docker compose stop(保卷保容器);down -v 会删卷 = 删库,只在故意重来时用。
步骤 4:理解初始化脚本机制
把 .js / .sh 放进 /docker-entrypoint-initdb.d(如挂载 ./mongo-init 目录),容器首次初始化(数据目录为空)时按文件名顺序执行一次:
// mongo-init/01-create-app-user.js
db = db.getSiblingDB("coderoadmap_lab");
db.createUser({
user: "app",
pwd: "app123",
roles: [{ role: "readWrite", db: "coderoadmap_lab" }]
});
关键规则写进笔记:卷里已有数据则脚本不会重跑;改了脚本想生效,要么删卷重建(清空数据),要么进容器手动执行。业务用户 app 比 root 更贴近生产习惯——应用连接串应换成最小权限用户。
步骤 5:对照 Spring 配置键
spring:
data:
mongodb:
uri: mongodb://app:app123@127.0.0.1:27017/coderoadmap_lab?authSource=coderoadmap_lab
绝不改动 spring.datasource.url(那是 MySQL)。如果步骤 4 建了 app 用户,authSource 改为业务库名。
步骤 6:生产化清单(自查)
- 镜像钉版本(
mongo:7.x),不用 latest - 数据在命名卷或宿主机目录,且纳入备份策略
- 应用用最小权限用户,不用 root
- 密码走环境变量 / 密钥管理,不进 git
- 端口仅本机或内网可达,不暴露公网
- 生产高可用走副本集(单容器只是开发环境形态)
讲清楚要点
- 容器可写层随容器删除而消失:数据必须挂卷到
/data/db——开发环境也要挂,否则「昨晚的数据呢」每周上演。 MONGO_INITDB_ROOT_*只在首次初始化生效:数据目录非空时被忽略;改密码要么删卷重来,要么进 shelldb.updateUser。- init 目录是「一次性种子」不是「同步机制」:按文件名顺序执行一次;理解这个前提才不会「改了脚本怎么没生效」。
- compose 是团队的复现契约:版本、端口、卷、healthcheck 全部声明式,比口头
docker run稳定得多。 - healthcheck 让「活着」可验证:
depends_on: condition: service_healthy、CI 冒烟测试都依赖它。 - 单容器 ≠ 生产架构:生产是副本集(1 主 2 从 + 仲裁),提供高可用与故障转移,也是启用多文档事务的前提——面试常考。
你可能会懵的地方
-
docker restart、compose down、down -v差在哪? restart 保容器保卷;down删容器保命名卷;down -v连卷一起删 = 清库。日常用 stop/down,-v只在明确要重来时用。 -
Apple Silicon / Windows 上卷性能异常? 文件系统跨虚拟化有开销,开发可接受;数据库镜像的数据卷走 Docker 管理的命名卷(而非 bind mount 到宿主目录)性能更好——本课用的就是命名卷。
-
容器里改了密码,应用连不上了? INITDB 环境变量不再生效。进 mongosh 用
db.updateUser改密码,或评估数据可弃后删卷重建;改完同步更新所有客户端 URI。 -
和 MongoDB Atlas 什么关系? Atlas 是官方托管云(本质是帮你运维的副本集/集群)。本地 Docker 管开发与学习;连接串形态相同,仅主机与认证细节不同——本地练的技能可平移。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
不挂 -v 直接跑 | 删容器数据全丢 | mongo_data:/data/db |
down -v 当日常停机 | 误删数据卷 | 日常 stop / down;-v 仅重来时 |
镜像用 mongo:latest | 环境不可复现 | 钉大版本 mongo:7 |
| 把 Mongo 端口写进 MySQL datasource | 协议错配连不上库 | spring.data.mongodb.uri |
| 改 init 脚本期待重跑 | 卷非空时不执行 | 删卷重建或手动执行 |
| 应用连 root 用户 | 权限过大 | init 建最小权限业务用户 |
卡点急救
- 现象:
docker ps里容器一直 restarting 查日志docker logs mongo-dev。高频原因:卷里残留了与当前环境变量不一致的旧数据(密码对不上)——确认卷归属,必要时换新卷名重建。 - 现象:27017 端口被占用
本机可能还装着原生 MongoDB 服务。Windows 用
netstat -ano | findstr 27017找到占用者停掉,或 compose 改映射27018:27017并同步改 URI。 - 现象:healthcheck 一直 unhealthy
进容器手动跑
mongosh --quiet --eval "db.runCommand({ping:1})"复现;多为认证参数缺失(healthcheck 命令里认证可省略 ping,但路径或引号错误会直接失败),逐字符核对 test 命令数组。
面试常问
-
容器里的 MongoDB 怎么保证数据不丢? 数据目录
/data/db挂载到命名卷或宿主目录,容器删除重建后数据仍在卷里;再配合定期备份(mongodump或云快照)。生产环境则部署副本集,故障转移由集群层保证——分清「持久化」与「高可用」两层。 -
MONGO_INITDB_ROOT_USERNAME什么时候生效? 仅在数据目录为空的首次初始化时生效:创建 root 用户并执行/docker-entrypoint-initdb.d下的脚本;之后重启、重建容器都不会重跑。要重置就得删卷重建,或手动维护用户。 -
生产环境部署 MongoDB 要注意什么? 钉镜像版本、数据卷 + 备份策略、最小权限用户与网络隔离、副本集高可用(也是事务前提)、资源限制与监控(
mongostat/$indexStats)。答出「副本集提供高可用 + 故障转移」是及格线,能提备份与权限是加分。
自测三问
- 为什么要挂载
/data/db?不挂会怎样? MONGO_INITDB_ROOT_*与 init 目录脚本分别在什么时机生效?down与down -v的区别是什么?哪个等价于删库?
小结一句话
mongo:7 + 命名卷 + compose(含 healthcheck)是本地 MongoDB 的标准姿势:容器可弃、数据长存、环境可复现;与 MySQL 主库的配置严格分开。
延伸
- 官方 Docker 安装 MongoDB 教程
- Docker Hub mongo 镜像页
- 下一课:Node.js 驱动与 Mongoose——从脚本视角再打通一条客户端路径。