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



我有一个docker-composer.yml,它正在设置两个服务:serverdb。Node.js服务器是server服务,使用pg连接到PostgreSQL数据库;并且CCD_ 5服务是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业务输出:

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

注意:由于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服务访问PostgreSQL数据库,使用server服务的相同脚本成功连接

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

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

此输出表示连接成功。


总结如下:

我无法从server容器访问db容器,连接超时,但我可以从主机访问db容器,连接成功。


注意事项

首先,感谢到目前为止的回答。解决提出的一些问题:

  • 缺少网络:

    它不是必需的,因为docker compose有一个默认的网络。A使用自定义网络进行了测试,但也不起作用。

  • 初始化顺序:

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

  • 更新:

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

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

    使用的命令行:

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

编辑:通过删除Sequelize的依赖项改进了代码。现在它只使用pg并直接调用脚本

感谢您提供源代码来重现此问题。docker-compose文件中没有您已经排除的问题。

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

您正在使用node:14-alpinepg: 7.18.2。事实证明,节点14和node-pg的早期版本上存在一个错误。

解决方案是降级到节点v12使用当前为8.4.2的node-pg的最新版本(在v8.0.3上进行了修复)

我已经在您提供的分支上验证了这两种解决方案,它们都有效。

这不是一个完整的答案;我手头没有你的代码,所以我实际上无法测试compose文件。然而,我想指出一些问题:

  • links指令已弃用。

    links是Docker引入用户定义网络和自动DNS支持之前使用的遗留选项。你可以去掉它。组合文件中的容器可以通过名称相互引用,而不需要名称。

  • expose指令没有任何作用。它可以在例如CCD_;该图像将暴露该端口上的服务";,但它实际上并没有让任何事情发生。它在撰写文件中几乎完全没有用。

  • depends_on指令也没有你想象的那么有用。它确实会导致docker compose首先打开数据库容器,但它被认为是"容器";向上";一旦第一个过程已经开始。它不会导致docker compose等待数据库准备好为请求提供服务,这意味着如果应用程序在数据库准备好之前尝试连接,您仍然会遇到错误。

    最好的解决方案是在应用程序中构建数据库重新连接逻辑,这样,如果数据库出现故障(例如,重新启动postgres容器以激活新配置或升级postgres版本),应用程序将重试连接,直到成功为止。

    一个可接受的解决方案是在应用程序启动中包含一些代码,这些代码会一直阻塞到数据库对请求做出响应。

问题与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以进入容器应用程序,然后使用apt install -ypostgresql客户端安装postgresql-client

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

检查pg配置

您说db0使用环境变量DATABASE_URL来达到postgresql。我不确定:

发件人https://node-postgres.com/features/connecting,我们可以找到这个例子:

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

这句话:

nodepostgres使用与libpq相同的环境变量来连接PostgreSQL服务器。

libpq文档中,没有DATABASE_URL

要将pg文档中提供的示例与您的docker-compose.yml文件进行适配,请尝试使用以下文件(我只更改了应用程序服务的环境变量):

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:

最新更新