我有一个docker-composer.yml,它正在设置两个服务:server
和db
。 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:
编辑:上面的代码已更改为删除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
服务输出:
注意:第一行是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
并直接调用脚本。
答案 0 :(得分:1)
感谢您提供源来重现此问题。
您已经排除了docker-compose
文件中的任何问题。
问题出在您的Dockerfile
和您使用的node-pg
的版本之间。
您正在使用node:14-alpine
和pg: 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: