GitHub Actions for EKS Deployment

GitHub Actions for EKS Deployment

Create CI CD Pipeline for Scala App using GitHub Actions

GitHub Actions is a powerful automation tool that allows developers to create custom workflows to automate their software development processes. With GitHub Actions, developers can define a series of steps to run when specific events occur, such as code push, pull requests, and issue creation. These steps can include building and testing code, deploying applications, and sending notifications. Additionally, there are a wide variety of pre-built actions available in the GitHub Marketplace that can be incorporated into workflows to further streamline the development process

In this blog post, we will be covering the following tasks.

  • Create a CI pipeline for scala app

    • The CI pipeline workflow will run various unit tests on the code
  • Create CD pipeline for app deployment

    • The pipeline workflow will build an image

    • The pipeline workflow will push the image to ECR

    • The pipeline workflow will then deploy the latest image with commit tag to EKS cluster

Create CI pipeline for continuous integration

  1. In your repository, create the .github/workflows/ directory to store your workflow files.

  2. In the .github/workflows/ directory, create a new file called check-scala-app.yml and add the following code.

# **What it does**: Checks that the code pushed passes all the required checks.
# **Why we have it**: To make sure pushed code can only be merged when all the required checks passed.
# **Author**: Saumya Pandey.
name: CI Check Scala App Service

on:
  pull_request:
    branches:
      - staging
      - develop

env:
  ORG: Saumya Inc
  PROJECT_NAME: Sample Scala App
  AUTHOR: Saumya (SaumyaBhushan)
jobs:
  check_scala_app_service:
    env:
      MODULE_NAME: scala_app_service
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11

      - name: Cache SBT
        uses: actions/cache@v2
        with:
          path: |
            ~/.ivy2/cache
            ~/.sbt
          key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }}

      - name: Run test cases and generate reports for Scala-App-Service
        env:
          URL: "jdbc:h2:mem:test;MODE=Oracle;"
          DRIVER: "org.h2.Driver"
          USERNAME:
          PASSWORD:
        run: |
          sudo timedatectl set-timezone Asia/Kolkata
          echo "...........Compiling the code .........."
          sbt clean compile
      - name: check Scala format
        run : |
          echo "...........Checking Scalafmt ........"
          sbt scalafmtAll
      - name: check Scalstyle
        run: |
          echo "...........Checking Scalastyle ........"
          sbt scalastyle
      - name: check copy paste detector
        run: |
          echo "...........Checking copy paste detector ........"
          sbt cpd
      - name: check scapegoat
        run: |
          echo "...........Checking Scapegoat ..........."
          sbt scapegoat
      - name: check test coverage and create coverage report
        run: |
          echo "...........Checking Test Coverage and Coverage Report.........."
          sbt coverage test coverageReport

This is a GitHub Actions workflow file written in YAML. It is used to define a CI (continuous integration) process for a Scala app project. Let's break down each keyword and code line by line:

  • name: This keyword specifies the name of the workflow, which is "CI Check Scala App Service".

  • on: This keyword specifies the events that trigger the workflow. In this case, it is triggered when a pull request is created or updated in the "staging" or "develop" branches.

  • env: This is used to set environment variables that can be accessed by the steps in the workflow.

  • jobs: This defines the set of jobs that will be executed by the workflow.

    • check_scala_app_service: This is the name of the job, which is "check_scala_app_service" in this case.

      • runs-on: This keyword specifies the type of machine that will run the job. In this case, it is "ubuntu-latest".

      • steps: This defines the set of steps that will be executed by the job. One workflow can have multiple steps

        • uses: This step is used to checkout the source code of the project from the repository using the "actions/checkout@v2" action.

        • uses: This step uses the "actions/setup-java@v1" action to install JDK 11 on the machine.

        • with: This keyword specifies the configuration options for the "actions/setup-java@v1" action, which in this case is setting the "java-version" to 11.

        • name: This keyword specifies the name of the step, which is "Cache SBT" in this case.

        • uses: This step uses the "actions/cache@v2" action to cache the SBT dependencies.

        • with: This keyword specifies the configuration options for the "actions/cache@v2" action, which in this case is caching the "/.ivy2/cache" and "/.sbt" directories.

        • key: This keyword specifies the key for the cache, which is generated based on the OS and the hash of the "build.sbt" file.

      • run: This keyword specifies the shell command that will be executed by the step. In this case, it sets the timezone, cleans and compiles the code using sbt.

  • The workflow performs several checks on the code, including compiling the code, checking the Scala format using sbt scalafmtAll, checking Scala style using sbt scalastyle, checking for copy-paste code using sbt cpd, checking for code quality issues using sbt scapegoat, and checking the test coverage and generating the coverage report using sbt coverage test coverageReport.

Create CD pipeline for Continuous Deployment

Similarly, as we did above go to your workflow directory .github/workflows/ , create new file deploy-scala-app.yml

# **What it does**: Build docker image and push it to Elastic Container Registry.
# **Why we have it**: Scala App needs to be build as an image and store at remote private registry
# **Author: Saumya Bhushan

name: Build and Deploy Scala App Service On Dev
on:
  push:
    branches:
      - develop

jobs:
  build-scala-app-service-on-develop:
    env:
      EKS_CLUSTER: test-eks-dev
    name: push image to scala-app-service repo (ECR)
    runs-on: ubuntu-18.04
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      # Setup JDK 11
      - name: Set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 11

      - name: Cache SBT
        uses: actions/cache@v2
        with:
          path: |
            ~/.ivy2/cache
            ~/.sbt
          key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }}

      - name: Configure AWS credentials
        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: us-east-2

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      #  Generate Commit Id
      - name: Generate Commit Id
        id: hash-id
        run: echo "::set-output name=git_hash::$(git rev-parse --short "$GITHUB_SHA")"

      # Build the Docker image
      - name: Package and Build Backend
        run: |
          echo "Building image..."
          sbt docker:publishLocal
      # Tag, and push image to Amazon ECR
      - name: Build, tag, and push image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: scala-app-service
          IMAGE_TAG: ${{ steps.hash-id.outputs.git_hash }}
        run: |
          echo "Pushing image to ECR..."
          docker tag sacla-app:1.0-SNAPSHOT $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

    outputs:
      output-id: ${{ steps.hash-id.outputs.git_hash }}

  deploy-on-eks-develop:
    needs: build-scala-app-service-on-develop
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Configure AWS credentials
        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: us-east-2

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: deploy to cluster
        uses: kodermax/kubectl-aws-eks@master
        env:
          KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA_DEV }}
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: scala-app-service
          IMAGE_TAG: ${{needs.build-registration-service-on-develop.outputs.output-id}}
          KUBECTL_VERSION: "v1.23.0"
        with:
          args: set image deployment/$ECR_REPOSITORY $ECR_REPOSITORY=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -n scala-app-service

      - name: restart the deployment
        uses: kodermax/kubectl-aws-eks@master
        env:
          KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA_DEV }}
          KUBECTL_VERSION: "v1.23.0"
        with:
          args: rollout restart deployment/scala-app-service -n scala-app-service

      - name: verify deployment
        uses: kodermax/kubectl-aws-eks@master
        env:
          KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA_DEV }}
        with:
          args: rollout status deployment/registration-service -n scala-app-service

This is a GitHub Actions workflow file written in YAML that automates the building and deployment of a Scala app service to an EKS cluster. The workflow is triggered on a push to the develop branch. The workflow consists of two jobs: build-scala-app-service-on-develop and deploy-on-eks-develop.

The first job, build-scala-app-service-on-develop, runs on an Ubuntu 18.04 runner and performs the following steps:

  1. Checks out the code from the repository.

  2. Sets up JDK 11.

  3. Caches SBT to speed up builds.

  4. Configures AWS credentials.

  5. Logs in to Amazon ECR.

  6. Generates a commit ID.

  7. Builds a Docker image and tags it with the commit ID.

  8. Pushes the Docker image to Amazon ECR.

  9. Outputs the commit ID.

The second job, deploy-on-eks-develop, runs on an Ubuntu latest runner and performs the following steps:

  1. Checks out the code from the repository.

  2. Configures AWS credentials.

  3. Logs in to Amazon ECR.

  4. Deploys the Docker image to the EKS cluster using kubectl.

  5. Restarts the deployment.

  6. Verifies the deployment.

To run this CD workflow successfully you need to set the following credentials in secret tab :

  • AWS_ACCESS_KEY_ID

  • AWS_SECRET_ACCESS_KEY

  • KUBE_CONFIG_DATA_DEV

To set the value go to your repo and go to Settings Tab

Then in the left panel go to Secrets and variables and then click on Actions

Then from here click on New Repository Secret

Add the Secret Name and Secret Value and click on Add secret . Similarly, add all the secret.

Now all your secrets are configured. Make the required changes in the deploy-scala-app.yml such as EKS_CLUSTER name, ECR_REPOSITORY name KUBECTL_VERSION .

  • set image deployment/$ECR_REPOSITORY $ECR_REPOSITORY=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -n scala-app-service: change the scala-app-service to your namespace name where you need to deploy the image

  • rollout restart deployment/registration-service -n scala-app-service : Here also change the deployment name and namespace name according to your setting.

  • rollout status deployment/registration-service -n scala-app-service : same as above

sbt docker:publishLocal
This command use docker plugin for sbt to create docker image without dockerfile . If you have dockerfile in your git repo then you can use docker build command to build image.

Now all set up whenever you make a change to your pull request or create a new pull request these workflows will run.

Note : CI workflow will run when there is a new pull request created from the staging or develop branch or if there is any change in the existing Pull Request created on this branch

CD workflow will run when code gets merged or pushed on develop branch

You can change the branch name according to your use case.

Hope you liked this blog. If you want to get more such blogs subscribe to my newsletter and follow me on hashnode and twitter.

References :

https://github.com/features/actions

https://www.scala-sbt.org/sbt-native-packager/formats/docker.html

https://github.com/kodermax/kubectl-aws-eks

Did you find this article valuable?

Support DevOps Talks by becoming a sponsor. Any amount is appreciated!