Cómo crear usuario / base de datos en script para Docker Postgres

213

He estado tratando de configurar un contenedor para una instancia de postgres de desarrollo creando un usuario y una base de datos personalizados. Estoy usando la imagen oficial de docker postgres . En la documentación, le indica que inserte un script bash dentro de la /docker-entrypoint-initdb.d/carpeta para configurar la base de datos con cualquier parámetro personalizado.

Mi script bash: make_db.sh

su postgres -c "createuser -w -d -r -s docker"
su postgres -c "createdb -O docker docker"

Dockerfile

FROM library/postgres

RUN ["mkdir", "/docker-entrypoint-initdb.d"]
ADD make_db.sh /docker-entrypoint-initdb.d/

El error que obtengo de docker logs -f db(db es el nombre de mi contenedor) es:

createuser: no se pudo conectar a la base de datos postgres: no se pudo conectar al servidor: No existe tal archivo o directorio

Parece que los comandos dentro de la /docker-entrypoint-initdb.d/carpeta se están ejecutando antes de que se inicie postgres. Mi pregunta es, ¿cómo configuro un usuario / base de datos mediante programación usando el contenedor oficial de postgres? ¿Hay alguna manera de hacer esto con un script?

pech0rin
fuente

Respuestas:

378

EDITAR - desde el 23 de julio de 2015

La imagen oficial de docker postgres ejecutará los .sqlscripts encontrados en la /docker-entrypoint-initdb.d/carpeta.

Entonces, todo lo que necesita es crear el siguiente script sql:

init.sql

CREATE USER docker;
CREATE DATABASE docker;
GRANT ALL PRIVILEGES ON DATABASE docker TO docker;

y agréguelo en su Dockerfile:

Dockerfile

FROM library/postgres
COPY init.sql /docker-entrypoint-initdb.d/

Pero desde el 8 de julio de 2015, si todo lo que necesita es crear un usuario y base de datos , es más fácil simplemente hacer uso de la POSTGRES_USER, POSTGRES_PASSWORDy POSTGRES_DBvariables de entorno:

docker run -e POSTGRES_USER=docker -e POSTGRES_PASSWORD=docker -e POSTGRES_DB=docker library/postgres

o con un Dockerfile:

FROM library/postgres
ENV POSTGRES_USER docker
ENV POSTGRES_PASSWORD docker
ENV POSTGRES_DB docker

para imágenes anteriores al 23 de julio de 2015

De la documentación de la imagen de Docker postgres , se dice que

[...] obtendrá cualquier script * .sh encontrado en ese directorio [ /docker-entrypoint-initdb.d] para realizar una inicialización adicional antes de iniciar el servicio

Lo importante aquí es "antes de comenzar el servicio" . Esto significa que su script make_db.sh se ejecutará antes de que se inicie el servicio postgres, por lo tanto, el mensaje de error "no se pudo conectar a la base de datos postgres" .

Después de eso hay otra información útil:

Si necesita ejecutar comandos SQL como parte de su inicialización, se recomienda el uso del modo de usuario único de Postgres.

De acuerdo, esto puede ser un poco misterioso a primera vista. Lo que dice es que su script de inicialización debe iniciar el servicio postgres en modo único antes de realizar sus acciones. Por lo tanto, puede cambiar su script make_db.ksh de la siguiente manera y debería acercarse a lo que desea:

NOTA , esto ha cambiado recientemente en la siguiente confirmación . Esto funcionará con el último cambio:

export PGUSER=postgres
psql <<- EOSQL
    CREATE USER docker;
    CREATE DATABASE docker;
    GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL

Anteriormente, --singlese requería el uso del modo:

gosu postgres postgres --single <<- EOSQL
    CREATE USER docker;
    CREATE DATABASE docker;
    GRANT ALL PRIVILEGES ON DATABASE docker TO docker;
EOSQL
Thomasleveil
fuente
2
puedes con:gosu postgres postgres --single < /tmp/somefile.sql
Thomasleveil
1
¿Cómo puedo correrpsql -U myDb -d myDb -f myDb.sql
DarVar
3
Nota: --single ya no se admite en ninguno de los Dockerfiles de postgres.
brianz
1
Seguí los pasos y cuando ingresé en el contenedor mysql y en el CD docker-entrypoint-initdb.d, vi mi init.sqlcon el código sql. ¡PERO cuando abro mysql (usando el acceso raíz) y escribo show database, solo veo el valor predeterminado del docker-compose.ymlarchivo! ¿Por qué no me funciona?
Mahmoud Zalt el
1
No se ejecuta db.sqlincluso después de que se copia en `docker-entrypoint-initdb.d`
kamal
16

Ahora puede poner archivos .sql dentro del directorio init:

De los documentos

Si desea realizar una inicialización adicional en una imagen derivada de esta, agregue uno o más scripts * .sql o * .sh en /docker-entrypoint-initdb.d (creando el directorio si es necesario). Después de que el punto de entrada llama a initdb para crear el usuario y la base de datos predeterminados de postgres, ejecutará cualquier archivo * .sql y generará cualquier script * .sh que se encuentre en ese directorio para realizar una inicialización adicional antes de iniciar el servicio.

Así que copiar su archivo .sql funcionará.

m0meni
fuente
Las variables de entorno para usuario / contraseña y creación de db todavía funcionan. (9.5.3)
Jeremiah Adams
1
Tengo un proyecto que funciona localmente. Sin embargo, cuando lo muevo a AWS EC2, dice que no se encuentra DB. Copio el archivo .sql en el directorio apropiado, pero aún así devuelve ese error. ¿Alguna idea de dónde podría mirar? Soy nuevo en Docker.
MadPhysicist
1
Esto solo es cierto si hay una única instancia de db vinculada a un solo volumen, es decir, no ejecutará ningún script si ya hay algo en la carpeta del volumen. Esto me da dolor de cabeza, porque quiero tener un inicializador db separado para cada uno de mis servicios de pila.
rojo violeta
Desde el documentos Warning: scripts in /docker-entrypoint-initdb.d are only run if you start the container with a data directory that is empty; any pre-existing database will be left untouched on container startup. One common problem is that if one of your /docker-entrypoint-initdb.d scripts fails (which will cause the entrypoint script to exit) and your orchestrator restarts the container with the already initialized data directory, it will not continue on with your scripts.
sh87
9

Al usar docker-compose:

Suponiendo que tiene el siguiente diseño de directorio:

$MYAPP_ROOT/docker-compose.yml
           /Docker/init.sql
           /Docker/db.Dockerfile

Expediente: docker-compose.yml

version: "3.3"
services:
  db:
    build:
      context: ./Docker
      dockerfile: db.Dockerfile
    volumes:
      - ./var/pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"

Expediente: Docker/init.sql

CREATE USER myUser;

CREATE DATABASE myApp_dev;
GRANT ALL PRIVILEGES ON DATABASE myApp_dev TO myUser;

CREATE DATABASE myApp_test;
GRANT ALL PRIVILEGES ON DATABASE myApp_test TO myUser;

Expediente: Docker/db.Dockerfile

FROM postgres:11.5-alpine
COPY init.sql /docker-entrypoint-initdb.d/

Servicios de composición e inicio:

docker-compose -f docker-compose.yml up --no-start
docker-compose -f docker-compose.yml start
Vlad
fuente
8

Agrego comandos personalizados a un entorno evocado en un CMD después de iniciar los servicios ... No lo he hecho con postgres, pero con Oracle:

#set up var with noop command
RUN export POST_START_CMDS=":"
RUN mkdir /scripts
ADD script.sql /scripts
CMD service oracle-xe start; $POST_START_CMDS; tail -f /var/log/dmesg

y empezar con

docker run -d ... -e POST_START_CMDS="su - oracle -c 'sqlplus @/scripts/script' " <image>

.

Rondó
fuente
7

Puedes usar estos comandos:

docker exec -it yournamecontainer psql -U postgres -c "CREATE DATABASE mydatabase ENCODING 'LATIN1' TEMPLATE template0 LC_COLLATE 'C' LC_CTYPE 'C';"

docker exec -it yournamecontainer psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE postgres TO postgres;"
Gabriel
fuente
3
ENCODING 'LATIN1'es muy raro ... Debe tener necesidades muy particulares para evitar usar utf8
Zyigh
4

Debe tener la base de datos ejecutándose antes de crear los usuarios. Para esto necesitas múltiples procesos. Puede iniciar postgres en un subshell (&) en el script de shell, o usar una herramienta como supervisor para ejecutar postgres y luego ejecutar cualquier script de inicialización.

Una guía para supervisor y acoplador https://docs.docker.com/articles/using_supervisord/

seanmcl
fuente
2

Con docker compose hay una alternativa simple (no es necesario crear un Dockerfile). Simplemente cree un init-database.sh:

#!/bin/bash
set -e

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
    CREATE USER docker;
    CREATE DATABASE my_project_development;
    GRANT ALL PRIVILEGES ON DATABASE my_project_development TO docker;
    CREATE DATABASE my_project_test;
    GRANT ALL PRIVILEGES ON DATABASE my_project_test TO docker;
EOSQL

Y referencia en la sección de volúmenes:

version: '3.4'

services:
  postgres:
    image: postgres
    restart: unless-stopped
    volumes:
      - postgres:/var/lib/postgresql/data
      - ./init-database.sh:/docker-entrypoint-initdb.d/init-database.sh
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - 5432:5432

volumes:
  postgres:
danigb
fuente