硬核实践:使用 Docker 部署生产级 MongoDB

MongoDB 的 Docker 化部署同样便捷,但默认的无认证、无持久化启动方式对于生产环境无异于裸奔。

本文将从零开始,拆解 docker run 的每一个参数,再到 Compose 声明式编排,涵盖认证、权限、备份、调优等关键环节,让你的 MongoDB 既安全又高效。


1. 使用 docker run 部署

1.1 最简启动(仅供测试)

bash 复制代码
docker run --name mongo-test -d mongo:8

这会拉取最新 mongo:8 镜像并启动,但强烈不推荐生产使用:

  • 没有认证,任何人可无密码连接。

  • 数据未持久化,容器删除即丢失。

  • 性能参数全为默认,不适合高负载。

1.2 生产级 docker run 命令及参数详解

一条面向生产的启动命令:

bash 复制代码
docker run -d \
  --name mongo-prod \
  --restart=unless-stopped \
  -p 27017:27017 \
  -v /etc/localtime:/etc/localtime:ro \
  -v mongo-data:/data/db \
  -v mongo-configdb:/data/configdb \
  -v /opt/mongo/mongod.conf:/etc/mongod.conf:ro \
  -e MONGO_INITDB_ROOT_USERNAME=root \
  -e MONGO_INITDB_ROOT_PASSWORD_FILE=/run/secrets/mongo_root_password \
  -e MONGO_INITDB_DATABASE=app_db \
  mongo:7 \
  --config /etc/mongod.conf
参数逐行解析:
  • -d:后台运行。

  • --name mongo-prod:指定容器名称,方便后续操作。

  • --restart=unless-stopped:Docker 服务重启或容器异常退出时自动重启,手动停止则不会。

  • -p 27017:27017 :端口映射 宿主机:容器。可改为 127.0.0.1:27017:27017 仅限本地访问,或 37017:27017 避免端口冲突。生产环境不建议使用27017【默认端口】,容易被共计,同时也要为了更安全,注意禁止外部访问。

  • -v /etc/localtime:/etc/localtime:ro:同步宿主机时区,避免日志和函数时间偏差。

  • -v mongo-data:/data/db :数据持久化。mongo-data 是 Docker 命名卷,位于 /var/lib/docker/volumes/。也可用绑定挂载 -v /data/mongo:/data/db

  • -v mongo-configdb:/data/configdb:存储副本集配置等元数据(即使单机也建议挂载)。

  • -v /opt/mongo/mongod.conf:/etc/mongod.conf:ro :挂载自定义配置文件。MongoDB 官方镜像会自动识别 /etc/mongod.conf 并作为配置启动(--config /etc/mongod.conf)。只读挂载更安全。

  • 环境变量(镜像初始化入口):

    • MONGO_INITDB_ROOT_USERNAME:创建 root 用户名。

    • MONGO_INITDB_ROOT_PASSWORD_FILE:指向包含密码的文件,安全替代明文环境变量。容器内路径通常为 /run/secrets/<name>。若不用文件,可用 MONGO_INITDB_ROOT_PASSWORD 直接设密码,但会暴露在 docker inspect 中。

    • MONGO_INITDB_DATABASE:初始化时自动创建的数据库名(非必须,但如果创建了应用用户,该用户对该库有读写权限;这里仅创建库,不自动创建用户,需配合 /docker-entrypoint-initdb.d/ 脚本)。

  • --config /etc/mongod.conf :传递命令行参数给 mongod,明确使用我们挂载的配置文件。

1.3 连接 MongoDB 与修改密码

进入容器并使用 mongosh(新版官方 shell):

bash 复制代码
docker exec -it mongo-prod mongosh -u root 

输入文件中的密码即可登录。若使用明文环境变量,也可直接在宿主机通过端口连接:

bash 复制代码
mongosh --host 127.0.0.1 --port 27017 -u root -p --authenticationDatabase admin

首次安全动作:若初期使用了随机密码等,登录后立即修改 root 密码:

bash 复制代码
use admin
db.changeUserPassword("root", "new_strong_password")

建议创建一个专用应用账户(见下一节),避免 root 滥用。


2. 认证与用户管理

2.1 启用认证

当设置了 MONGO_INITDB_ROOT_USERNAME 和密码变量时,镜像会自动以 --auth 模式启动,或等价地在配置中启用授权。生产环境必须确保 authorization: enabled。若使用自定义配置文件,请显式加入:

bash 复制代码
security:
  authorization: enabled

否则,任何人不需密码即可访问。

2.2 创建应用用户并细粒度授权

登录 root 后,为应用创建独立账号并授予对应数据库的 readWrite 角色:

bash 复制代码
use app_db
db.createUser({
  user: "app_user",
  pwd: "AppStrongPassword",
  roles: [{ role: "readWrite", db: "app_db" }]
})

如果应用只需要读取,应使用 read 角色。更细粒度的可自定义角色。

验证新用户连接:

bash 复制代码
mongosh -u app_user -p --authenticationDatabase app_db --host ...

最佳实践 :不要将 root 或具有 root 角色的账户提供给应用。

2.3 禁用匿名访问与测试数据库

官方镜像默认不会创建测试数据库,但为了加固,可在配置文件中设置:

bash 复制代码
net:
  bindIp: 0.0.0.0   # 生产若只允许容器间通信,可设为具体IP或127.0.0.1

并确保 security.authorization: enabled 生效。


3. 自定义配置与性能调优

3.1 挂载配置文件

创建宿主文件 /opt/mongo/mongod.conf(权限 600),内容示例:

bash 复制代码
# mongod.conf
net:
  port: 27017
  bindIp: 0.0.0.0
  maxIncomingConnections: 3000

security:
  authorization: enabled

storage:
  dbPath: /data/db
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1.5          # 物理内存的50%~70%,根据容器限制调整
    collectionConfig:
      blockCompressor: snappy

systemLog:
  destination: file
  path: /var/log/mongodb/mongod.log
  logAppend: true

setParameter:
  enableLocalhostAuthBypass: false   # 禁止本地回环免密(提升安全性)

挂载到 /etc/mongod.conf 后,MongoDB 会自动加载(镜像入口点脚本逻辑)。启动后可在 mongosh 中验证:

bash 复制代码
db.serverCmdLineOpts().parsed

3.2 时区问题

MongoDB 本身存储为 UTC 时间,无需修改。但在日志或客户端驱动中展示本地时间,可通过挂载 /etc/localtime 和设置宿主机时区变量 TZ。如有需要,可在宿主机 Docker 启动脚本中设置 -e TZ=Asia/Shanghai

3.3 日志持久化

配置文件中的 systemLog.path 路径如果不在数据卷内,日志会在容器内丢失。建议将 /var/log/mongodb 也挂载出来,或使用 Docker 的日志驱动。简单做法是让 MongoDB 将日志打到 stdout(不设定 destination: file),由 docker logs 集中收集,生产推荐后者。


4. Docker Compose 部署方案

4.1 生产级 docker-compose.yml

bash 复制代码
version: '3.8'

services:
  mongo:
    image: mongo:8
    container_name: mongo-prod
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/mongo_root_password
      MONGO_INITDB_DATABASE: app_db
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db
      - mongo-configdb:/data/configdb
      - /etc/localtime:/etc/localtime:ro
      - ./mongod.conf:/etc/mongod.conf:ro
      - mongo-log:/var/log/mongodb
    networks:
      - backend
    secrets:
      - mongo_root_password
    command: ["--config", "/etc/mongod.conf"]
    healthcheck:
      test: ["CMD-SHELL", "mongosh --quiet --eval 'db.adminCommand(\"ping\").ok' -u root -p \"$$(cat /run/secrets/mongo_root_password)\" --authenticationDatabase admin || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 40s

volumes:
  mongo-data:
  mongo-configdb:
  mongo-log:

networks:
  backend:

secrets:
  mongo_root_password:
    file: ./secrets/mongo_root_password.txt

4.2 关键点说明

  • secrets :密码存入外部文件(权限 600),通过 /run/secrets/mongo_root_password 提供给容器,配合 MONGO_INITDB_ROOT_PASSWORD_FILE 使用,密码绝不出现于 docker-compose.yml 或环境变量检查中。

  • healthcheck :使用 mongosh 执行 ping 命令,通过读取密码文件进行认证。$$ 用于转义 Compose 变量。

  • 日志卷mongo-log:/var/log/mongodb 持久化 MongoDB 自身日志(若配置了文件日志)。

  • command:确保使用自定义配置文件。

启动:docker-compose up -d

停止:docker-compose down

4.3 .env 文件支持

创建 .env 文件:

bash 复制代码
MONGO_VERSION=7
HOST_PORT=27017

docker-compose.yml 中引用:

bash 复制代码
image: mongo:${MONGO_VERSION}
ports:
  - "${HOST_PORT}:27017"

5. 安全加固实践

  1. 绝不用明文密码 :使用 _FILE 环境变量 + Docker secrets / Kubernetes secrets。

  2. 强制认证 :配置文件 security.authorization: enabled

  3. 关闭 localhost 免密旁路setParameter.enableLocalhostAuthBypass: false

  4. 绑定内网 IP :如果 MongoDB 仅被应用容器访问,bindIp 可设为 Docker 网络内的具体 IP 或 127.0.0.1,外部通过反向代理或 SSH 隧道管理。

  5. 网络隔离 :Compose 中创建独立 backend 网络,不暴露宿主机端口,仅让应用容器通过服务名 mongo 连接。

  6. TLS/SSL :生产跨节点通信需启用 TLS,挂载证书文件并在配置中设置 net.tls.mode: requireTLS 及证书路径。

  7. 审计日志:开启审计可跟踪所有操作:

    bash 复制代码
    auditLog:
      destination: file
      format: JSON
      path: /var/log/mongodb/audit.log

    需确保该日志也持久化或收集。


6. 日常运维与问题排查

6.1 备份与恢复

逻辑备份(推荐):

bash 复制代码
docker exec mongo-prod mongodump --username root --password "$(cat ./secrets/mongo_root_password.txt)" --authenticationDatabase admin --out /backup
docker cp mongo-prod:/backup ./backup-$(date +%F)

恢复

bash 复制代码
docker cp ./backup-2025-01-01 mongo-prod:/restore
docker exec mongo-prod mongorestore --username root --password "$(cat ./secrets/mongo_root_password.txt)" --authenticationDatabase admin /restore

物理备份 :停止写入后直接打包 /data/db 卷(或用文件系统快照),但更复杂,通常逻辑备份足够。

6.2 日志查看

cpp 复制代码
docker logs -f --tail 100 mongo-prod

若配置了文件日志,可进入容器查看或通过挂载卷查看。

6.3 升级镜像

  • 小版本升级 :拉取新镜像,重建容器(docker-compose up -d --force-recreate),数据卷不变。

  • 大版本升级 (如 6.0 → 7.0):务必查阅 MongoDB 升级文档,进行功能兼容性检查和逐步升级步骤,一般需要先设置 featureCompatibilityVersion

6.4 常见问题速查

问题现象 可能原因与解决
容器反复重启 检查 docker logs,常见为挂载卷权限问题(mongod 运行用户 uid=999),执行 chown -R 999:999 /data/mongo
无法连接,Authentication failed 确认认证数据库 (authSource) 正确,root 认证数据库为 admin;应用用户为自己建的数据库
Cannot start server 配置错误 挂载的 mongod.conf 格式有误(YAML 缩进严格),或参数与版本不兼容
内存占用过高 设置 cacheSizeGB 限制 WiredTiger 缓存;同时注意 docker run -m 限制容器总内存
慢查询多 在配置中开启慢日志:operationProfiling.mode: slowOp 配合 slowOpThresholdMs
时区显示偏差 MongoDB 内部 UTC 不可变,应用层转换时区即可;/etc/localtime 仅影响系统日志

结语

MongoDB 在 Docker 中的部署起点很低,但生产落地需要兼顾认证、持久化、网络、备份和资源管控。通过 docker run 理解每个参数的意义,再迁移至 Docker Compose 进行声明式管理,最终配合密码文件、健康检查和自定义配置,我们便能在保持容器轻便的同时,获得一个稳固的数据库基座。每一个细节的打磨,都是系统可靠性的基石。