Deploy on EC2: GitHub Actions for Spring Boot and MongoDB with Docker

Deploy on EC2: GitHub Actions for Spring Boot and MongoDB with Docker

In this blog post, we'll understand the process of creating a Continuous Integration and Continuous Deployment (CICD) pipeline using GitHub Actions for a Spring Boot application, along with MongoDB and Redis servers, all within the convenience of Docker. So, Let's explore GitHub Actions and Docker to effortlessly automate our build and deployment process on EC2.

Prerequisites

  • EC2 instance

  • Spring boot application

Step 1: Install docker on EC2

Install docker on your Ubuntu EC2 instance with the below commands.

sudo apt-get update
sudo apt-get install docker.io

To use docker without sudo, Give the user permission to docker.

sudo usermod -aG docker $USER

After hitting the above command, we need to reboot the system to get it to work.

sudo reboot

Now, you are ready to use docker without using sudo. So you can check your docker version with the below command.

docker --version

Step 2: Start Mongo and Redis service with Docker image

As we are using MongoDB and Redis in the application we need to set those as well. instead of installing those manually let's take advantage of docker to run MongoDB and Redis on the EC2 server.

docker run --name mongo -e MONGO_INITDB_ROOT_USERNAME=your_username -e MONGO_INITDB_ROOT_PASSWORD=your_password -p 27017:27017 -v /data:/var/lib/mongo/data -d mongo

Here, we are pulling a Mongo image from the docker hub, setting the Mongo username and password, specifying the port to run our Mongo container, and attaching volume in case we lose our container.

docker run --name redis -p 6379:6379 -v /data:/var/lib/redis/data -d redis

The same we have done for Redis and started both containers in detached mode with -d flag.

You can check your running containers with the command.

docker ps

You will be able to see your running Mongo and Redis containers.

Also, you can check the Mongo connection using a Mongo compass.

Your application should have the same configuration as done for the containers. You need to add MongoDB and Redis configuration to the application.properties or application.yml file.

spring:
  data:
    mongodb:
      database: your_db_name
      host: mongo
      username: your_username
      password: your_password
      authentication-database: admin
      port: 27017

  redis:
    port: 6379
    host: redis

Step 3: Dockerize your spring boot application

Clean Build your spring boot application with mvn clean install.

mvn clean install

Now, let's create a Dockerfile to Dockerize the spring boot application and add it to Dockerhub.

FROM openjdk:17
ENV SPRING_PROFILES_ACTIVE=dev
ADD target/my-springboot-server.jar my-springboot-server.jar
EXPOSE 7098
ENTRYPOINT [ "java", "-jar", "my-springboot-server.jar" ]

Here, We are using an image openjdk:17 for the Java 17 version. Setting up the dev environment. Adding a jar file which is created after mvn clean install. Expose port to run your application if any by default it will be 8080, then the Entrypoint which will be the actual command to run your application i.e. java -jar my-springboot-server.jar .

Step 4: Create a GitHub Action script

Create a folder named .github/workflows in your repository folder. Create a .yml file to write your deployment script such as deployment.yml*.*

Here, we will create an action named Build & Deploy backend server that will get triggered on any push in the development branch. And schedule jobs that will be running on ubuntu-latest (EC2 instance). We will be adding steps to run a script in a decided flow.

name: Build & Deploy backend server

on:
  push:
    branches: [ "development" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    steps:

Use GitHub's actions/checkout@v3 to checks-out your repository under $GITHUB_WORKSPACE, so your workflow can access it.

      - uses: actions/checkout@v3

Install Java 17 using actions/setup-java@v3 .

      - name: Install Java 17 JDK
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'zulu'
          cache: maven

Next step to build your application jar with mvn clean install .

      - name: Build with Maven
        run: mvn clean install

And here comes the action to build and push your image to Dockerhub, to push your image into Dockerhub you need to log in to your Dockerhub account, you need to pass your docker creds as it is not publically shareable, Hence we need to add those into secrets of a repository. In the next step, we will see how to add secrets to a repository.

Also login to your Dockerhub account from the EC2 machine as well so it can have access to pull your private image.

      - name: Build & Push Docker image
        uses: mr-smithers-excellent/docker-build-push@v5
        with:
          image: ${{secrets.DOCKER_USERNAME }}/your_dockerhub_repo
          tags: latest
          registry: docker.io
          dockerfile: Dockerfile
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

With the above action, every new image will get pushed to Dockerhub and is ready to pull and run. So let's Write steps to Pull and Run the image.


      - name: Pull & Run Docker image
        uses: appleboy/ssh-action@master
        with:
          host: 13.212.244.119
          username: ubuntu
          key: ${{ secrets.ACTION_PRIVATE_KEY }}
          envs: GITHUB_SHA
          script: |
            docker login
            docker pull ${{ secrets.DOCKER_USERNAME }}/your_dockerhub_repo
            docker stop your_dockerhub_repo
            docker rm your_dockerhub_repo
            docker run --detach --name your_dockerhub_repo -p 7098:7098 ${{ secrets.DOCKER_USERNAME }}/your_dockerhub_repo

You need to add your ssh private key to your GitHub secrets ACTION_PRIVATE_KEY and add a public key to your EC2 machine so they can communicate and transfer data.

To successfully run your pipeline, you have to connect your repo with your EC2 by adding secrets.ACTION_PRIVATE_KEY Also, give Dockerhub access by adding secrets.DOCKER_USERNAME and secrets.DOCKER_PASSWORD .

Step 5: Add secrets to your GitHub Repo

Let's dive into how to set secrets to your GitHub repo.

  1. Go to your repository settings.

  2. Open Secrets and Variables under the security section.

  3. Click on Actions -> New repository secret.

  4. Enter your Secret name and its value, then click on Add secret.

Follow the same to add other secrets. Secrets are successfully added to your repository.

till this step, you will be able to successfully build and run your pipeline.

On doing docker ps on your EC2 machine you will be able to list out all 3 Docker Containers. One for my-springboot-server and one for Mongo and the other one for Redis. But these three images are still running separately and not able to communicate with each other.

If you try to hit an API endpoint of my-springboot-server then it will throw an error like MongoSocketReadTimeoutException. So to get this work, the Docker network comes into the picture.

Step 6: Add your Docker images to a Docker network

Create a Docker network.

docker network create my-network

To see a list of your Docker networks.

docker network ls

Add your images to my-network.

docker network connect my-network my-springboot-server
docker network connect my-network mongo
docker network connect my-network redis

To check if your images are connected to a network, inspect my-network.

docker network inspect my-network

Also, update your deployment.yml file to automatically add your newly created Docker container to the Docker network by updating the Docker run command in the .yml file.

docker run --detach --name your_dockerhub_repo -p 7098:7098 --network my-network ${{ secrets.DOCKER_USERNAME }}/your_dockerhub_repo

After all, your GitHub action file will look like this and is ready to execute and run your pipeline.

# This workflow will build and push a new container image to Dockerhub.
# Pull the latest image from DockerHub and run on EC2 

name: Build & Deploy backend server

on:
  push:
    branches: [ "development" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Install Java 17 JDK
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'zulu'
          cache: maven

      - name: Build with Maven
        run: mvn clean install

      - name: Build & Push Docker image
        uses: mr-smithers-excellent/docker-build-push@v5
        with:
          image: ${{secrets.DOCKER_USERNAME }}/your_dockerhub_repo
          tags: latest
          registry: docker.io
          dockerfile: Dockerfile
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Pull & Run Docker image
        uses: appleboy/ssh-action@master
        with:
          host: 13.212.244.119
          username: ec2-user
          key: ${{ secrets.ACTION_PRIVATE_KEY }}
          envs: GITHUB_SHA
          script: |
            docker login
            docker pull ${{ secrets.DOCKER_USERNAME }}/your_dockerhub_repo
            docker stop your_dockerhub_repo
            docker rm your_dockerhub_repo
            docker run --detach --name your_dockerhub_repo -p 7098:7098 --network my-network ${{ secrets.DOCKER_USERNAME }}/your_dockerhub_repo

Conclusion


With this automation in place, you'll be able to streamline your build and deployment process on EC2, saving time and effort while ensuring a smooth and efficient development workflow. GitHub Actions is a native feature of the GitHub platform hence it eliminates the need for external CI/CD tools or services. This makes the setup and configuration process much simpler, more accessible and delivers outstanding results.