I have a nodejs app and i have a routine to daily backup a postgres database using spawn to run db_dumpall.
The question is how do i execute a command from a container to the other.
I have tried enabling ssh in both containers but i cant connect them automatically using password or public key.
NODEJS FUNCTION:
const backupDB = (path: string) => {
return new Promise((resolve, reject) => {
const backupProcess = spawn('bash', ['-c', `docker exec ${dbOptions.container} pg_dumpall -U ${dbOptions.user} > ${path}`]);
// THIS WORKS WELL IF I EXECUTE THIS FUNTION OUTSIDE THE CONTAINER
backupProcess.on('exit', (code, signal) => {
resolve(code)
if (code)
console.log('Backup process exited with code ', code);
else if (signal)
console.error('Backup process was killed with singal ', signal);
else
console.log('Successfully backedup the database')
});
backupProcess.on('error', (error) => {
console.log(error)
resolve('Ocurrió un error al hacer el backup de la base de datos')
});
})
};
DOCKER COMPOSE FILE:
nodejs-server:
image: nodejs-server
build:
context: ../backend_apollo_server_express
dockerfile: Dockerfile
ports:
- "4000:4000"
environment:
- PROTOCOL=http://
- HOST=localhost
- PORT=4000
- JWT_SECRET=appsecret321
- JWT_EXPIRESIN=300
- WORKER_POOL_ENABLED=0
- DB_NAME=lims
- DB_USER=lims
- DB_PASSWORD=lims
- CONTAINER_NAME=frontend_postgres_1
- DB_SCHEMA=public
- "DATABASE_URL=postgresql://lims:lims@postgres/lims?schema=public"
depends_on:
- postgres
volumes:
- ../backend_apollo_server_express:/usr/src/app
- "/etc/timezone:/etc/timezone:ro"
- "/etc/localtime:/etc/localtime:ro"
- app-volume:/root/.ssh
postgres:
container_name: db_postgres
command: sh -c "service ssh start && runuser -u postgres postgres"
image: postgresc
build:
context: ../backend_apollo_server_express
dockerfile: Dockerfile.database
environment:
- "POSTGRES_USER=lims"
- "POSTGRES_PASSWORD=lims"
volumes:
- /home/javier/lims/dockerVolumes/db:/var/lib/postgresql/data
- "/etc/timezone:/etc/timezone:ro"
- "/etc/localtime:/etc/localtime:ro"
- app-volume:/usr/src/shared-volume
ports:
- 5434:5432
volumes:
app-volume:
CodePudding user response:
One container can't run a command in another container. At the same time, most relational databases are designed so that you can communicate with them over TCP; you can talk to the database from outside its container and you don't need Docker-specific tricks to talk to it.
For your use case, the important detail is that pg_dumpall
takes --host
, --port
, --username
, and similar parameters, and also honors the standard PostgreSQL environment variables like $PGHOST
and $PGUSER
. That means that, if pg_dumpall
is in the same image as your node application, then you can use the normal Node child_process
API to just run it.
In your Dockerfile, you need to install the PostgreSQL command-line tools. The default node
image is Debian based, so this will look something like
FROM node:16
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get install --no-install-recommends --assume-yes \
postgresql-client
WORKDIR /app
COPY package*.json ./
...
When you go to run it, you can just run pg_dumpall
as a subprocess. You do not need docker exec
, an ssh connection, or anything else. Note that this will run in the Node container but that's probably not a problem.
import { open } from 'fs/promises';
import { spawn } from 'child_process';
import { Writable } from 'stream';
const runPgDumpall = async (stream: Writable) => {
const subprocess = spawn('pg_dumpall', [], {
stdio: ['inherit', stream, 'inherit']
});
return new Promise((resolve, reject) => {
subprocess.on('exit', (code, signal) => resolve(code));
subprocess.on('error', err => reject(err));
});
};
const backupDB = async (path: string) => {
const fh = await open(path, 'w');
const stream = fh.createWriteStream();
try {
return await runPgDumpall(stream);
} finally {
stream.end();
}
}
Finally, in the docker-compose.yml
file you need to give the Node application details on how to contact the database container. You can pass these using environment variables.
version: '3.8'
services:
nodejs-server:
build: ../backend_apollo_server_express
environment:
- PGHOST=postgres
- PGUSER=lims
- PGPASSWORD=lims
postgres:
image: 'postgres:13'
environment:
- POSTGRES_USER=lims
- POSTGRES_PASSWORD=lims
volumes:
- /home/javier/lims/dockerVolumes/db:/var/lib/postgresql/data
CodePudding user response:
You actually can but this is fishy as you should enable one container to have control of others, meanning that if compromised you could be in trouble.
There are two ways around this: 1You actually can but this is fishy as you should enable one container to have control of others, meanning that if compromised you could be in trouble.
There are two ways around this:
- Using docker exec to execute from your host a command on your docker (using crontab for example)
- You could put this command inside a docker with a crontab daemon inside you'll just have to establish a connection to your DB inside this docker.