Search code examples
pythonbashpostgresqldockerredeploy

Postgres and Docker redeploy on file change


I am creating a web application using Java/Postgres/Docker/Gradle. My project is set up like this:

There is a db container and an application container. The app container just runs a plain jar file. The jar file is added using a volume from the host system. The container initially runs this jar and monitors it for changes. If it changes, it redeploys the jar allowing developers to see the changes on their local machines immediately. This is like deploying/updating a war file on Tomcat. The live application updates when you 'push a button'. No need to stop the app, rebuild and rerun: Just rebuild.

Using the Postgres image on Dockerhub, I want to do something similar: I want to 'push a button' in gradle which will update some file monitored by the DB container and have it redeploy.

I've hit a wall, unfortunately. My initial idea was to create a Dockerfile that extends the Postgres one. All it would do is run the Postgres service as usual and run a script looking for changes in some file and redeploying.

The following Dockerfile fails, unfortunately:

FROM postgres:9.4

WORKDIR /db
COPY monitor.py .
COPY run-pg-and-monitor.sh .

CMD ["run-pg-and-monitor.sh"]

The shell script has the following commands:

# Run monitor in the background
python3 monitor.py &

# Run postgres normally
postgres

Unfortunately, this doesn't fly with Postgres. I get the error:

db_1       | "root" execution of the PostgreSQL server is not permitted.
db_1       | The server must be started under an unprivileged user ID to prevent
db_1       | possible system security compromise.  See the documentation for
db_1       | more information on how to properly start the server.

If I were to set the command to:

CMD ["postgres"]

which is what the Postgres container originally did, the error goes away. Obviously, this doesn't run monitor.py which I need.

Oddly enough, running CMD without the square brackets also causes this issue.

CMD postgres

Running it from a shell environment also fails:

CMD ["sh", "-c", "postgres"]

Perhaps this has something to do with running it from a shell...

I know that postgres is supposed to be run from the postgres user and not root. What I find strange is if I set CMD to:

CMD ["whoami"]

I get:

db_1       | root

So, it was already running postgres as root with no problems.

A second idea I had was to just make a gradle task that:

1) Updates the jar which triggers a redeploy for the app container

2) Runs docker exec ... to update the running db container manually.

The only problem with this idea is that the job will obviously fail in the event that there is no app or db container running. I would have to add logic which checks to see if they are. This does not seem ideal.

Does anyone have any insights? Is there a solution that already exists I'm not aware of?


Solution

  • The reason for those oddities is that the postgres Dockerfile sets it's own docker-entrypoint.sh script as ENTRYPOINT for the image. So on container start docker-entrypoint.sh is executed and whatever is set as CMD is passed to the script as it's argument(s).

    The entry script will do the required init steps for postgres, like dropping privileges, only if the first passed argument equals postgres (see here and here), before finally simply executing whatever was passed via CMD.

    Overriding ENTRYPOINT instead of CMD, essentially wrapping docker-entrypoint.sh with your own script, should give you the desired behavior:

    # Run monitor in the background
    python3 monitor.py &
    
    # Run postgres normally
    docker-entrypoint.sh "$@"