Search code examples
dockerdocker-composedocker-network

How to share a single Docker database service across multiple Docker Compose groups?


I have a Docker Compose group consisting of a web server and an Oracle database. I use this for development only, not in production. The web server depends_on Oracle and so waits for Oracle before it starts up.

version: '3.9'

services:
  frontend:
    image: "${FRONTEND_IMAGE_TAG}"
    env_file:
      - .env
    volumes:
      - .:/var/www/
    ports:
      - ${httpsport}
    restart: always  
    depends_on: 
      oracle:
          condition: service_healthy      

  oracle:
    image: container-registry.oracle.com/database/enterprise:${ORACLE_VERSION}
    env_file:
      - .env
    ports:
      - ${ORACLE_LISTEN_PORT}:1521
      - ${ORACLE_TCPS_PORT}:2484
      - ${ORACLE_EM_PORT}:5500
    volumes:
      - ${ORACLE_ORADATA}:/opt/oracle/oradata
      - ${ORACLE_TRANSFER}:/opt/oracle/transfer

It works very well as long as I only run a single instance of my group. But often during development, I'd like to run multiple development groups e.g.

cd /web/dev
docker compose up -d

cd /web/test
docker compose up -d

The problem here is that it will attempt to run a 2nd Oracle database, which I absolutely do not want to happen, as I'm using a volume to persist data (plus it seems like a big unnecessary load on the machine). I only want to run the Oracle Docker container once and share it among any 2nd and subsequent attempts to run my Compose group.

Some approaches I'm considering:

  • use Docker Network to split out Oracle into its own service. Then I'd have to figure out a way to check if the Oracle service was up and running, and start it when it's not etc. Seems unclear how this would work

  • use Docker Compose extends to split out Oracle. I think this will have the same problems as previous approach

  • maybe write some shell script to do the checks and start Oracle service. I don't like doing this though, it feels like Docker should have a in-built solution

There is a Github issue with many people requesting this feature from Docker. Until that gets updated I'd love to hear some workarounds.


Solution

  • In the end I wrote a shell script to manage this. I split my Docker Compose file into 2 files, one for Oracle running on its own network, and one for the front end. The shell script checks that Oracle is up and healthy before starting the front end.

    oracle.yaml looks like this:

    version: '3.9'
    
    networks:
      mynetwork:
        
    services:
      oracle:
        image: container-registry.oracle.com/database/enterprise:${ORACLE_VERSION}
        env_file:
          - .env
        ports:
          - ${ORACLE_LISTEN_PORT}:1521
          - ${ORACLE_TCPS_PORT}:2484
          - ${ORACLE_EM_PORT}:5500
        volumes:
          - ${ORACLE_ORADATA}:/opt/oracle/oradata
          - ${ORACLE_TRANSFER}:/opt/oracle/transfer
        networks:
          - mynetwork
    

    and the frontend one looks like this:

    version: '3.9'
    
    services:
      frontend:
        image: "${FRONTEND_IMAGE_TAG}"
        env_file:
          - .env
        volumes:
          - .:/var/www/
        ports:
          - ${httpsport}
        restart: always 
    

    Here's the shell script:

    #!/bin/bash
    # This script can be used to share an Oracle Docker instance with multiple Docker front end containers
    # It checks if an Oracle Docker instance is running, starts it if necessary, then starts a frontend Docker instance
    # This script must be run from the same directory as your docker-compose.yaml and oracle.yaml files
    
    echo "Checking for a running Oracle Docker process..."
    
    # Check if there is already a healthy Oracle Docker process running
    docker_ps_output=$(docker ps --filter "name=oracle" --filter "health=healthy" | sed '1d')
    if [[ -n $docker_ps_output ]]; then
        echo "Oracle is running. Starting frontend Docker container."
    else
        echo "No healthy Oracle Docker process found. Starting Oracle Docker container now."
    
        # Start the Oracle Docker process
        docker compose -f oracle.yaml up -d
    
        # Loop until a healthy Oracle Docker process is found
        while true; do
            docker_ps_output=$(docker ps --filter "name=oracle" --filter "health=healthy" | sed '1d')
            if [[ -n $docker_ps_output ]]; then
                echo "Oracle Docker process is healthy. Starting frontend Docker container."
                break
            else
                echo "Oracle Docker process is not yet healthy. Continuing to check..."
                sleep 5
            fi
        done
    fi
    
    # Start our frontend Docker container (ignore warnings about removing orphans)
    export COMPOSE_IGNORE_ORPHANS=True
    docker compose up -d   
    echo "frontend is starting"