Skip to content

Express+Mongodb 动态注入 mongodb 密码实践记录

初衷

简单介绍下如何在 express 项目下配合 docker-compose 动态注入 mongodb 账号密码,项目目录在下方,但不涉及具体项目如何运行,只介绍如何使用。

项目目录

📦backend
 ┣ 📂db
 ┃ ┗ 📜index.ts
 ┣ 📂interface
 ┣ 📂middleware
 ┣ 📂models
 ┣ 📂routes
 ┣ 📂utils
 ┣ 📜.dockerignore
 ┣ 📜.eslintrc.js
 ┣ 📜.gitignore
 ┣ 📜Dockerfile 
 ┣ 📜README.md
 ┣ 📜app.ts
 ┣ 📜build.sh // 注入 mongodb 密码和单个库密码
 ┣ 📜dev.env // 开发环境变量
 ┣ 📜docker-compose.yml // 生成项目和 mongodb 镜像
 ┣ 📜mongo-init.sh // 初始化账号密码
 ┣ 📜package-lock.json 
 ┣ 📜package.json
 ┣ 📜prod.env // 生产环境变量
 ┗ 📜tsconfig.json

环境文件

dev.env

ini
NODE_ENV=dev
PORT=3981

# MONGODB_URL=mongodb://localhost:27017/auto-answer
MONGODB_URL=mongodb://mongodb:27017/auto-answer

MONGO_INITDB_ROOT_USERNAME=root
MONGO_INITDB_ROOT_PASSWORD=123456
MONGO_USERNAME=admin
MONGO_PASSWORD=test123456
MONGO_HOST=localhost
MONGO_PORT=27017
MONGO_INITDB_DATABASE=auto-answer

prod.env

ini
NODE_ENV=production
PORT=3981

# MONGODB_URL=mongodb://localhost:27017/auto-answer
MONGODB_URL=mongodb://mongodb:27017/auto-answer

MONGO_INITDB_ROOT_USERNAME=root
MONGO_INITDB_ROOT_PASSWORD=123456
MONGO_USERNAME=admin
MONGO_PASSWORD=test123456
MONGO_HOST=mongodb
MONGO_PORT=27017
MONGO_INITDB_DATABASE=auto-answer

构建运行 compose 脚本 build.sh

sh
#!/bin/bash

if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]
then
  echo "Need input with build env('prod' or 'dev') or MONGO_INITDB_ROOT_PASSWORD or MONGO_PASSWORD!"
  exit 1
else 
  echo $1
  echo $2
  echo $3
  env MACHINE_ENV=$1 MONGO_INITDB_ROOT_PASSWORD=$2 MONGO_PASSWORD=$3 docker-compose -p auto-answer-backend-hub up -d 
fi

# 通过 sh build.sh 传参数

构建 compose 只需要运行下方命令即可(这里需要注意命令需要在 dockerfilecompose 构建完成之后运行)

sh
sh build.sh MACHINE_ENV MONGO_INITDB_ROOT_PASSWORD MONGO_PASSWORD

将上述 MONGO_INITDB_ROOT_PASSWORDMONGO_PASSWORD 替换成你所有需要的密码

MACHINE_ENV 为上面项目目录中的 devprod,可根据需求更改。

构建 Dockerfile

docker
FROM node:16.0.0
LABEL maintainer="webB1anyaoyao@gmail.com"
COPY . /app
WORKDIR /app
RUN npm install
RUN ls
RUN npm run build
EXPOSE 3981
CMD [ "npm", "start"]

构建 docker-compose.yml

yaml
version: "3"
services:
  app:
    container_name: auto-answer-backend-hub
    restart: on-failure
    env_file:
      - ${MACHINE_ENV}.env
    environment:
      - NODE_ENV=${MACHINE_ENV}
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
      - MONGO_PASSWORD=${MONGO_PASSWORD}
    build: ./
    ports:
      - "3981:3981"
    # volumes:
    #   - .:/app
    depends_on:
      - mongodb
  mongodb:
    container_name: auto-answer-mongo-hub
    image: mongo:4.0.8
    env_file:
      - ${MACHINE_ENV}.env
    environment:
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
      - MONGO_PASSWORD=${MONGO_PASSWORD}
    volumes: 
      - ~/data:/data/db
      - ./mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro
    ports:
      - "27017:27017"
  • env_file:为环境变量文件,${MACHINE_ENV}.envMACHINE_EN 为运行 build.sh 注入的变量
  • environment: 环境变量如果和 env_file 相同,将会覆盖 env_file 中变量,上述 docker-compose.yml 中通过运行 build.sh 注入的变量覆盖了 MONGO_INITDB_ROOT_PASSWORDMONGO_PASSWORD 可以让数据库密码变成动态的

mongo-init.sh 生成 mongodb 脚本

sh
mongo -- "$MONGO_INITDB_DATABASE" <<EOF
  db.createUser({
    user: "$MONGO_USERNAME",
    pwd: "$MONGO_PASSWORD",
    roles: [
      { role: "readWrite", db: "$MONGO_INITDB_DATABASE" }
    ]
  })
EOF

数据连接

文件位置./db/index.ts

typescript
import mongoose from 'mongoose'

const {
  MONGO_USERNAME = 'admin',
  MONGO_PASSWORD = 'test123456',
  MONGO_HOST = 'localhost',
  MONGO_PORT = '27017',
  MONGO_INITDB_DATABASE = 'auto-answer'
} = process.env

let uri = ''
// 通过环境变量判断,切换连接 mongodb 的 uri
if (process.env.NODE_ENV === 'dev' || !process.env.NODE_ENV) {
  uri = `mongodb://${MONGO_HOST}:${MONGO_PORT}/${MONGO_INITDB_DATABASE}`
} else {
  uri = `mongodb://${MONGO_USERNAME}:${encodeURIComponent(MONGO_PASSWORD)}@${MONGO_HOST}:${MONGO_PORT}/${MONGO_INITDB_DATABASE}`
}

console.log(uri)

// mongodb://admin:test123456@localhost:27017/auto-answer
mongoose.connect(uri, {
  // authSource: 'admin'
}, () => {
  console.log('connect!')
})

const db = mongoose.connection

db.once('open', () => {
  console.log('db is connect!')
})

db.on('error', error => {
  console.log(error)
})

db.on('close', () => {
  console.log('db is closed!')
})

export default mongoose

验证

完成上面的步骤基本上就能通过 compose 运行后端的项目了,在项目目录通过命令:

sh
sh build.sh MACHINE_ENV MONGO_INITDB_ROOT_PASSWORD MONGO_PASSWORD

就会运行 compose 生成两个容器:

  • auto-answer-backend-hub:后端服务
  • auto-answer-mongo-hub:数据库服务

通过 docker 命令进入到 mongodb 容器:

sh
docker exec -it auto-answer-mongo-hub /bin/base

进入后,输入 mongo 进入 mongo

sh
mongo

这时候输入 show dbs,因为我们开启了数据库权限是看不到数据的

验证 root 用户

  • 进入 admin
sh
> use admin
switched to db admin
  • 使用 db.auth
sh
db.auth('root', MONGO_INITDB_ROOT_PASSWORD)

上面的 root 为环境文件中的 MONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORD 为运行 sh 脚本是注入的密码(这里需要更换成自己的密码)。

如果密码账号和密码正确的话会出现以下提示:

sh
> db.auth('root', MONGO_INITDB_ROOT_PASSWORD)
1

这时可以查看所有数据库

sh
> show dbs
admin        0.000GB
auto-answer  0.000GB
config       0.000GB
local        0.000GB

验证工作库

我们在mongo-init.sh中通过:

sh
db.createUser({
    user: "$MONGO_USERNAME",
    pwd: "$MONGO_PASSWORD",
    roles: [
      { role: "readWrite", db: "$MONGO_INITDB_DATABASE" }
    ]
 })

生成了MONGO_INITDB_DATABASE,而这里的MONGO_INITDB_DATABASE就是我们环境变量中设置的auto-answer

然后我们退出 mongo 后重新进入 auto-answer

sh
> exit
bye
root@a25acd1cd787:/# mongo
MongoDB shell version v4.0.8
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("1179501c-6e50-433b-90dc-db828be7c467") }
MongoDB server version: 4.0.8
> show dbs
> use auto-answer
switched to db auto-answer
> show collections
Warning: unable to run listCollections, attempting to approximate collection names by parsing connectionStatus

这时可以发现 auto-answer 下没有任何表,下一步进行 db.auth

sh
> db.auth(MONGO_USERNAME, MONGO_PASSWORD)
1
> show dbs
auto-answer  0.000GB
> show collections
airs
cookers

上面的 MONGO_USERNAME 是环境变量中的用户名(需替换成自己的),MONGO_PASSWORD 是通过 sh 注入的生成用户的密码。

至此完成所有操作。