Github Actions: how to deploy application to AWS ECS and migrate database with Flyway

gasparini16

Piotr Łagowski

Posted on May 14, 2024

Github Actions: how to deploy application to AWS ECS and migrate database with Flyway

Automatization is extremely important in the context of application development and this is where Github Actions will be great. In this article, we will focus on the necessary instructions to run a workflow that will perform the deployment of our application on AWS ECS and run the database migration using Flyway. An application built using the Spring Framework will be used as an example.

The following instructions necessary for deployment have been developed for the deployment of CRESH, a great platform for validating business ideas.

Adding secrets in Github Repo

The first step will be to add secrets relating to information such as:

  • aws user access key
  • aws use secret access key
  • datasource connection pool
  • database username
  • database password

Those fields are necessary for the deployment and database migration.

Setting workflow environment variables

At the very beginning, we can define the environment variables that we will use in the workflow area. Below are the necessary variables that we will need for deployment:

env:
  ECR_REPOSITORY: <ecr_repo_name>
  ECS_SERVICE: <ecs_service_name>
  ECS_CLUSTER: <ecs_cluster_name>
  CONTAINER_NAME: <container_name>
Enter fullscreen mode Exit fullscreen mode

Action Steps

Setup AWS CLI

Logging into the AWS CLI is essential for the entire workflow. An Access Key and Secret Access Key can be created on the AWS Console for a specific user. Let's assume that for this article, a github_user user has been created to allow you to perform any action using the AWS CLI. Remember that the user you create on AWS should have the appropriate roles.

- name: Setup AWS CLI
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-west-1
Enter fullscreen mode Exit fullscreen mode

Setup Java

A simple step that will be used to set the Java version:

- name: Setup Java
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: '17'
Enter fullscreen mode Exit fullscreen mode

Setup Flyway

Downloading and unpacking a specific version of the flyway. This will enable us to run the migration:

      - name: Setup Flyway
        run: |
          curl -LJO https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/7.14.0/flyway-commandline-7.14.0-linux-x64.tar.gz
          tar xvzf flyway-commandline-7.14.0-linux-x64.tar.gz
          sudo mv flyway-7.14.0 /usr/local/flyway
          sudo ln -s /usr/local/flyway/flyway /usr/local/bin/flyway
Enter fullscreen mode Exit fullscreen mode

Run Flyway Migration

Start the migration process, in which it is necessary to provide database access secrets and indicate the location of the migration files:

      - name: Run Flyway Migration
        run: |
          /usr/local/flyway/flyway -url=${{ secrets.SPRING_DATASOURCE_URL }} -user=${{ secrets.SPRING_DATASOURCE_USERNAME }} -password=${{ secrets.SPRING_DATASOURCE_PASSWORD }} -locations=filesystem:src/main/resources/database/migrations migrate
Enter fullscreen mode Exit fullscreen mode

Login to Amazon ECR

And here app deployment starts! The AWS Github actions repo comes to your aid here. Logging can be done as follows:

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@62f4f872db3836360b72999f4b87f1ff13310f3a
Enter fullscreen mode Exit fullscreen mode

Build and Push Docker Image to AWS ECR

Building our application and pushing it to the AWS Elastic Container Registry. In order to build the application, you will need a Dockerfile, which will contain the maven command that prepares the jar file. Example Dockefile:

# Use an official Maven image as the build image with Java 17
FROM maven:3.8.4-openjdk-17-slim AS build

# Set the working directory in the container
WORKDIR /app

# Copy the project files into the container
COPY . .

# Build the application
RUN mvn clean install

# Use a lightweight JRE image with Java 17 as the final base image
FROM openjdk:17-jdk-slim

# Set the working directory in the container
WORKDIR /app

# Copy the JAR file from the build image to the runtime image
COPY --from=build /app/target/app_name.jar .

# Specify the command to run the application
CMD ["java", "-jar", "app_name.jar"]
Enter fullscreen mode Exit fullscreen mode

In this step, we can also determine how we want to version the pushed image. In the example below, the timestamp will be used along with the short revision specifier, e.g: 2024.05.13_08-18-37-<git_rev>

      - name: Build and Push Docker Image to AWS ECR
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        run: |
          VERSION="$(date +'%Y.%m.%d_%H-%M-%S')-$(git rev-parse --short HEAD)"
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$VERSION .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$VERSION
          echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$VERSION" >> $GITHUB_OUTPUT
Enter fullscreen mode Exit fullscreen mode

Download task definition

This step will download the existing task-definition file, which contains the necessary information about the container, including the Image ID.

      - name: Download task definition
        run: |
          aws ecs describe-task-definition --task-definition cresh-backend-ecs-task --query taskDefinition > task-definition.json
          echo "Docker Image URI: ${{ steps.build-image.outputs.image }}"
          replacement_image=${{ steps.build-image.outputs.image }}
          json_file="task-definition.json"
          jq --arg replacement_image "$replacement_image" '.containerDefinitions[0].image = $replacement_image' "$json_file" > tmp.json && mv tmp.json "$json_file"
Enter fullscreen mode Exit fullscreen mode

Fill the new Image ID in the Amazon ECS task definition

Replacing the image ID in the downloaded task-definition.json file is necessary.

      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ./task-definition.json
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}
Enter fullscreen mode Exit fullscreen mode

Trigger deployment

Finally, the deployment process takes place by replacing the task definition file with the image ID we want to deploy.

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ./task-definition.json
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true
Enter fullscreen mode Exit fullscreen mode

Summary

The above article provides the necessary instructions needed to create the workflow responsible for deploying the application on AWS ECS and migrating the database using Flyway. What do you think about this? Maybe you know other ways to build such a workflow? Share it in the comments!

Useful links

AWS Github Actions Repo
Information about ECS
Information about ECR

💖 💪 🙅 🚩
gasparini16
Piotr Łagowski

Posted on May 14, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related