我有一个docker-composer.yml,它正在设置两个服务:server
和db
。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:
编辑:上面的代码更改为删除links
和expose
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-alpine
和pg: 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 -y
postgresql客户端安装postgresql-client
。
命令psql -h db -p 15432 -U postgres -W
成功!
检查pg
配置
您说db
0使用环境变量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: