If you are developing microservices, docker-compose(compose), can be a very powerful tool. Think of a case with an API service and database. Creating and maintaining a database with the use of the Liquibase.
Initial plan
…would be:
- start DB
- run the Liquibase to make changes
- start an app
Adding docker-compose as a driver to utilize this process:
PostgresDB Setup
This is the simplest step. According to the docs, this would be enough:
service:
...
pg:
container_name: pg
image: postgres:13.1
healthcheck:
test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER
timeout: 45s
interval: 10s
retries: 10
restart: always
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: password
POSTGRES_DB: docker
volumes:
- ./db:/docker-entrypoint-initdb.d/
ports:
- 5432:5432
...
Without going into Postgres setup specifics above configuration can be interpreted as:
- service name is pg
- container name is pg, too
- use image postgres version 13.1
- setup healthcheck
- restart always container is it not running
- set environment variables
- attach volume
- expose container port to the host
healthcheck
This instruction tells Docker how to test container health status. Container starts with starting status. After interval period test is executed. If in timeout period test return 0, then container is healthy. If test returns 1, or there is no response after timeout period test fails. Next test is executed after interval passes. If retries number of time test fails, container is considered unhealthy.
restart policy
The restart policy defines what to do when the container fails or stops.
environment
This instruction defines environment variables set in the container. The application running in the container will be able to read them.
volumes
The volumes mount a local file system within a container file system. In case above directory ./db will be mounted to container as /docker-entrypoint-initdb.d/. If ./db does not exist Docker will make it.
Liquibase Postgres
We could start the PG database and then run the Liquibase manually. But why?:
service:
...
liquibase_pg:
container_name: pg_updater
image: liquibase/liquibase:4.7.0
volumes:
- ./liquibase:/liquibase/changelog
command: liquibase --url="jdbc:postgresql://pg:5432/docker" --changeLogFile=./changelog/changelog.xml --username=root --password=password update
depends_on:
- pg
...
So, the new service is named liquibase_pg. As well it mounts volume /liquibase/changelog for the local directory ./liquibase. Then it executes command but only when service pg is healthy as it depends on pg service. The command is the Liquibase specific. It defines connection string, credentials, command and utilizes mounted volume.
Mounted volume should contain Liquibase changelog.
depends
This instruction defines a list of services that need to be running and healthy before Docker can start this one.
Application
services:
...
app:
container_name: echo
build: .
ports:
- 9999:9999
volumes:
- ./configs:/app/configs
depends_on:
- liquibase_pg
restart: always
...
This is some application developer is working on. It does not pull any image. Rather if the image does not exist it will be built from Dockerfile located in the path defined with build. And it will start only after Liquibase is started.
Nothing here that we did not already use.
Putting all together
The whole configuration can be found here.
In full setup, the application is run on MariaDB as well.