This post describes how you may achieve a postgres container initialization using a bash script using docker secrets (you could use configs as well) without having to modify official image for postgres

This post requires docker swarm to be active, though you don’t need a cluster

First of all initialize docker swarm if you haven’t done so

docker swarm init

Before we start, let’s see why we may use docker secrets to achieve it, since we will be using docker-compose (service based) to allow us to do it, from the docs we get :

You can use secrets to manage any sensitive data which a container needs at runtime but you don’t want to store in the image or in source control…

So this allows us to have files managed by docker under containers at runtime.

Without getting into the security specifications for docker secrets, we may jump to how we may use it.

docker secret create path/to/file/in/host name_of_secret[.optional extension]

Ok so now we have a way to create a docker managed secret from a file.

We also have that there are a few flags we may use when setting the container access to the secret.

The default simple usage is just to pass the secret name to the container with:

docker run --secret name_of_secret.ext some_image

This will mount the secret under /run/secrets/name_of_secret.ext by the default

But there are other flags via csv

Being the explicitly documented:

source 
target

#Example:
--secret source=name_of_secret,target=/path/to/secret/inside/the/container

This will mount the secret under /path/to/secret/inside/the/container and the path doesn’t have to exist prior to mounting.

We also have others modifiers when setting the secret inside the container

UID
GID
Mode
--secret source=name_of_secret,target=/path/to/secret/inside,uid:1000,gid:1000,mode:400 th container

There are no examples in the referred link for documentation of docker secrets

So this allows us to set a secret inside a container, that may belong to a specific user and group, an most important for our use case, the mode that allows us to set the permission bit for execution we will require for our bash script.

Please not the mode must be an octal for permissions, will not get into details on permission octals.

So what this has to do with postgresql official docker image you may ask?

Well, from the documentation for image usage we get that it is possible to extend the image by setting scripts inside the /docker-entrypoint-initdb.d/ folder, and an example would be:

#!/bin/bash 
set -e
echo "Creating DB"
psql <<- EOSQL
    CREATE USER docker_secret;
    CREATE DATABASE docker_secrets;
    GRANT ALL PRIVILEGES ON DATABASE docker_secrets TO docker_secret;
EOSQL
echo "Done Creating DB"

Of course you may set an extremely complex bash, or multiple bash scripts inside this folder in order to fully set your postgres container on initialization.

So now let’s check an example of this merge.

For simplicity, we will use a docker-compose.yml example.

Let’s create the following docker-compose.yml file

version: '3.6'
services:
  postgres:
    image: postgres:11.1
    secrets:
      - source: database_initialization
        target: "/docker-entrypoint-initdb.d/create-db.sh"
        #Grant read and execute permission to owner, execute and read to group and others
        mode: 0755
        #The uid for root is due to the permissions for the init.db folder for image
        uid: "0"
secrets:
  database_initialization:
    file: create-db.sh

With the contents from the example above for database creation under a file a named: create-db.sh in our root folder for this example.

As you see we are not modifying the official image, and not referencing a single dockerfile to be built etc..

If you are in windows watch out for CRLF line endings, and change them to LF.

So secrets using UID GID an Mode are only support with swarm mode active.

So let’s start our postgres service with docker stack

$docker stack deploy --compose-file=docker-compose.yml 

Creating network postgres_default
Creating secret postgres_database_initialization
Creating service postgres_echo_secret

Lets check our service logs to see what is going on.

postgres_postgres.1.mqms6d4f2224@linuxkit-00155d51a607    | /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/create-db.sh
postgres_postgres.1.mqms6d4f2224@linuxkit-00155d51a607    | Creating DB
postgres_postgres.1.mqms6d4f2224@linuxkit-00155d51a607    | CREATE ROLE
postgres_postgres.1.mqms6d4f2224@linuxkit-00155d51a607    | CREATE DATABASE
postgres_postgres.1.mqms6d4f2224@linuxkit-00155d51a607    | GRANT
postgres_postgres.1.mqms6d4f2224@linuxkit-00155d51a607    | Done Creating DB

And there we have it, the script has been ran an our database has been created.

This example was for postgresql , but may be used with many other images.

This could also have used docker configs as usage is extremely smilar,here is the compose file to achieve it below:

version: '3.6'
services:
  postgres:
    image: postgres:11.1
    configs:
      - source: database_initialization
        target: "/docker-entrypoint-initdb.d/create-db.sh"
        #Grant read and execute permission
        mode: 0755
        uid: "0"
configs:
  database_initialization:
    file: create-db.sh

Hope you enjoyed reading, and that somehow it helped you somehow.

André Ilhicas dos Santos

Devops Padawan, curious about systems automation, learning new languages, paradigms tools each day.

ilhicas ilhicas


Published