为什么我的 dockerized nodejs 服务器无法连接到我的 dockerized mongodb?

时间:2021-01-07 01:17:08

标签: node.js mongodb docker docker-compose

我有一个运行正常的 mongodb docker 容器。如果我公开端口 27017,我可以使用 localhost 从外部 docker 查询它。我可以使用 mongo 容器名称从同一个 docker 网络上的 python docker 容器查询它。

我在 docker 容器中也有一个 nodejs 服务器。它与 mongodb 容器在同一个 docker 网络上。如果我创建一个简单的测试脚本并将其放置在 nodejs 容器中,我就可以在 nodejs 容器中运行它并使用 mongo 容器名称成功查询 mongo 容器。

在单独的服务器上,我检查项目(与发生问题的代码相同)并运行“docker-compose up”,nodejs 容器能够查询 mongo 容器。

但是,在本地运行项目时,nodejs服务端代码使用容器名连接mongo失败。我经常收到错误“连接到数据库失败,MongoNetworkError:第一次连接时无法连接到服务器 [sciencedog_db:27017] [MongoNetworkError:连接超时]”。

谁能给我任何关于要寻找什么的想法?错误似乎很清楚,但测试脚本清楚地表明容器之间实际上存在连接。有什么方法可以配置节点服务器,使网络正常工作时与 mongo 容器的连接失败?是不是我遗漏了一些环境因素?

直接与节点一起运行时工作的测试脚本:

const { MongoClient } = require("mongodb");

// Replace the uri string with your MongoDB deployment's connection string.
const uri =
//   "mongodb+srv://<user>:<password>@<cluster-url>?retryWrites=true&w=majority";
    "mongodb://sciencedog_db:27017";

const client = new MongoClient(uri);

async function run() {
  try {
    await client.connect();

    const database = client.db('sciencedog');
    const collection = database.collection('users');

    // // Query for a movie that has the title 'Back to the Future'
    const query = { username: 'daniel' };
    const user = await collection.findOne(query);

    console.log(user);
  } finally {
    // Ensures that the client will close when you finish/error
    await client.close();
  }
}
run().catch(console.dir);

nodejs 服务器中的代码出现故障(较大模块的一部分),应用程序使用 gulp 运行:

const MongoClient = require('mongodb').MongoClient

const host = process.env.MODE === 'development' ? 'localhost' : 'sciencedog_db'
const dbUrl = `mongodb://${host}:27017`
const dbName = 'sciencedog'

var db_client
function getConnection() {
    /**
     * Get a connection to the database.
     * @return {MongoClient} Connection to the database
     */
    return new Promise(function (resolve, reject) {
        if (typeof db_client == 'undefined') {
            console.log("There is no db_client");
            MongoClient.connect(dbUrl).then(
                function (client) {
                    console.log("So we got one");
                    db_client = client
                    console.log("Connected successfully to server");
                    resolve(db_client)
                },
                function (err) {
                    let err_msg = 'Connection to database failed, ' + err
                    console.log(err_msg);
                    reject(err_msg)
                }
            )
        } else {
            console.log('Found existing database connection');
            resolve(db_client)
        }
    })
}

我的 docker-compose 文件:

version: '3.7'
services:
    sciencedog_python:
        build: .
        container_name: sciencedog_python
        init: true
        stop_signal: SIGINT
        environment:
            - 'PYTHONUNBUFFERED=TRUE'
        networks:
            - sciencedog
        ports:
            - 8080:8080
            - 8443:8443
        volumes:
            - type: bind
              source: /etc/sciencedog/.env
              target: /etc/sciencedog/.env
              read_only: true
            - type: bind
              source: .
              target: /opt/python_sciencedog/

    sciencedog_node:
        build: ../sciencedog/.
        container_name: sciencedog_node
        ports:
            - 80:8001
        networks:
            - sciencedog
        volumes:
            - type: bind
              source: /etc/sciencedog/.env
              target: /etc/sciencedog/.env
              read_only: true
            - type: bind
              source: ../sciencedog/src/.
              target: /opt/sciencedog_node/src/

    sciencedog_db:
        image: mongo:4.0.4
        container_name: sciencedog_db
        volumes:
            - sciencedog:/data/db
        networks:
            - sciencedog

volumes:
    sciencedog:
        driver: local

networks:
    sciencedog:
        driver: bridge

docker-compose dev 扩展(启用来自主机的连接,容器不需要通过 docker 网络进行通信):

version: '3.7'
services:
    sciencedog_python:
        ports:
            - 6900:6900
        stdin_open: true
        tty: true

    sciencedog_db:
        ports:
            - 27017:27017

1 个答案:

答案 0 :(得分:0)

由于相同的代码能够在另一台机器上连接,我猜问题是初始化顺序 - 在开发机器上运行时,nodejs 容器首先启动并尝试过早连接到 Mongo。

解决这个问题的快速方法是声明 nodejs 容器和 Mongo 容器之间的依赖关系:

sciencedog_node:
    networks:
        # etc etc etc
    depends_on:
        - sciencedog_db
    

请注意,即使在声明此类依赖项时,也不能保证 Mongo 将 100% 准备好接收连接(请参阅 https://docs.docker.com/compose/startup-order/),因此我认为您会受益于为 MongoClient 配置慷慨的超时:

// example taken from https://stackoverflow.com/questions/39979924/how-to-set-mongoclient-connection-timeout
MongoClient.connect(dbUrl, {
    server: {
        socketOptions: {
            connectTimeoutMS: 20000
        }
    }
}).then(.....)