Problem

Suppose we have an already running app, e.g. a Wagtail blog, which runs on a postgres database that is mounted as a volume named website_pgdata

~/blogtutorials: apoehlmann $ docker volume ls
DRIVER              VOLUME NAME
local               website_pgdata

We are now about to set up a new postgres service via its own docker-compose.yml file. However we would like it to be connected to our already existing volume. How can we do that?

Solution

One way is the following docker-compose.yml file:

version: '3.6'

services:
  postgres:
    container_name: testpg
    environment:
      POSTGRES_DB: web_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    image: postgres:latest
    expose:
       - 5432
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
    external: true
    name:
      website_pgdata

And that's all we need to do.

Code explanation

Our docker-compose runs with version 3.6 and creates a new service called postgres, our new container will be called testpg.

Again, we are going to use an already existing database. In my case that database is called web_db, the postgres user is postgres and so is the password of that user (make sure to use the credentials of your database!). For our new container to be able to connect to that database we specify these variables as environment variables.

We use the lastest postgres image and open port 5432 (default postgres port) in case other, future services might want to connect to this db.

Now to the crucial part: we specify an external volume pgdata that will be mounted into the container at /var/lib/postgres/data. Why exactly this path? Because this PostgreSQL's default database location on Linux. This might be slightly different from version to version, so make sure to specify the correct path here (e.g. in some other project of mine it happend to be /var/lib/postgresql/9.5/main).

Next we need to provide some further information regarding the pgdata Volume. On the same height as services: we set up a new block volumes: where we specify the name of our new volume pgdata (this step is optional however make sure it is the same name specified in the postgres service!)

If we would run docker-compose now Docker would create a completely new volume. However we want to use an existing volume therefore we need to set external: true and specify the existing volume's name: website_pgdata.

Deployment

From within the same directory as our docker-compose.yml (in my case ~/blogtutorials) we will now call docker-compose with the -d flag which will first build the new container, start it and run it in detached mode (=background)

~/blogtutorials: apoehlmann $ docker-compose up -d
Creating testpg ... done

Our container should be running now

~/blogtutorials: apoehlmann $ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
dad8e90789df        postgres:latest     "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        5432/tcp            testpg

Let's quickly inspect our container: we expect the already existing volume to be listed under the "Mounts" section

~/blogtutorials: apoehlmann $ docker inspect testpg
"Mounts": [
            {
                "Type": "volume",
                "Name": "website_pgdata",
                "Source": "/var/lib/docker/volumes/website_pgdata/_data",
                "Destination": "/var/lib/postgresql/data",
                "Driver": "local",
                "Mode": "rw",
                "RW": true,
                "Propagation": ""
            }
        ],

The name of the volume matches the existing one. Source tells us the path on the host machine (= our PC) and Destination tells us the path inside the container.

Sanity Check: looking at the database within the container

As a last check we log into our new container and have a look at the database:

~/blogtutorials: apoehlmann $ docker exec -it testpg bash
root@00e22268eedb:/#

We are now running an interactive bash within our container testpg. We switch to the user postgres, run psql and check if the database web_db exists:

# switch to postgres user
root@00e22268eedb:/# su postgres

# run psql
$ psql
psql (10.3 (Debian 10.3-1.pgdg90+1))
Type "help" for help.

# list all DBs
postgres=# \l

                                 List of databases
   Name    |  Owner   | Encoding |  Collate   |   Ctype    |   Access privileges
   
-----------+----------+----------+------------+------------+--------------------
---
 postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 | 
 template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres        
  +
           |          |          |            |            | postgres=CTc/postgr
es
 template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 | =c/postgres        
  +
           |          |          |            |            | postgres=CTc/postgr
es
 web_db    | postgres | UTF8     | en_US.utf8 | en_US.utf8 | 
(4 rows)

# connect to our db web_db
postgres=# \c web_db
You are now connected to database "web_db" as user "postgres".

# list all existing tables
web_db=# \dt
 public | auth_group                                   | table | postgres
 public | auth_group_permissions                       | table | postgres
 public | auth_permission                              | table | postgres
 public | auth_user                                    | table | postgres
 public | auth_user_groups                             | table | postgres
 public | auth_user_user_permissions                   | table | postgres
 public | base_footertext                              | table | postgres
 public | base_homepage                                | table | postgres
 public | base_standardpage                            | table | postgres
 public | blog_blogindexpage                           | table | postgres
 public | blog_blogpage                                | table | postgres
 public | blog_blogpagerelatedlink                     | table | postgres
 public | blog_blogpagetag                             | table | postgres
 public | django_admin_log                             | table | postgres
 public | django_content_type                          | table | postgres
 public | django_migrations                            | table | postgres
 public | django_session                               | table | postgres
 public | django_site                                  | table | postgres
 public | taggit_tag                                   | table | postgres
 public | taggit_taggeditem                            | table | postgres
 public | wagtailcore_collection                       | table | postgres
 public | wagtailcore_collectionviewrestriction        | table | postgres
 public | wagtailcore_collectionviewrestriction_groups | table | postgres
--More--

Everything worked fine and both the database and its data exist in our just newly created container.

So we now know how to create a docker-compose.yml file that creates a new postgres service and mounts an already existing volume into a specified path inside the container.

Note that we dealt with a special case here: a postgres database. However the concept here can be applied to any service regarding any kind of volume and any path inside the container.

For more information on volumes have a look at the official documentation.

Getagged mit:
Docker Tutorial English
blog comments powered by Disqus