DEV Community

Laura
Laura

Posted on • Edited on

Migrating an Springboot Java API: Part 2 - Azure Build

On the previous post I dockerized an Java 1.8 Springboot solution.
In this guide I will show how I used Azure DevOps pipelines to build and push images into AWS.

Pre-requisites

IAM role

In order to allow my Azure builds to push images into AWS, I created an IAM role with the following permission policies:

AmazonEC2ContainerRegistryFullAccess
AmazonElasticContainerRegistryPublicFullAccess
EC2InstanceProfileForImageBuilderECRContainerBuilds
Enter fullscreen mode Exit fullscreen mode

Changing the hard-coded configurations with variables

In the configuration files of the project, application.yml and application.properties I then substituted it for the variables I created in my Variable groups.
For example, for the Flyway database migration configuration in the application.properties looks like:

#Flyway properties
spring.flyway.enabled=true
spring.flyway.url=jdbc:{{DB_ENGINE}}://{{DB_HOST}}:{{DB_PORT}}/{{DB_NAME}}?characterEncoding=UTF-8
spring.flyway.password={{DB_PASSWORD}}
spring.flyway.user={{DB_USERNAME}}
spring.flyway.schemas={{DB_NAME}}
spring.flyway.locations=classpath:db/migrations
spring.flyway.out-of-order=true
spring.flyway.baseline-on-migrate=true
Enter fullscreen mode Exit fullscreen mode

In the future, the solution will use environment variables instead of replacing them.

Azure pipeline configuration

I then went to Azure -> Pipelines -> New pipeline -> Starter Pipeline
I have used the Variable Groups in Azure Library to hold most of the configuration
In the azure-pipelines.yml file they substitute the fields in the $(...) format. In the code, I added a custom task to replace the fields in the {{...}} format

trigger:
  - main

resources:
  - repo: self

stages:
  - stage: ReplaceVariables
    displayName: Replace variables
    jobs:
      - job: ReplaceAzureVariables
        displayName: Replace Azure variables
        pool:
          vmImage: ubuntu-latest
        steps:
        - task: replacetokens@5
          displayName: Replace Tokens
          inputs:
            targetFiles: |
              **/application.yml
              **/application.properties

            encoding: 'auto'
            tokenPattern: 'doublebraces'
            writeBOM: true
            actionOnMissing: 'warn'
            keepToken: false
            actionOnNoFiles: 'continue'
            enableTransforms: false
            enableRecursion: false
            useLegacyPattern: false
            enableTelemetry: false

        - task: CopyFiles@2
          displayName: Copy all files 
          inputs:
            Contents: '**'
            TargetFolder: '$(Build.ArtifactStagingDirectory)'

        - publish: $(Build.ArtifactStagingDirectory)
          displayName: Publish all files
          artifact: drop

  - stage: CustomerDockerBuildPublish
    displayName: Customer API
    dependsOn: ReplaceVariables
    jobs:
      - job: Build_and_Push
        displayName: Customer - Build & Push Docker image
        pool:
          vmImage: ubuntu-latest
        steps:
          # Skip source code checkout and reuse sources
          - checkout: none
          # Download the artifact from the ReplaceVariables stage
          - task: DownloadPipelineArtifact@2
            inputs:
              buildType: 'current'
              artifact: drop
              targetPath: '$(Build.SourcesDirectory)'

          - task: Docker@2
            displayName: Customer - Build Customer API Docker image
            inputs:
              command: build
              dockerfile: './Customer.Dockerfile'
              buildContext: '$(Build.SourcesDirectory)'
              repository: $(CUSTOMER_DOCKER_REPOSITORY_NAME)
              tags: |
                $(Build.BuildNumber)

          - task: ECRPushImage@1
            displayName: Customer - Push Admin API Docker image
            inputs:
              awsCredentials: 'my-azure-service-connection'
              regionName: '$(AWS_REGION)'
              imageSource: 'imagename'
              sourceImageName: '$(CUSTOMER_DOCKER_REPOSITORY_NAME)'
              sourceImageTag: '$(Build.BuildNumber)'
              pushTag: '$(Build.BuildNumber)'
              repositoryName: '$(CUSTOMER_DOCKER_REPOSITORY_NAME)'
              logRequest: true
              logResponse: true

  - stage: AdminDockerBuildPublish
    displayName: Admin API
    dependsOn: ReplaceVariables
    jobs:
      - job: Build_and_Push
        displayName: Admin - Build & Push Docker image
        pool:
          vmImage: ubuntu-latest
        steps:
          # Skip source code checkout and reuse sources
          - checkout: none
          # Download the artifact from the ReplaceVariables stage
          - task: DownloadPipelineArtifact@2
            inputs:
              buildType: 'current'
              artifact: drop
              targetPath: '$(Build.SourcesDirectory)'

          - task: Docker@2
            displayName: Admin - Build Admin API Docker image
            inputs:
              command: build
              dockerfile: './Admin.Dockerfile'
              buildContext: '$(Build.SourcesDirectory)'
              repository: $(ADMIN_DOCKER_REPOSITORY_NAME)
              tags: |
                $(Build.BuildNumber)

          - task: ECRPushImage@1
            displayName: Admin - Push Admin API Docker image
            inputs:
              awsCredentials: 'my-azure-service-connection'
              regionName: '$(AWS_REGION)'
              imageSource: 'imagename'
              sourceImageName: '$(ADMIN_DOCKER_REPOSITORY_NAME)'
              sourceImageTag: '$(Build.BuildNumber)'
              pushTag: '$(Build.BuildNumber)'
              repositoryName: '$(ADMIN_DOCKER_REPOSITORY_NAME)'
              logRequest: true
              logResponse: true
Enter fullscreen mode Exit fullscreen mode

The pipeline build run should look like this:

Azure pipeline

Amazon ECR

When the build is successful, it will push a new Docker image to ECR with the Build.BuildNumber as the image tag:

Amazon ECR examples

This pipeline is set to run after each commit to main branch. The images are now ready to be deployed into an environment.

In the next post of the series I will show how I used Amazon Elastic Beanstalk to orchestrate and run the API images.

Top comments (0)