无法从应用容器访问Postgres容器

时间:2020-11-05 20:34:37

标签: node.js postgresql docker docker-compose

我有一个docker-composer.yml,它正在设置两个服务:serverdb。 Node.js服务器是server服务,它使用pg连接到PostgreSQL数据库。而db服务是PostgreSQL映像。

在服务器启动时,它尝试连接到数据库,但是超时。


docker-compose.yml

version: '3.8'

services:
  server:
    image: myapi
    build: .
    container_name: server
    env_file: .env
    environment:
      - PORT=80
      - DATABASE_URL=postgres://postgres:postgres@db:15432/mydb
      - REDIS_URL=redis://redis
    ports:
      - 3000:80
    depends_on:
      - db
    command: node script.js
    restart: unless-stopped

  db:
    image: postgres
    container_name: db
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: mydb
    ports:
      - 15432:15432
    volumes:
      - db-data:/var/lib/postgresql/data
    command: -p 15432
    restart: unless-stopped

volumes:
  db-data:

编辑:上面的代码已更改为删除linksexpose


db服务输出:

db        | 
db        | PostgreSQL Database directory appears to contain a database; Skipping initialization
db        | 
db        | 2020-11-05 20:18:15.865 UTC [1] LOG:  starting PostgreSQL 13.0 (Debian 13.0-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
db        | 2020-11-05 20:18:15.865 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 15432
db        | 2020-11-05 20:18:15.865 UTC [1] LOG:  listening on IPv6 address "::", port 15432
db        | 2020-11-05 20:18:15.873 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.15432"
db        | 2020-11-05 20:18:15.880 UTC [25] LOG:  database system was shut down at 2020-11-05 20:18:12 UTC
db        | 2020-11-05 20:18:15.884 UTC [1] LOG:  database system is ready to accept connections

script.js-由server服务中的命令使用。

const pg = require('pg');

console.log(process.env.DATABASE_URL);

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  connectionTimeoutMillis: 5000,
});
pool.connect((err, _, done) => {
  if (err) {
    console.error(err);
    done(err);
  }
  done();
});
pool.query('SELECT NOW()', (err, res) => {
  console.log(err, res);
  pool.end();
});

const client = new pg.Client({
  connectionString: process.env.DATABASE_URL,
  connectionTimeoutMillis: 5000,
});
client.connect(console.error);
client.query('SELECT NOW()', (err, res) => {
  console.log(err, res);
  client.end();
});

server服务输出:

注意:第一行是console.log的第一个script.js调用的输出。

注意:由于server服务是用restart: unless-stopped设置的,它将永远重复此输出。

server    | postgres://postgres:postgres@db:15432/mydb
server    | Error: Connection terminated due to connection timeout
server    |     at Connection.<anonymous> (/home/node/app/node_modules/pg/lib/client.js:255:9)
server    |     at Object.onceWrapper (events.js:421:28)
server    |     at Connection.emit (events.js:315:20)
server    |     at Socket.<anonymous> (/home/node/app/node_modules/pg/lib/connection.js:78:10)
server    |     at Socket.emit (events.js:315:20)
server    |     at emitCloseNT (net.js:1659:8)
server    |     at processTicksAndRejections (internal/process/task_queues.js:79:21)
server    |     at runNextTicks (internal/process/task_queues.js:62:3)
server    |     at listOnTimeout (internal/timers.js:523:9)
server    |     at processTimers (internal/timers.js:497:7)
server    | Error: Connection terminated due to connection timeout
server    |     at Connection.<anonymous> (/home/node/app/node_modules/pg/lib/client.js:255:9)
server    |     at Object.onceWrapper (events.js:421:28)
server    |     at Connection.emit (events.js:315:20)
server    |     at Socket.<anonymous> (/home/node/app/node_modules/pg/lib/connection.js:78:10)
server    |     at Socket.emit (events.js:315:20)
server    |     at emitCloseNT (net.js:1659:8)
server    |     at processTicksAndRejections (internal/process/task_queues.js:79:21)
server    |     at runNextTicks (internal/process/task_queues.js:62:3)
server    |     at listOnTimeout (internal/timers.js:523:9)
server    |     at processTimers (internal/timers.js:497:7) undefined
server    | Error: timeout expired
server    |     at Timeout._onTimeout (/home/node/app/node_modules/pg/lib/client.js:95:26)
server    |     at listOnTimeout (internal/timers.js:554:17)
server    |     at processTimers (internal/timers.js:497:7)
server    | Error: Connection terminated unexpectedly
server    |     at Connection.<anonymous> (/home/node/app/node_modules/pg/lib/client.js:255:9)
server    |     at Object.onceWrapper (events.js:421:28)
server    |     at Connection.emit (events.js:315:20)
server    |     at Socket.<anonymous> (/home/node/app/node_modules/pg/lib/connection.js:78:10)
server    |     at Socket.emit (events.js:315:20)
server    |     at emitCloseNT (net.js:1659:8)
server    |     at processTicksAndRejections (internal/process/task_queues.js:79:21) undefined
server    | postgres://postgres:postgres@db:15432/mydb
...

我可以使用db服务中的相同脚本从主机上的server服务访问PostgreSQL数据库,成功连接

从主机运行的脚本的输出:

➜  node script.js
postgres://postgres:postgres@localhost:15432/mydb
null Client { ... }
undefined Result { ... }
null Result { ... }

此输出表示连接成功。


总结:

我无法从db容器到达server容器,但连接超时,但是我可以从主机到达db容器,并成功连接。 / p>


注意事项

首先,感谢您到目前为止的回答。解决一些要点:

  • 缺少网络:

    这不是必需的,因为docker-compose具有默认网络。使用自定义网络进行了测试,但也无法正常工作。

  • 初始化顺序:

    我正在使用depends_on来确保首先启动db容器,但是我知道这并不能确保首先对数据库进行初始化,然后对服务器进行初始化。这不是问题所在,因为服务器在发生超时时中断并且由于restart: unless-stopped设置而再次运行。因此,如果数据库仍在第一次或第二次尝试启动服务器时进行初始化,则没有问题,因为服务器将继续重新启动,直到连接成功为止(从未发生过)。

  • 更新:

    server容器中,我可以使用db通过psql服务访问数据库。我仍然无法从那里的Node.js应用程序连接。

    DATABASE_URL并不是问题,因为我在psql命令中使用的URI与script.js使用并由第一个console.log调用打印的URI在那里。

    使用的命令行:

    docker exec -it server psql postgres://postgres:postgres@db:15432/mydb
    

编辑:通过消除对Sequelize的依赖性,改进了代码。现在,它仅使用pg并直接调用脚本。

3 个答案:

答案 0 :(得分:1)

感谢您提供源来重现此问题。 您已经排除了docker-compose文件中的任何问题。

问题出在您的Dockerfile和您使用的node-pg的版本之间。

您正在使用node:14-alpinepg: 7.18.2。 原来在节点14和早期版本的node-pg上存在bug

解决方案可以降级为节点v12 ,或者使用最新的node-pg版本,该版本当前为8.4.2(已在v8.0.3中修复)。

我已经在您提供的分支机构上验证了这两种解决方案,它们都可以工作。

答案 1 :(得分:1)

这不是一个完整的答案;我没有您的代码,因此我实际上无法测试撰写文件。但是,我想指出一些问题:

  • 不推荐使用links指令。

    links是一个旧选项,在Docker引入用户定义的网络和自动DNS支持之前使用。您可以摆脱它。组成文件中的容器可以按名称相互引用而无需引用。

  • expose指令不执行任何操作。举例来说,Dockerfile可以提供很多信息,例如“此映像将在此端口上公开服务”,但实际上并没有使任何事情发生。在撰写文件中几乎完全没有用。

  • depends_on指令也没有您想象的有用。实际上,这将导致docker-compose首先启动数据库容器,但是一旦第一个进程开始,该容器就会被视为“启动”。不会导致docker-compose等待数据库准备好处理请求,这意味着如果您的应用程序在数据库准备就绪之前尝试连接,您仍然会遇到错误。

    对此的最佳解决方案是在应用程序中内置数据库重新连接逻辑,以便如果数据库出现故障(例如,您重新启动postgres容器以激活新配置或升级postgres版本),则该应用将重试连接,直到成功。

    一种可接受的解决方案是在应用程序启动时包含代码,直到数据库响应请求为止,该代码会阻塞。

答案 2 :(得分:1)

该问题与docker没有关系。要对此进行测试,请执行以下操作:

使用此 docker-compose.yml 文件:

version: '3.8'

services:
  app:
    image: ubuntu
    container_name: app
    command: sleep 8h

  db:
    image: postgres
    container_name: db
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: mydb
    expose:
      - '15432'
    ports:
      - 15432:15432
    volumes:
      - db-data:/var/lib/postgresql/data
    command: -p 15432
    restart: unless-stopped

volumes:
  db-data:

执行一个docker exec -it app bash进入容器应用程序,然后使用postgresql-client postgresql-client`安装apt install -y

命令psql -h db -p 15432 -U postgres -W成功!

检查pg的配置

您说pg使用环境变量DATABASE_URL到达PostgreSQL。我不确定:

https://node-postgres.com/features/connecting,我们可以找到以下示例:

$ PGUSER=dbuser \
  PGHOST=database.server.com \
  PGPASSWORD=secretpassword \
  PGDATABASE=mydb \
  PGPORT=3211 \
  node script.js

这句话:

node-postgres使用与libpq相同的环境变量来连接到PostgreSQL服务器。

libpq文档中,没有DATABASE_URL

要使用docker-compose.yml文件适应pg文档中提供的示例,请尝试以下文件(我仅更改了应用服务的环境变量):

version: '3.8'

services:
  server:
    image: myapi
    build: .
    container_name: server
    env_file: .env
    environment:
      - PORT=80
      - PGUSER=postgres
      - PGPASSWORD=postgres
      - PGHOST=db
      - PGDATABASE=mydb
      - PGPORT=15432
      - REDIS_URL=redis://redis
    ports:
      - 3000:80
    depends_on:
      - db
    command: node script.js
    restart: unless-stopped

  db:
    image: postgres
    container_name: db
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: mydb
    ports:
      - 15432:15432
    volumes:
      - db-data:/var/lib/postgresql/data
    command: -p 15432
    restart: unless-stopped

volumes:
  db-data: