Docker 运行 MongoDB
官方镜像、卷、compose 片段。
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:生产化清单(自查)
讲清楚要点
- 容器可写层随容器删除而消失:数据必须挂卷到
/data/db——开发环境也要挂,否则「昨晚的数据呢」每周上演。
MONGO_INITDB_ROOT_* 只在首次初始化生效:数据目录非空时被忽略;改密码要么删卷重来,要么进 shell db.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 主库的配置严格分开。
延伸