Simplify Local Development with Docker Compose – Part I

In this blog post, I will explain the ways to setup local development environment using docker compose and automate the process using batch or shell scripts.

Nowadays we are deploying applications in production using docker containers. We can create similar environment in our local development and test our application before committing the code.

This approach will help us to test similar to production and helps us to avoid silly mistakes.

I am going to demonstrate the the approach using the Spring Boot application. But approach can be used by any container based applications.

Creating Dockerfile

Let’s first create Dockerfile in root of the project to create image of our Spring Boot application.

FROM eclipse-temurin:17.0.4_8-jdk-jammy
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar

RUN addgroup  springboot && adduser sbuser
RUN usermod -G springboot sbuser
USER sbuser

EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]Code language: Java (java)

To create Docker image and run application, first we need to package the application to jar and run few docker commands to create image and run application.

Instead of running all the repetitive steps again and again using docker compose file and batch or shell script we can simplify the whole image creation and container running process.

Let create folder local-dev and put our docker-compose and batch/script files in it.

Creating Docker Compose File

version: '3'

services:
  springboot-crud-example:
    build:
      context: ../
      dockerfile: ./Dockerfile
    ports:
      - 8080:8080
      - 8000:8000
    environment:
      JAVA_OPTS:
        -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=nCode language: Java (java)

In our docker compose file , we are going to use the Docker file created in step 1.

When ever you want to start and test the application, simply you can run below commands

mvn clean packageCode language: Java (java)
docker-compose up --build --force-recreateCode language: Java (java)

If you want to stop the application, you can run below command.

docker-compose down Code language: Java (java)

We can further simplify the process by creating batch file or shell script by combining the commands.

Batch file


call mvn -f ../pom.xml clean package -DskipTests

echo Exit Code = %ERRORLEVEL%
if not "%ERRORLEVEL%" == "0" (
    ECHO "Error building application"
 ) ELSE (

       docker-compose rm -f
       docker-compose up --build --force-recreate
   )
exit /bCode language: Java (java)

Shell Script

#!/bin/bash


mvn -f ../pom.xml clean package

rc=$?
if [ $rc -ne 0 ] ; then
  echo Errors found during mvn clean verify, exit code [$rc];
else
 docker-compose rm -f
 docker-compose up --build --force-recreate
fi
Code language: Java (java)

Whenever user wants to start the application, he can simply run batch file or shell script.

rm -f – remove the any running containers

–build – Build images before starting containers

–force-recreate – Recreate containers even if their configuration and image haven’t changed.

force-recreate option will force the docker to create new image every time. As docker caches images without this option, it will use cached image.

Using Local Database

When you are testing containerized application and the application uses database to store and retrieve application, you may want to create a local database and test against it.

your application will not able to connect to locally installed database using host name as docker container runs in separate network

To overcome this problem, In Windows or Mac , you need to change the hostname from localhost to host.docker.internal in your application.properties file

from

spring.datasource.url=jdbc:postgresql://localhost:5432/eisCode language: Java (java)

to

spring.datasource.url=jdbc:postgresql://host.docker.internal:5432/eis
Code language: Java (java)

In Linux , you can set network mode to host to connect to locally installed database.

version: '3'

services:
  springboot-crud-example:
    build:
      context: ../
      dockerfile: ./Dockerfile
    ports:
      - 8080:8080
      - 8000:8000
    environment:
      JAVA_OPTS:
        -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
    network_mode: "host" # allows application to access local database in Linux environmentCode language: Java (java)

Note

In general locally installed databases are setup to accept connections from local systems only. Since docker runs in separate network, applications running in docker won’t able to reach the local database. You can configure the database to accept connections from any network but it poses security risk.

dev and test databases are configured to accept the request from company network , so if your application is pointing to them, you may not need to change the host name in real world applications.

Using Containerized Database

Instead of using local database, you can also use containerized database to test application which requires database access.

We can run the application and database in single network using docker compose file.

version: '3'

services:
  springboot-crud-example:
    container_name: 'springboot-app'
    build:
      context: ../
      dockerfile: ./Dockerfile
    ports:
      - 8080:8080
      - 8000:8000
    environment:
      SPRING_PROFILES_ACTIVE: container
      JAVA_OPTS:
        -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
    depends_on:
       - "postgres"


  postgres:
    container_name: 'postgresdb'
    image: 'postgres:13.2'
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: admin
      POSTGRES_DB: eisCode language: Java (java)

In above compose file we are starting application and postgresql database.

To access the database running with in container, we can not use localhost.

There are different approaches we can take so that application can connect to database running within the container.

Using Container Name

We can define name for container and use it as host name. In above compose file we named postgres service as “postgresdb” so we can use it as host name.

from

spring.datasource.url=jdbc:postgresql://localhost:5432/eis

to

spring.datasource.url=jdbc:postgresql://postgresdb:5432/eis

Changing the url in default application.properties file in real world application for testing becomes messy . We can use spring profile concept to create separate profile for container based testing.

Create application-container.properties file in resources folder and place the following property.

spring.datasource.url=jdbc:postgresql://postgresdb:5432/eis

Using Network Alias

We can create a network and assign a alias for for postgres and use it as host

version: '3'

services:
  springboot-crud-example:
    build:
      context: ../
      dockerfile: ./Dockerfile
    ports:
      - 8080:8080
      - 8000:8000
    environment:
      SPRING_PROFILES_ACTIVE: container
      JAVA_OPTS:
        -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
    depends_on:
       - "postgres"
    networks:
      app-net:
        aliases:
          - springbootapp

  postgres:
    image: 'postgres:13.2'
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: admin
      POSTGRES_DB: eis
    networks:
      app-net:
        aliases:
        - postgresdb

networks:
  app-net: {}Code language: Java (java)

In application-container.properties file place the following property.

spring.datasource.url=jdbc:postgresql://postgresdb:5432/eis

Using Network IP address

We can assign pre-defined IP address to each container and use it as host.

version: '3'

services:
  springboot-crud-example:
    build:
      context: ../
      dockerfile: ./Dockerfile
    ports:
      - 8080:8080
      - 8000:8000
    environment:
      SPRING_PROFILES_ACTIVE: container
      JAVA_OPTS:
        -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
    depends_on:
      - "postgres"
    networks:
      app-net:
        aliases:
          - springbootapp
        ipv4_address: 172.19.1.2
  postgres:
    image: 'postgres:13.2'
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: admin
      POSTGRES_DB: eis
    networks:
      app-net:
        aliases:
          - postgresdb
        ipv4_address: 172.19.1.3
networks:
  app-net:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.19.1.0/24
          gateway: 172.19.1.1Code language: Java (java)

In application-container.properties file place the following property.

spring.datasource.url=jdbc:postgresql://172.19.1.3:5432/eis

Note

Depending on postgressql settings , you may need to override the user and password property also in your profile specific properties file.

Similar Posts