DEV Community

Laura
Laura

Posted on • Edited on

Migrating an Springboot Java API: Part 1 - Docker

Despite being a primarily .NET Core Azure developer, sometimes we take new challenges. I want to write my process of getting an old Java API from no CI/CD into AWS, in hope that helps devs in a similar situation.

I am aware many things I do in this tutorial can be improved, but being a beginner in AWS and coming from a different tech stack means I will take some decisions which the future me will frown upon, but bear with me ;)

Pre-requisites

  • Docker
  • IDE that supports Java and Springboot. I used IntelliJ IDEA
  • JDK (in my case 1.8)
  • Springboot
  • A database (in my case MySql)

The original setup

This application had no CI/CD pipeline. We could access only an AWS EC2 instance with the running APIs amd the source code.

The original setup is a Java 1.8 solution with 3 projects:

MyJavaSolution/
├── pom.xml
│
├── admin/
│ │── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ ├── resources/
│ │ │ └──── application.yml
│ │ └── pom.xml
│
├── customer/
│ │── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ ├── resources/
│ │ │ └──── application.yml
│ │ └── pom.xml
│
├── common/
│ │── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ ├── resources/
│ │ │ └──── application.yml
│ │ └── pom.xml
Enter fullscreen mode Exit fullscreen mode

Both the APIs rely on the common project. And both APIs use a Redis cache (which we will include in the docker-compose)

The objective

The final objective is having a fully functioning CI/CD pipeline and deployment to a cloud provider.

For this series I will use Azure DevOps for the pipeline and AWS as hosting provider.

Part 1 - Dockerizing the project

The first step I did was dockerizing the application. That allows me to reduce the variability between environments and builds, and to access services such as AWS Elastic Beanstalk.

The APIs

Without changing any of the actual solution, I created the following Docker files:

  • Customer.Dockerfile
# Use a base image with Java 8 and Maven for building
FROM maven:3.6.3-jdk-8 AS builder

WORKDIR /app/common
COPY ./common/pom.xml ./pom.xml
COPY ./common/src ./src

WORKDIR /app/admin
COPY ./admin/pom.xml ./pom.xml
COPY ./admin/src ./src

WORKDIR /app/customer
COPY ./customer/pom.xml ./pom.xml
COPY ./customer/src ./src

WORKDIR /app
COPY ./pom.xml ./pom.xml
RUN mvn dependency:go-offline

RUN mvn package

FROM openjdk:8-jdk-alpine AS runner
WORKDIR /app
COPY --from=builder app/common/target/common-0.0.1-SNAPSHOT.jar ./common.jar
COPY --from=builder app/customer/target/customer-0.0.1-SNAPSHOT.jar ./customer.jar

EXPOSE 9001

CMD ["java", "-jar", "customer.jar"]

Enter fullscreen mode Exit fullscreen mode
  • Admin.Dockerfile
# Use a base image with Java 8 and Maven for building
FROM maven:3.6.3-jdk-8 AS builder

WORKDIR /app/common
COPY ./common/pom.xml ./pom.xml
COPY ./common/src ./src

WORKDIR /app/admin
COPY ./admin/pom.xml ./pom.xml
COPY ./admin/src ./src

WORKDIR /app/customer
COPY ./customer/pom.xml ./pom.xml
COPY ./customer/src ./src

WORKDIR /app
COPY ./pom.xml ./pom.xml
RUN mvn dependency:go-offline

RUN mvn package

FROM openjdk:8-jdk-alpine AS runner
WORKDIR /app
COPY --from=builder app/common/target/common-0.0.1-SNAPSHOT.jar ./common.jar
COPY --from=builder app/admin/target/admin-0.0.1-SNAPSHOT.jar ./admin.jar

EXPOSE 9002

CMD ["java", "-jar", "admin.jar"]
Enter fullscreen mode Exit fullscreen mode

It might seem a bit strange that I included the customer/ folder in the Admin.Dockerfile and vice-versa but the reason was because the solution pom.xml relied on both to create the .jar files and it the refactoring of this issue was not part of the scope.

The docker-compose

After creating the dockerfiles and ensuring they work individually, same as if we running it with Springboot. I then created a docker-compose to ensure me and my team could run it easily:

version: '3'
services:
  local-redis:
    image: redis:7.2.1
    ports:
      - "6379:6379"
  test-customer:
    build:
      context: .
      dockerfile: Customer.Dockerfile
    depends_on:
      - local-redis
    ports:
      - "9001:9001"
  test-admin:
    build:
      context: .
      dockerfile: Admin.Dockerfile
    depends_on:
      - local-redis
    ports:
      - "9002:9002"
Enter fullscreen mode Exit fullscreen mode

Redis cache

Having now included the Redis cache as a Docker container running alongside the APIs, I just needed to change it's reference in the application.xmls.

spring:
  application:
    name: MyJavaSolution
# The redis cache should have the same host name and port as the one defined in the docker-compose.yml
  redis:
    host: local-redis
    port: 6379
    timeout: 5000
Enter fullscreen mode Exit fullscreen mode

And with that we have a fully working, OS-agnostic way of building and running this 2 Java 1.8 APIs.
In the next post of this series I will show how to use Azure DevOps pipelines to build and deploy this.

Top comments (0)