How to use repmgr with dockerized Postgresql? - postgresql

I'm trying to create Postgresql setup with replication and automatic failover. I wanted it to be reusable, scalable and portable, so I tried to use docker to run Postgres. I also didnt want to reinvent the wheel, so I tried to use repmgr as it is recommended tool when setting up postgres replication.
On first machine, I started master node using docker-compose and simple Dockerfile. Then I added bash script to run after start of container. It looks like master was setup properly.
Then on second machine I tried to run standby. I started postgres in docker and experimented. As we can read on repmgr github repo, to run standby I need to clone master with repmgr command, which uses pg_basebackup internally. Pg_basebackup wont let me clone db into not-empty directory (pgdata), so I need to remove pgdata created by dockerized postgres before cloning. When I do that, container dies, because of course the only service, postgres, dies without its files. So I can not run repmgr command afterwards, because there is no container to run it inside :)
So I tried creating another Dockerfile for standbies only. Inside pgdata is removed, then repmgr clones master, then postgres is started properly. There are no errors. But when I log into master machine and verify if replication is running - it is not.
Is it possible to run repmgr for dockerized postgres?
If so, how? What am I doing wrong?
If not, how can I create postgres cluster replication without manually configuring each server? You know, not treating servers as pets but as cattle etc :)
Files I used and created:
docker-compose.yml (both master and standby node):
version: '3'
services:
db:
build:
context: .
volumes:
- ${DATABASE_STORAGE_PATH}:/var/lib/postgresql/data
env_file:
- .env
environment:
- PGDATA=/var/lib/postgresql/data/pgdata
ports:
- ${DB_PORT}:5432
Master Dockerfile:
FROM postgres:9.6.4
# install repmgr
RUN apt-get update \
&& apt-get install -y repmgr
# COPY conf files
COPY repmgr.conf .
COPY postgres.replication.conf /var/lib/postgresql/data/pgdata/
RUN echo "include '/var/lib/postgresql/data/pgdata/postgres.replication.conf'" >> /var/lib/postgresql/data/pgdata/postgresql.conf
CMD ["postgres"]
Bash script to run when postgres container (master) is alive:
#!/bin/bash
CONTAINER_ID="replicatest2_db_1"
docker exec -d $CONTAINER_ID createuser -s repmgr -U postgres
docker exec -d $CONTAINER_ID createdb repmgr -O repmgr -U postgres
docker exec -d $CONTAINER_ID su postgres -c 'repmgr -f repmgr.conf master register'
Standby docker-compose is the same. Standby Dockerfile:
FROM postgres:9.6.4
# install repmgr
RUN apt-get update \
&& apt-get install -y repmgr
# COPY repmgr conf
COPY repmgr.conf .
RUN rm -rf /var/lib/postgresql/data/pgdata
RUN su postgres -c "repmgr -h {master-host} -p {master-port} -U repmgr -d repmgr -D /var/lib/postgresql/data/pgdata -f repmgr.conf standby clone"
CMD ["postgres"]

You can use pg-dock, with pg-dock you can run dockerized postgres cluster with repmgr, otherwise you can explore the project and get understand how the things are work.
Good Luck.

Related

Artifactory upgrade fail, postgres 9.5 -> 9.6 upgrade instructions needed

I had planned an upgrade of artifactory from 6.7.5 to 6.8.1. As part of the upgrade I checked jfrog's repo on github and it looks like they have a new recommended nginx and postgres version.
The current docker-compose is using postgres 9.5 and the new default version if 9.6. Simply pulling down postgres 9.6 however does not do an inplace upgrade.
FATAL: database files are incompatible with server DETAIL: The data
directory was initialized by PostgreSQL version 9.5, which is not
compatible with this version 9.6.11.
The upgrade instructions do not mention anything about how to do the upgrade.
The examples provided in github (https://github.com/jfrog/artifactory-docker-examples) are just examples.
Using them in production could cause issues and backwards compatibility is not guaranteed.
To get over the PostgreSQL matter when upgrading, I would suggest:
$ docker-compose -f yml-file-name.yml stop
edit the yml-file-name.yml and change the docker.bintray.io/postgres:9.6.11 to docker.bintray.io/postgres:9.5.2
$ docker-compose -f yml-file-name.yml up -d
Artifactory should be upgraded after following this, however it will keep using the previous version of the PostgreSQL DB
I have been able to upgrade database using following approach:
Dump all database to an SQL script using old database image; store it in a volume for future import:
# Override PostgreSQL image used to export using old binaries
printf "version: '2.1'\nservices:\n postgresql:\n image: docker.bintray.io/postgres:9.5.2\n" > image_override.yml
started_container=$(docker-compose -f artifactory-pro.yml -f image_override.yml run -d -v sql_dump_volume:/tmp/dump --no-deps postgresql)
# Dump database to a text file in a volume (to make it available for import)
docker exec "${started_container}" bash -c "until pg_isready -q; do sleep 1; done"
docker exec "${started_container}" bash -c "pg_dumpall --clean --if-exists --username=\${POSTGRES_USER} > /tmp/dump/dump.sql"
docker stop "${started_container}"
docker rm --force "${started_container}"
Back up old database directory and prepare a new one:
mv -fv /data/postgresql /data/postgresql.old
mkdir -p /data/postgresql
chown --reference=/data/postgresql.old /data/postgresql
chmod --reference=/data/postgresql.old /data/postgresql
Run a new database image with mounting dump script from step 1. It processes SQL scripts upon startup when setting up a new database, provided it's started as postgres something. We just don't need to leave the server running afterwards, so I provided --version to make entrypoint execute, import the data and quit:
docker-compose -f artifactory-pro.yml run --rm --no-deps -e POSTGRES_DB=postgres -e POSTGRES_USER=root -v sql_dump_volume:/docker-entrypoint-initdb.d postgresql postgres --version
After all this is done, I was able to start Artifactory normally with docker-compose -f artifactory-pro.yml up -d and it started up normally, applying rest of schema and file upgrade procedure as usual.
I have also prepared a script that basically does the above steps along with some additional checks and cleanup. Feel free to use if you find it useful.

Docker postgres doesn't start at build

I want do use a docker container to simulate my production environment, so I installed the db and the server in the same container, and not each in his own.
This is my dockerfile:
FROM debian
RUN apt update
RUN apt install postgresql-9.6 tomcat8 tomcat8-admin -y
RUN service postgresql start
RUN service postgresql status # says postgres is down
RUN su - postgres ;
RUN createdb db_example # fails !!!
RUN psql -c "CREATE USER springuser WITH PASSWORD 'test123';"
RUN exit
RUN service tomcat8 start
COPY target/App-1.0.war /var/lib/tomcat8/webapps/
CMD ["/bin/bash"]
The problem is that the database is down so I am uable to create the user and the database.
If I start the a debian docker container and do this steps per hand everything works fine.
Thanks for your help
All the recommendations in the comments are correct, it's better to keep services in different containers.
Nevertheless and just to let you know, the problem in the Dockerfile is that starting services in RUN statements is useless. For every line in the Dockerfile, docker creates a new image. For example RUN service postgresql start, it may start postgresql during docker build, but it doesn't persist in the final image. Only the filesystem persist from one step to another, not the processes.
Every process need to be started in the entrypoint, this is the only command that's called when you exec docker run:
FROM debian
RUN apt update
RUN apt install postgresql-9.6 tomcat8 tomcat8-admin -y
COPY target/App-1.0.war /var/lib/tomcat8/webapps/
ENTRYPOINT["/bin/bash", "-c", "service postgresql start && service postgresql status && createdb db_example && psql -c \"CREATE USER springuser WITH PASSWORD 'test123';\" && service tomcat8 start && sleep infinity"]
(It may have problems with quotes on psql command)
I have the problem hat in the war file the localhost for the database war hard coded.
Thanks to Light.G, he suggested me to use --net=host for the container, so now there is one container with the database and one with the tomcat server.
This are the steps I followed.
Build the docker image
docker build -t $USER/App .
Start a postgres database
We are using the host namespace it is not possible to run another programm on the post 5432.
Start the postgres container like this:
docker run -it --rm --net=host -e POSTGRES_USER='springuser' -e POSTGRES_DB='db_example' -e POSTGRES_PASSWORD='test123' postgres
Start the tomcat
Start the App container, with this command:
docker run -it --net=host --rm $USER/App

Initialize data on dockerized mongo

I'm running a dockerized mongo container.
I'd like to create a mongo image with some initialized data.
Any ideas?
A more self-contained approach:
create javascript files that initialize your database
create a derived MongoDB docker image that contains these files
There are many answers that use disposable containers or create volumes and link them, but this seems overly complicated. If you take a look at the mongo docker image's docker-entrypoint.sh, you see that line 206 executes /docker-entrypoint-initdb.d/*.js files on initialization using a syntax: mongo <db> <js-file>. If you create a derived MongoDB docker image that contains your seed data, you can:
have a single docker run command that stands up a mongo with seed data
have data is persisted through container stops and starts
reset that data with docker stop, rm, and run commands
easily deploy with runtime schedulers like k8s, mesos, swarm, rancher
This approach is especially well suited to:
POCs that just need some realistic data for display
CI/CD pipelines that need consistent data for black box testing
example deployments for product demos (sales engineers, product owners)
How to:
Create and test your initialization scripts (grooming data as appropriate)
Create a Dockerfile for your derived image that copies your init scripts
FROM mongo:3.4
COPY seed-data.js /docker-entrypoint-initdb.d/
Build your docker image
docker build -t mongo-sample-data:3.4 .
Optionally, push your image to a docker registry for others to use
Run your docker image
docker run \
--name mongo-sample-data \
-p 27017:27017 \
--restart=always \
-e MONGO_INITDB_DATABASE=application \
-d mongo-sample-data:3.4
By default, docker-entrypoint.sh will apply your scripts to the test db; the above run command env var MONGO_INITDB_DATABASE=application will apply these scripts to the application db instead. Alternatively, you could create and switch to different dbs in the js file.
I have a github repo that does just this - here are the relevant files.
with the latest release of mongo docker , something like this works for me.
FROM mongo
COPY dump /home/dump
COPY mongo_restore.sh /docker-entrypoint-initdb.d/
the mongo restore script looks like this.
#!/bin/bash
# Restore from dump
mongorestore --drop --gzip --db "<RESTORE_DB_NAME>" /home/dump
and you could build the image normally.
docker build -t <TAG> .
First create a docker volume
docker volume create --name mongostore
then create your mongo container
docker run -d --name mongo -v mongostore:/data/db mongo:latest
The -v switch here is responsible for mounting the volume mongostore at the /data/db location, which is where mongo saves its data. The volume is persistent (on the host). Even with no containers running you will see your mongostore volume listed by
docker volume ls
You can kill the container and create a new one (same line as above) and the new mongo container will pick up the state of the previous container.
Initializing the volume
Mongo initializes a new database if none is present. This is responsible for creating the initial data in the mongostore. Let's say that you want to create a brand new environment using a pre-seeded database. The problem becomes how to transfer data from your local environment (for instance) to the volume before creating the mongo container. I'll list two cases.
Local environment
You're using either Docker for Mac/Windows or Docker Toolbox. In this case you can easily mount a local drive to a temporary container to initialize the volume. Eg:
docker run --rm -v /Users/myname/work/mongodb:/incoming \
-v mongostore:/data alpine:3.4 cp -rp /incoming/* /data
This doesn't work for cloud storage. In that case you need to copy the files.
Remote environment (AWS, GCP, Azure, ...)
It's a good idea to tar/compress things up to speed the upload.
tar czf mongodata.tar.gz /Users/myname/work/mongodb
Then create a temporary container to untar and copy the files to the mongostore. the tail -f /dev/null just makes sure that the container doesn't exit.
docker run -d --name temp -v mongostore:/data alpine:3.4 tail -f /dev/null
Copy files to it
docker cp mongodata.tar.gz temp:.
Untar and move to the volume
docker exec temp tar xzf mongodata.tar.gz && cp -rp mongodb/* /data
Cleanup
docker rm temp
You could also copy the files to the remote host and mounting from there but I tend to avoid interacting with the remote host at all.
Disclaimer. I'm writing this from memory (no testing).
Here is how its done with docker-compose. I use an older image of mongo but the docker-entrypoint.sh accepts *.js and *.sh files for all versions of the image.
docker-compose.yaml
version: '3'
services:
mongo:
container_name: mongo
image: mongo:3.2.12
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db:cached
- ./deploy/local/mongo_fixtures /fixtures
- ./deploy/local/mongo_import.sh:/docker-entrypoint-initdb.d/mongo_import.sh
volumes:
mongo-data:
driver: local
mongo_import.sh:
#!/bin/bash
# Import from fixtures
mongoimport --db wcm-local --collection clients --file /fixtures/properties.json && \
mongoimport --db wcm-local --collection configs --file /fixtures/configs.json
And my monogo_fixtures json files are the product of monogoexport which have the following format:
{"_id":"some_id","field":"value"}
{"_id":"another_id","field":"value"}
This should help those using this without a custom Dockefile, just using the image straight away with the right entrypoint setup right in your docker-compose file. Cheers!
I've found a way that is somehow easier for me.
Say you have a database in a docker container on your server, and you want to back it up, here’s what you could do.
What might differ from your setup to mine is the name of your mongo docker container [mongodb] (default when using elastic_spence). So make sure you start your container first with --name mongodb to match the following steps:
$ docker run \
--rm \
--link mongodb:mongo \
-v /root:/backup \
mongo \
bash -c ‘mongodump --out /backup --host $MONGO_PORT_27017_TCP_ADDR’
And to restore the database from a dump.
$ docker run \
--rm \
--link mongodb:mongo \
-v /root:/backup \
mongo \
bash -c ‘mongorestore /backup --host $MONGO_PORT_27017_TCP_ADDR’
If you need to download the dump from to your server you can use scp:
$ scp -r root#IP:/root/backup ./backup
Or upload it:
$ scp -r ./backup root#IP:/root/backup
P.S: Original source by Tim Brandin available at https://blog.studiointeract.com/mongodump-and-mongorestore-for-mongodb-in-a-docker-container-8ad0eb747c62
Thank you!

Postgres in Docker persistent data

I'm running postgres inside a docker container to limit the amount of system resources it has access to. I'm having some trouble understanding how to make the data persistent. I've read the following articles:
https://www.andreagrandi.it/2015/02/21/how-to-create-a-docker-image-for-postgresql-and-persist-data/
http://container42.com/2013/12/16/persistent-volumes-with-docker-container-as-volume-pattern/
Which suggest using a data only container, and then having my postgres container link to it. What I'm failing to understand is; what's the advantage to this? As far as I can tell, if for some reason the docker-machine shut down (for example; moving it to a different physical machine), the data only container stops running, and all of it's contents are lost? I've tried creating a volume in the postgres container, but it doesn't actually seem to save anything to the disk.
Here's my docker file. What am I doing wrong?
FROM ubuntu
MAINTAINER Andrew Broadbent <andrew.broadbent#manchester.ac.uk>
# Add the PostgreSQL PGP key to verify their Debian packages.
# It should be the same key as https://www.postgresql.org/media/keys/ACCC4CF8.asc
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8
# Add PostgreSQL's repository. It contains the most recent stable release
# of PostgreSQL, ``9.3``.
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list
# Install ``python-software-properties``, ``software-properties-common`` and PostgreSQL 9.3
# There are some warnings (in red) that show up during the build. You can hide
# them by prefixing each apt-get statement with DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y python-software-properties software-properties-common postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3
# Note: The official Debian and Ubuntu images automatically ``apt-get clean``
# after each ``apt-get``
# Run the rest of the commands as the ``postgres`` user created by the ``postgres-9.3`` package when it was ``apt-get installed``
USER postgres
# Create a PostgreSQL role named ``docker`` with ``docker`` as the password and
# then create a database `docker` owned by the ``docker`` role.
# Note: here we use ``&&\`` to run commands one after the other - the ``\``
# allows the RUN command to span multiple lines.
RUN /etc/init.d/postgresql start &&\
psql --command "CREATE USER docker WITH SUPERUSER PASSWORD 'docker';" &&\
createdb -O docker docker
# Complete configuration
USER root
RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/9.3/main/pg_hba.conf
RUN echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
# Expose the PostgreSQL port
EXPOSE 5432
# Add VOLUMEs to allow backup of config, logs and databases
RUN mkdir -p /var/run/postgresql && chown -R postgres /var/run/postgresql
VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]
# Set the default command to run when starting the container
USER postgres
CMD ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]
This one answers your question about data container:
docker mounting volumes on host
Regarding to your dockerfile, I would suggest you either:
1) use data container pattern
2) mount the volume to host machine by specifying: docker run -v [host-path]:[container-path] ..., so that data will be kept at one place in your host and will not be lost after the container is removed.
Ref: https://docs.docker.com/engine/tutorials/dockervolumes/#/mount-a-host-directory-as-a-data-volume

Postgresql raises 'data directory has wrong ownership' when trying to use volume

I'm trying to run postgresql in docker container, but of course I need to have my database data to be persistent, so I'm trying to use data only container which expose volume to store database at this place.
So, my data container has such Dockerfile:
FROM ubuntu
# Create data directory
RUN mkdir -p /data/postgresql
# Create /data volume
VOLUME /data/postgresql
Which I run:
docker run --name postgresql_data lyapun/postgresql_data true
In my postgresql.conf I set:
data_directory = '/data/postgresql'
Then I run my postgresql container in such way:
docker run -d --name postgre --volumes-from postgresql_data lyapun/postgresql
And I got:
2014-07-04 07:45:57 GMT FATAL: data directory "/data/postgresql" has wrong ownership
2014-07-04 07:45:57 GMT HINT: The server must be started by the user that owns the data directory.
How to deal with this issue? I googled a lot to find some information about using postgresql with docker volumes, but I didn't found anything.
Thanks!
Ok, seems like I found workaround for this issue.
Instead of running postgres in such way:
CMD ["/usr/lib/postgresql/9.1/bin/postgres", "-D", "/var/lib/postgresql/9.1/main", "-c", "config_file=/etc/postgresql/9.1/main/postgresql.conf"]
I wrote bash script:
chown -Rf postgres:postgres /data/postgresql
chmod -R 700 /data/postgresql
sudo -u postgres /usr/lib/postgresql/9.1/bin/postgres -D /var/lib/postgresql/9.1/main -c config_file=/etc/postgresql/9.1/main/postgresql.conf
And replaced CMD in postgresql image to:
CMD ["bash", "/run.sh"]
It works!
You have to set ownership of directory /data/postgresql to the same user, under which you are running your postgresql binary. For example, in Ubuntu it is usually postgres user.
Then you have to use this command:
chown postgres.postgres /data/postgresql
A better way to solve that issue, assuming your postgres images is named "postgres" and that your backup is ./backup.tar:
First, add this to your postgres Dockerfile:
VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]
Then run:
docker run -it --name postgres -v $(pwd):/db postgres sh -c "tar xvf /db/backup.tar --no-overwrite-dir" && \
docker run -it --name data --volumes-from postgres busybox true && \
docker rm postgres && \
docker run -it --name postgres --volumes-from=data postgres
You don't have permission issues since the archive is extracted by the postgres user of your postgres image, so it is the owner of the extracted files.
You can then backup your data using the data container. The advantage of this solution is that you don't chmod/chown every time you run the image.
This type of errors is quite common when you link a NTFS directory into your docker container. NTFS directories don't support ext3 file & directory access control.
The only way to make it work is to link directory from a ext3 drive into your container.
I got a bit desperate when I played around Apache / PHP containers with linking the www folder. After I linked files reside on a ext3 filesystem the problem disappear.
I published a short Docker tutorial on youtube, may it helps to understand this problem: https://www.youtube.com/watch?v=eS9O05TTFjM