Linux 中使用 docker-compose 部署 MongoDB 6 以上版本副本集及配置 SSL / TLS 协议

一、准备环境

MongoDB 副本集部署至少 3 个节点(奇数节点),为了保障数据安全性,可考虑将 MongoDB 节点分布在不同的主机上,本示例使用一台主机部署 3 个 MongoDB示例。

1、创建 MongoDB 集群数据相关目录
# 创建 MongoDB 集群根目录及 3 个子目录
mkdir -p /mongodb-cluster/{
    
    mongo01, mongo02, mongo03}

# 创建 3 个 MongoDB 下子目录
mkdir -p /mongodb-cluster/mongo01/{
    
    certs, configdb, data, db, logs}
mkdir -p /mongodb-cluster/mongo02/{
    
    certs, configdb, data, db, logs}
mkdir -p /mongodb-cluster/mongo03/{
    
    certs, configdb, data, db, logs}

# 准备给上面 logs 目录赋予操作权限(注意:未赋予 logs 目录权限,运行会报错)
chmod 777 /mongodb-cluster/mongo01/logs
chmod 777 /mongodb-cluster/mongo01/logs
chmod 777 /mongodb-cluster/mongo01/logs
2、生成服务端、集群内部成员、客户端相关证书

由于部署 MongoDB 副本集,集群成员和客户端需要与服务端进行认证通信,保证通信的可靠性,目前 MongoDB 提供如下方式进行通信认证:

security.clusterAuthMode

类型:String

默认值:keyFile

用于集群认证的方式。如果您使用 内部 x.509 身份验证,请在此处指定。此选项可以具有以下值之一:

价值 描述
keyFile 使用密钥文件进行身份验证。只接受密钥文件。
sendKeyFile 用于滚动升级目的。发送密钥文件进行身份验证,但可以同时接受密钥文件和 x.509 证书。
sendX509 用于滚动升级目的。发送 x.509 证书进行身份验证,但可以同时接受密钥文件和 x.509 证书。
x509 受到推崇的。发送 x.509 证书进行身份验证并仅接受 x.509 证书。

通过上面说的几种通信认证方式,默认 keyFile 密钥文件方式认证,这种方式也是最简单的,只能用于测试环境下使用,通常我们为了服务端和客户端通信安全,则会使用 x509 认证方式,下面我就使用 x509 方式进行配置 TLS 协议认证。

注意:因为大多数 CA 高可靠证书,都是由第三方厂商提供,需要收费的。(这里自己通过 openssl 生成自签名 CA 证书,实现通信认证)。

自签名证书生成
(1)、生成根证书 ca.pem
# 生成 CA 私钥(ca.key)(不加密)
openssl genrsa -out ca.key 2048
# 生成 CA 证书签名请求(ca.csr)
# 每个集群成员证书和服务端证书中必须具有相同的 O、OU 和 DC (具体可参考:https://www.mongodb.com/docs/manual/core/security-x.509)
openssl req -new -key ca.key -out ca.csr # 注意:在执行这条命令后,需要按要求填写内容,最后面两项可选,密码可不填
# 生成自签名 CA 证书(ca.pem), 直接免费 100 年有效
openssl x509 -req -days 36500 -in ca.csr -signkey ca.key -out ca.pem
(2)、生成服务端证书 server.pem
# 生成 server 端私钥 (server.key) 不加密
openssl genrsa -out server.key 2048
# 生成 server 证书签名请求 (server.csr)
openssl req -new -key server.key -out server.csr # 注意:输入内容和上面的保持一致
# 使用 ca 证书签署服务端 csr 以生成服务端证书 (server.crt) 
openssl ca -days 36500 -in server.csr -out server.crt -cert ca.pem -keyfile ca.key # 注意:执行这条命令会出现 2 处错误,按如下方式解决,再执行

# 第一个错误
/etc/pki/CA/index.txt No such file or directory
# 直接执行如下命令
touch /etc/pki/CA/index.txt

# 第二个错误
/etc/pki/CA/serial No such file or directory
# 直接执行如下命令后,文件里面输入数字,如:01,再保存 
vim /etc/pki/CA/serial

# 解决完问题后,再执行上面的命令对证书进行签名(后面输入区域,都输入 y 即可)
openssl ca -days 36500 -in server.csr -out server.crt -cert ca.pem -keyfile ca.key

# 删掉 server.crt 中的 certificate 信息,保留加密证书内容即可

# 合并证书和私钥成 PEM 文件, 构建命令如下:
cat server.key server.crt > server.pem

# 再验证自签名证书是否成功(返回,OK 代表成功)
openssl verify -CAfile ca.pem server.pem
(3)、生成客户端证书 client.pem(流程和服务端一样,只是稍微改了下名)
# 生成 client 端私钥 (client.key) 不加密
openssl genrsa -out client.key 2048
# 生成 client 证书签名请求 (client.csr)
openssl req -new -key client.key -out client.csr # 注意:输入内容和上面的保持一致
# 使用 ca 证书签署客户端 csr 以生成客户端证书 (client.crt) 
openssl ca -days 36500 -in client.csr -out client.crt -cert ca.pem -keyfile ca.key 
# 注意:这条命令运行后会出现 2 个错误,是因为之前签署了服务端证书,前面自己创建的两个文件已存在,所以,需要把 /etc/pki/CA 下的 index.txt 和 serial 文件删除,再重新执行该命令,再解决前面提到的错误就可以了

# 解决完问题后,再执行上面的命令对证书进行签名(后面输入区域,都输入 y 即可)
openssl ca -days 36500 -in client.csr -out client.crt -cert ca.pem -keyfile ca.key

# 删掉 client.crt 中的 certificate 信息,保留加密证书内容即可

# 合并证书和私钥成 PEM 文件, 构建命令如下:

cat client.key client.crt > client.pem

# 再验证自签名证书是否成功(返回,OK 代表成功)

openssl verify -CAfile ca.pem client.pem
(4)、生成服务端成员证书 cluster.pem(流程和客户端一样,只是稍微改了下名),这里就不写了。
二、创建 MongoDB 集群的核心文件并配置
1、将前面生成的所有 PEM 证书 copy 到各 mongo 下 certs 目录中
# 执行如下命令
cp 文件路径 /mongodb-cluster/mongo01/certs
cp 文件路径 /mongodb-cluster/mongo02/certs
cp 文件路径 /mongodb-cluster/mongo03/certs

# 注意:确认包含如下 4 个文件
ca.pem  client.pem  cluster.pem  server.pem
2、创建各示例的 mongod.conf 核心文件并配置
# 创建 mongod.conf 配置文件
touch /mongodb-cluster/mongo01/configdb/mongod.conf
touch /mongodb-cluster/mongo02/configdb/mongod.conf
touch /mongodb-cluster/mongo03/configdb/mongod.conf

在各个示例 mongod.conf 文件里面写入如下内容:

注意:每个示例配置的端口号 27017 可不改,因为这是容器里面的端口,也可以根据需求更改

net:
  bindIp: 0.0.0.0 # 配置允许所有主机连接,默认只能本地连接
  port: 27017 # 因为在一台主机中,三个示例的端口对应 27017、27018、27019 或者 其它不冲突的端口也可以
  tls:
    CAFile: /data/certs/ca.pem # 容器内映射路径,在本配置文件下,所有配置的路径都是容器路径,作为宿主机的映射路径
    certificateKeyFile: /data/certs/server.pem
    clusterFile: /data/certs/cluster.pem
    allowInvalidCertificates: true
    allowInvalidHostnames: true
    allowConnectionsWithoutCertificates: true
    mode: requireTLS # MongoDB 认证模型,使用 TLS, 默认不使用 TLS
processManagement:
  fork: false  # 是否开启以守护进程模式在后台运行
replication:
  replSetName: rs0 # 副本集名称
security:
  clusterAuthMode: x509  # 集群安全认证模型使用 x509
  authorization: enabled # 启用基于 RBAC 权限访问操作
storage:
  engine: wiredTiger # 6.0 以上版本默认使用 wiredTiger 引擎, 从 4.2 版本移除了 MMAPv1 存储引擎
  # 具体参数配置可参考:https://www.mongodb.com/docs/manual/reference/configuration-options/#storage-options
  wiredTiger:
      engineConfig:
         cacheSizeGB: 0.5
         journalCompressor: zstd # 默认使用 snappy 方式压缩,这里使用 zstd 高效压缩方式 
         zstdCompressionLevel: 6 # 设置 zstd 方式压缩级别,默认为 6, 范围:1-22, 级别越高,压缩率也越高
      collectionConfig:
         blockCompressor: zstd
      indexConfig:
         prefixCompression: true # 开启索引前缀压缩
systemLog:
  destination: file  # 日志存储方式为 file, 默认为 system_log 输出方式
  logAppend: true # 是否开启日志追加,默认为 false
  path: /data/logs/mongod.log  # 日志存储路径
3、创建 docker-compose.yaml 部署文件
# 创建 docker-compose.yaml 文件
touch /mongodb-cluster/docker-compose.yaml

在 docker-compose.yaml 文件里面写入如下内容:

version: "3.9"
services:
  mongo01:
    container_name: mongo01 # 容器名
    image: mongo # 拉取的镜像,未指定版本,默认 latest 最新版
    ports:
      - 27017:27017 # 容器内端口号映射,如:宿主机端口:容器端口
    environment:
      TZ: Asia/Shanghai # 配置时区信息
    volumes:
      - /etc/localtime:/etc/localtime
      - ./mongo01/db:/data/db  # 挂载数据 db 目录, 默认容器内是 /data/db, 因为前面没配置 storage.dbPath 路径
      - ./mongo01/configdb:/data/configdb # 挂载前面的 mongod.conf 配置文件, 默认容器内 /data/configdb
      - ./mongo01/certs:/data/certs # 这个是自定义挂载的 TLS 相关配置
      - ./mongo01/logs:/data/logs # 挂载容器内日志目录
    restart: always
    command: mongod --replSet rs0 -f /data/configdb/mongod.conf # 容器启动后,需要执行的命令
    networks:
      - mongo_network # 使用自定义网络
  mongo02:
    container_name: mongo02
    image: mongo
    ports:
      - 27018:27018
    environment:
      TZ: Asia/Shanghai
    volumes:
      - /etc/localtime:/etc/localtime
      - ./mongo02/db:/data/db
      - ./mongo02/configdb:/data/configdb
      - ./mongo02/certs:/data/certs
      - ./mongo02/logs:/data/logs
    restart: always
    command: mongod --replSet rs0 -f /data/configdb/mongod.conf
    networks:
      - mongo_network
  mongo03:
    container_name: mongo03
    image: mongo
    ports:
      - 27019:27019
    environment:
      TZ: Asia/Shanghai
    volumes:
      - /etc/localtime:/etc/localtime
      - ./mongo03/db:/data/db
      - ./mongo03/configdb:/data/configdb
      - ./mongo03/certs:/data/certs
      - ./mongo03/logs:/data/logs
    restart: always
    command: mongod --replSet rs0 -f /data/configdb/mongod.conf
    networks:
      - mongo_network 
# 创建容器网络
networks:
  mongo_network:
    name: mongo_network
三、启动 MongoDB 集群并初始化及配置客户端认证
1、启动 MongoDB 集群
# 使用如下命令启动
docker-compose up -d

# 查看容器日志(未输出错误信息或没有日志信息则成功)
docker logs -f mongo01 -n 100

# 需要查看具体容器输出日志,可在前面配置的日志目录中查看
2、进入其中一个容器,进行集群初始化(在 3 个示例中随便进入哪个都行,但只要对一个容器内做初始化即可)
# 进入容器
docker exec -it mongo01 /bin/bash

# 进入 mongo 服务端, 需要指定客户端签名证书(注意:MongoDB 6.0 以上版本, 客户端命令为 mongosh)
# --host 是你前面生成签名证书时填的 Common Name 的值, 简称:CN, 可根据需求填写, 配置写错了, 会导致连接有问题
# 具体可参考:https://www.mongodb.com/docs/manual/core/security-x.509
mongosh --tls --tlsCertificateKeyFile /data/certs/client.pem --tlsCAFile /data/certs/ca.pem --host localhost --port 27017

# 进入 mongo 服务端后,需要先创建一个用户并赋予权限,不然,操作会出错
# 执行如下命令, 创建用户(注意:在执行命令之前,需要先 use admin 切换到 admin 库)
# 具体可参考:https://www.mongodb.com/docs/manual/reference/built-in-roles
db.createUser(
	{
    
    
		user:"root", # 用户名
		pwd:"123456", # 密码
		roles:[{
    
    role:"root",db:"admin"}] # 可配置多个角色, root 角色表示拥有超级用户角色, 作用于 admin 数据库
	}
);

# 使用刚创建的 root 用户登录认证, 进行执行接下来的操作
# root 用户登录认证(注意:需要在 admin 库执行, use admin 切换命令)
db.auth("root", "123456")
# 执行如下命令,初始化 mongo 副本集, 返回 ok 代表已初始化成功
rs.initiate( {
    
    
   _id : "rs0", # 副本集名称
   members: [
      {
    
     _id: 0, host: "172.21.0.3:27017" }, # mongo 实例 IP, 配置成宿主机 IP 即可
      {
    
     _id: 1, host: "172.21.0.4:27018" },
      {
    
     _id: 2, host: "172.21.0.2:27019" }
   ]
})

# 再执行 rs.status() 命令查看集群状态, 也可通过 rs.conf() 命令查看集群节点配置信息
3、使用 x509 证书对客户端进行身份验证

将 x.509 证书添加 subject 为用户要使用客户端证书进行身份验证,您必须首先将来自客户端证书的值 subject 作为 MongoDB 用户添加到 $external 数据库中。每个唯一的 x.509 客户端证书对应一个 MongoDB 用户。您不能使用单个客户端证书对多个 MongoDB 用户进行身份验证。具体可参考:https://www.mongodb.com/docs/manual/tutorial/configure-x509-client-authentication

# 在宿主机上执行如下命令(注意:client.pem 是前面生成的客户端签名证书)
# 使用以下命令从客户端证书中检索 RFC2253 格式化:subject
openssl x509 -in client.pem -inform PEM -subject -nameopt RFC2253

# 该命令会返回 subject 字符串和证书:
subject= CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry
-----BEGIN CERTIFICATE-----
# ...
-----END CERTIFICATE-----

# 添加作为用户 RFC2253 的合规值, subject 根据需要省略空格。
# 首先, 需要进入主节点容器内 mongo 服务端, 执行如下操作:
# 进入容器
docker exec -it mongo01 /bin/bash
# 进入 mongo 服务端
mongosh --tls --tlsCertificateKeyFile /data/certs/client.pem --tlsCAFile /data/certs/ca.pem --host localhost --port 27017
# 需要切换为 admin 库, use admin 命令, 再执行 root 用户认证命令
db.auth("root", "123456")

# 以下添加一个用户并授予该用户 readWrite 在数据库中的角色 test 和 root 角色:
db.getSiblingDB("$external").runCommand(
  {
    
    
    createUser: "[email protected],CN=localhost,OU=BND,O=BN,ST=JS,C=CN",
    roles: [
         {
    
     role: "readWrite", db: "test" },
         {
    
     role: "root", db: "admin" }
    ],
    writeConcern: {
    
     w: "majority" , wtimeout: 5000 }
  }
)

# 客户端连接后认证, 使用如下命令:
db.getSiblingDB("$external").auth(
  {
    
    
    mechanism: "MONGODB-X509"
  }
)

# 客户端连接期间进行身份认证, 执行如下命令:
mongosh --tls --tlsCertificateKeyFile client.pem \
    --tlsCAFile ca.pem \
    --authenticationDatabase '$external' \
    --authenticationMechanism MONGODB-X509
四、测试 MongoDB 副本集数据同步
1、在主容器里面写入数据,再进入从容器里面查询
# 进入主容器
docker exec -it mongo01 /bin/bash

# 进入 mongo 服务端
mongosh --tls --tlsCertificateKeyFile /data/certs/client.pem --tlsCAFile /data/certs/ca.pem --host localhost --port 27017

# 切换 admin 库, use admin 命令
use admin
# db 认证
db.auth("root", "123456")
# 插入一条数据在 test 库中, 文档不存在会自动创建
db.test.insertOne( {
    
     x: 1 } )
# 查询数据
db.test.find()

# 然后, 下面再从容器里面查看是否有数据
docker exec -it mongo02 /bin/bash # 进入从容器
mongosh --tls --tlsCertificateKeyFile /data/certs/client.pem --tlsCAFile /data/certs/ca.pem --host localhost --port 27018 # 进入 mongo 服务端

use admin # 切换 admin
db.auth("root", "123456") # db 认证
db.test.find() # 查询数据, 这里会出错, 默认是强制读主的, 不允许从节点进行读操作, 只能作为备份和高可用, 需要显示开启读操作

# 解决方案, 如下:
rs.secondaryOk() or db.getMongo().setReadPref("primaryPreferred")
# 这两条命令执行其中一条即可, 再次执行 db.test.find() 查询数据就能读到了
# 注意:rs.secondaryOk() 命令在新版本被弃用了, 建议使用:db.getMongo().setReadPref("primaryPreferred") 命令进行操作
五、使用 Navicat 客户端连接 MongoDB 副本集
1、将服务器中的客户端签名证书和 CA 证书导出

包含:ca.pem 和 client.pem 两个文件

2、进行使用 Navicat 连接,如下图:

在这里插入图片描述

继续配置启用 SSL 模式进行连接,如下图:

在这里插入图片描述

然后,点击测试连接,即可连接成功。

六、其它
1、添加副本,在登录到主节点下输入
rs.add("ip:port")
2、删除副本
rs.remove("ip:port")
3、新增仲裁节点
rs.addArb("ip:port")
4、修改副本集里面的节点 IP
# 声明 cfg 变量
cfg = rs.conf()

# 执行修改各节点的 IP
cfg.members[0].host = "192.xxx.xxx.121:27017"
cfg.members[1].host = "192.xxx.xxx.121:27018"
cfg.members[2].host = "192.xxx.xxx.121:27019"

# 重新加载配置
rs.reconfig(cfg)

# 查询副本集状态
rs.status()

# 查询副本集配置
rs.conf()
七、SpringBoot 整合 MongoDB 副本集

续下一篇文章SpringBoot 整合 MongoDB 6 以上版本副本集及配置 SSL / TLS 协议

猜你喜欢

转载自blog.csdn.net/weixin_43322048/article/details/129190278