#node.js #postgresql #docker #docker-compose
#node.js #postgresql #docker #docker-compose
Вопрос:
У меня есть 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
вывод службы:
NOTE: The first line is the output of the first console.log
call from script.js
.
NOTE: Since the server
service is set up with restart: unless-stopped
, it will repeat this output forever.
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
...
From the host computer, I can reach the PostgreSQL database at the db
service, connecting successfully, using the same script from the server
service.
Вывод сценария, запущенного с главного компьютера:
➜ node script.js
postgres://postgres:postgres@localhost:15432/mydb
null Client { ... }
undefined Result { ... }
null Result { ... }
Этот вывод означает, что соединение выполнено успешно.
В итоге:
Я не могу получить доступ к db
контейнеру из server
контейнера, получая тайм-ауты при подключении, но я могу получить доступ к db
контейнеру с главного компьютера, успешно подключившись.
Соображения
Во-первых, спасибо за ответ до сих пор. Устранение некоторых поднятых вопросов:
-
Отсутствует сеть:
Это не требуется, поскольку docker-compose имеет сеть по умолчанию. A протестировано с пользовательской сетью, но это тоже не сработало.
-
Порядок инициализации:
Я использую
depends_on
, чтобы убедитьсяdb
, что контейнер запускается первым, но я знаю, что это не гарантирует, что база данных фактически инициализируется первой, а затем сервером. Это не проблема, потому что сервер прерывается, когда происходит тайм-аут, и он запускается снова, потому что он настроенrestart: unless-stopped
. Поэтому, если база данных все еще инициализируется при первой или второй попытке запуска сервера, проблем нет, потому что сервер будет продолжать перезапускаться до тех пор, пока соединение не завершится успешно (чего никогда не происходило). -
Обновить:
Из
server
контейнера я мог бы получить доступ к базе данных вdb
службе, используяpsql
. Я все еще не могу подключиться из Node.js приложение есть.Проблема
DATABASE_URL
не в этом, потому что URI, который я использовал вpsql
команде, — это тот же URI, который используетсяscript.js
и печатается при первомconsole.log
вызове.Используется командная строка:
docker exec -it server psql postgres://postgres:postgres@db:15432/mydb
Редактировать: улучшен код за счет удаления зависимости для продолжения. Теперь он использует только pg
и вызывает скрипт напрямую.
Комментарии:
1. я давно не использовал docker, но, возможно, попробуйте добавить: networks: — mynetwork На том же уровне, что и порт внутри служб сервера и базы данных
2. Мне не удалось воспроизвести вашу ошибку: я повторно использовал ваш файл docker-compose, заменил сервис
app
стандартным образом Ubuntu, выполнилdocker-compose up
и установилpostgresql-client
. Командаpsql -h db -p 15432 -U postgres -W
из контейнера приложения выполнена успешно! Я рекомендую вам посмотреть конфигурацию Sequelize, возможно, ошибка в строке подключения.3. @NelsonG. @jeeves Я только что попробовал это, и я смог добраться до
db
контейнера, используяpsql
server
контейнер. Теперь мне нужно выяснить, почему я не могу получить доступ из node.js использование приложенияpg
. Спасибо за помощь!4. Можете ли вы поделиться своим файлом Dockerfile. Я взял ваш docker-compose и скрипт и не могу воспроизвести вашу проблему. Я использую
node:10-alpine
в качестве базового образа без других установленных библиотек, кромеpg
5. Ответ обновлен решением 🙂
Ответ №1:
Спасибо за предоставление источника для воспроизведения проблемы. Никаких проблем в docker-compose
файле, как вы уже исключили.
Проблема заключается между вашей Dockerfile
и node-pg
используемой вами версией.
Вы используете node:14-alpine
и pg: 7.18.2
. Оказывается, есть ошибка на узле 14 и более ранних версиях node-pg
.
Решением является либо понижение версии до node v12, либо использование последней версии node-pg
, которая в настоящее время является 8.4.2 (исправление было введено в версии 8.0.3).
Я проверил оба этих решения в предоставленной вами ветке, и они работают.
Комментарии:
1. Я добавил некоторые соображения в конце описания вопроса. Я также тестировал их запуск отдельно, как вы сказали, но проблема сохранялась.
2. Вот почему он работал на хост-компьютере, версия узла там 12. Спасибо!!!
Ответ №2:
Это не полный ответ; У меня нет вашего кода под рукой, поэтому я не могу протестировать файл compose. Тем не менее, есть несколько проблем, на которые я хотел бы указать:
-
links
Директива устарела.Это
links
устаревшая опция, которая использовалась до того, как Docker представил определяемые пользователем сети и автоматическую поддержку DNS. Вы можете просто избавиться от него. Контейнеры в файле компоновки могут ссылаться друг на друга по имени без. -
expose
Директива ничего не делает. Это может быть информативно, напримерDockerfile
, как способ сказать: «это изображение предоставит сервис на этом порту», но на самом деле ничего не происходит. Это почти полностью бесполезно в файле компоновки. -
depends_on
Директива также менее полезна, чем вы думаете. Это действительно приведет к тому, что docker-compose сначала откроет контейнер базы данных, но контейнер считается «запущенным», как только запустится первый процесс. Это не заставляет docker-compose ждать, пока база данных будет готова к обслуживанию запросов, что означает, что вы все равно столкнетесь с ошибками, если ваше приложение попытается подключиться до того, как база данных будет готова.Лучшее решение для этого — встроить логику повторного подключения к базе данных в ваше приложение, чтобы, если база данных когда-либо выйдет из строя (например, вы перезапускаете контейнер postgres для активации новой конфигурации или обновления версии postgres), приложение будет повторять попытки подключения до тех пор, пока оно не будет успешным.
Приемлемым решением является включение кода в запуск вашего приложения, который блокируется до тех пор, пока база данных не ответит на запросы.
Комментарии:
1. В конце описания вопроса я добавил некоторые соображения относительно порядка инициализации. Я провел тест: я запустил
db
контейнер и убедился, что база данных правильно инициализирована, получив доступ к ней с главного компьютера, затем я запустилserver
контейнер, но получил тот же результат: тайм-аут.2. Я удалю
link
иexpose
, спасибо. Что касается этого, можно с уверенностью сказать, что порт был доступен, потому что я мог сделать это с главного компьютера.
Ответ №3:
Проблема не имеет ничего общего с 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
с apt install -y
помощью postgresql-client `.
Команда 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
.
Чтобы адаптировать пример, приведенный в документации 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:
Комментарии:
1. Я сделал «минимальную» реализацию своего проекта ( здесь ), но ваша идея лучше как минимальный проект. Я попробую это и вернусь с любыми отзывами. Спасибо!
2. Хорошо, я создал настоящий минимальный проект, используя только pg, и у меня все еще есть проблема, она здесь, в ветке minimal . :'(