Migrating an Springboot Java API: Part 2 – Azure Build

Migrating an Springboot Java API (4 Part Series)

1 Migrating an Springboot Java API: Part 1 – Docker
2 Migrating an Springboot Java API: Part 2 – Azure Build
3 Migrating an Springboot Java API: Part 3 – AWS ElasticBeanstalk
4 Migrating an Springboot Java API: Part 4 – Azure Release

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
AmazonEC2ContainerRegistryFullAccess
AmazonElasticContainerRegistryPublicFullAccess
EC2InstanceProfileForImageBuilderECRContainerBuilds
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:

<span>#Flyway properties</span>
<span>spring.flyway.enabled=true</span>
<span>spring.flyway.url=jdbc:{{DB_ENGINE}}://{{DB_HOST}}:{{DB_PORT}}/{{DB_NAME}}?characterEncoding=UTF-8</span>
<span>spring.flyway.password={{DB_PASSWORD}}</span>
<span>spring.flyway.user={{DB_USERNAME}}</span>
<span>spring.flyway.schemas={{DB_NAME}}</span>
<span>spring.flyway.locations=classpath:db/migrations</span>
<span>spring.flyway.out-of-order=true</span>
<span>spring.flyway.baseline-on-migrate=true</span>
<span>#Flyway properties</span>
<span>spring.flyway.enabled=true</span>
<span>spring.flyway.url=jdbc:{{DB_ENGINE}}://{{DB_HOST}}:{{DB_PORT}}/{{DB_NAME}}?characterEncoding=UTF-8</span>
<span>spring.flyway.password={{DB_PASSWORD}}</span>
<span>spring.flyway.user={{DB_USERNAME}}</span>
<span>spring.flyway.schemas={{DB_NAME}}</span>
<span>spring.flyway.locations=classpath:db/migrations</span>
<span>spring.flyway.out-of-order=true</span>
<span>spring.flyway.baseline-on-migrate=true</span>
#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

<span>trigger</span><span>:</span>
<span>-</span> <span>main</span>
<span>resources</span><span>:</span>
<span>-</span> <span>repo</span><span>:</span> <span>self</span>
<span>stages</span><span>:</span>
<span>-</span> <span>stage</span><span>:</span> <span>ReplaceVariables</span>
<span>displayName</span><span>:</span> <span>Replace variables</span>
<span>jobs</span><span>:</span>
<span>-</span> <span>job</span><span>:</span> <span>ReplaceAzureVariables</span>
<span>displayName</span><span>:</span> <span>Replace Azure variables</span>
<span>pool</span><span>:</span>
<span>vmImage</span><span>:</span> <span>ubuntu-latest</span>
<span>steps</span><span>:</span>
<span>-</span> <span>task</span><span>:</span> <span>replacetokens@5</span>
<span>displayName</span><span>:</span> <span>Replace Tokens</span>
<span>inputs</span><span>:</span>
<span>targetFiles</span><span>:</span> <span>|</span>
<span>**/application.yml</span>
<span>**/application.properties</span>
<span>encoding</span><span>:</span> <span>'</span><span>auto'</span>
<span>tokenPattern</span><span>:</span> <span>'</span><span>doublebraces'</span>
<span>writeBOM</span><span>:</span> <span>true</span>
<span>actionOnMissing</span><span>:</span> <span>'</span><span>warn'</span>
<span>keepToken</span><span>:</span> <span>false</span>
<span>actionOnNoFiles</span><span>:</span> <span>'</span><span>continue'</span>
<span>enableTransforms</span><span>:</span> <span>false</span>
<span>enableRecursion</span><span>:</span> <span>false</span>
<span>useLegacyPattern</span><span>:</span> <span>false</span>
<span>enableTelemetry</span><span>:</span> <span>false</span>
<span>-</span> <span>task</span><span>:</span> <span>CopyFiles@2</span>
<span>displayName</span><span>:</span> <span>Copy all files</span>
<span>inputs</span><span>:</span>
<span>Contents</span><span>:</span> <span>'</span><span>**'</span>
<span>TargetFolder</span><span>:</span> <span>'</span><span>$(Build.ArtifactStagingDirectory)'</span>
<span>-</span> <span>publish</span><span>:</span> <span>$(Build.ArtifactStagingDirectory)</span>
<span>displayName</span><span>:</span> <span>Publish all files</span>
<span>artifact</span><span>:</span> <span>drop</span>
<span>-</span> <span>stage</span><span>:</span> <span>CustomerDockerBuildPublish</span>
<span>displayName</span><span>:</span> <span>Customer API</span>
<span>dependsOn</span><span>:</span> <span>ReplaceVariables</span>
<span>jobs</span><span>:</span>
<span>-</span> <span>job</span><span>:</span> <span>Build_and_Push</span>
<span>displayName</span><span>:</span> <span>Customer - Build & Push Docker image</span>
<span>pool</span><span>:</span>
<span>vmImage</span><span>:</span> <span>ubuntu-latest</span>
<span>steps</span><span>:</span>
<span># Skip source code checkout and reuse sources</span>
<span>-</span> <span>checkout</span><span>:</span> <span>none</span>
<span># Download the artifact from the ReplaceVariables stage</span>
<span>-</span> <span>task</span><span>:</span> <span>DownloadPipelineArtifact@2</span>
<span>inputs</span><span>:</span>
<span>buildType</span><span>:</span> <span>'</span><span>current'</span>
<span>artifact</span><span>:</span> <span>drop</span>
<span>targetPath</span><span>:</span> <span>'</span><span>$(Build.SourcesDirectory)'</span>
<span>-</span> <span>task</span><span>:</span> <span>Docker@2</span>
<span>displayName</span><span>:</span> <span>Customer - Build Customer API Docker image</span>
<span>inputs</span><span>:</span>
<span>command</span><span>:</span> <span>build</span>
<span>dockerfile</span><span>:</span> <span>'</span><span>./Customer.Dockerfile'</span>
<span>buildContext</span><span>:</span> <span>'</span><span>$(Build.SourcesDirectory)'</span>
<span>repository</span><span>:</span> <span>$(CUSTOMER_DOCKER_REPOSITORY_NAME)</span>
<span>tags</span><span>:</span> <span>|</span>
<span>$(Build.BuildNumber)</span>
<span>-</span> <span>task</span><span>:</span> <span>ECRPushImage@1</span>
<span>displayName</span><span>:</span> <span>Customer - Push Admin API Docker image</span>
<span>inputs</span><span>:</span>
<span>awsCredentials</span><span>:</span> <span>'</span><span>my-azure-service-connection'</span>
<span>regionName</span><span>:</span> <span>'</span><span>$(AWS_REGION)'</span>
<span>imageSource</span><span>:</span> <span>'</span><span>imagename'</span>
<span>sourceImageName</span><span>:</span> <span>'</span><span>$(CUSTOMER_DOCKER_REPOSITORY_NAME)'</span>
<span>sourceImageTag</span><span>:</span> <span>'</span><span>$(Build.BuildNumber)'</span>
<span>pushTag</span><span>:</span> <span>'</span><span>$(Build.BuildNumber)'</span>
<span>repositoryName</span><span>:</span> <span>'</span><span>$(CUSTOMER_DOCKER_REPOSITORY_NAME)'</span>
<span>logRequest</span><span>:</span> <span>true</span>
<span>logResponse</span><span>:</span> <span>true</span>
<span>-</span> <span>stage</span><span>:</span> <span>AdminDockerBuildPublish</span>
<span>displayName</span><span>:</span> <span>Admin API</span>
<span>dependsOn</span><span>:</span> <span>ReplaceVariables</span>
<span>jobs</span><span>:</span>
<span>-</span> <span>job</span><span>:</span> <span>Build_and_Push</span>
<span>displayName</span><span>:</span> <span>Admin - Build & Push Docker image</span>
<span>pool</span><span>:</span>
<span>vmImage</span><span>:</span> <span>ubuntu-latest</span>
<span>steps</span><span>:</span>
<span># Skip source code checkout and reuse sources</span>
<span>-</span> <span>checkout</span><span>:</span> <span>none</span>
<span># Download the artifact from the ReplaceVariables stage</span>
<span>-</span> <span>task</span><span>:</span> <span>DownloadPipelineArtifact@2</span>
<span>inputs</span><span>:</span>
<span>buildType</span><span>:</span> <span>'</span><span>current'</span>
<span>artifact</span><span>:</span> <span>drop</span>
<span>targetPath</span><span>:</span> <span>'</span><span>$(Build.SourcesDirectory)'</span>
<span>-</span> <span>task</span><span>:</span> <span>Docker@2</span>
<span>displayName</span><span>:</span> <span>Admin - Build Admin API Docker image</span>
<span>inputs</span><span>:</span>
<span>command</span><span>:</span> <span>build</span>
<span>dockerfile</span><span>:</span> <span>'</span><span>./Admin.Dockerfile'</span>
<span>buildContext</span><span>:</span> <span>'</span><span>$(Build.SourcesDirectory)'</span>
<span>repository</span><span>:</span> <span>$(ADMIN_DOCKER_REPOSITORY_NAME)</span>
<span>tags</span><span>:</span> <span>|</span>
<span>$(Build.BuildNumber)</span>
<span>-</span> <span>task</span><span>:</span> <span>ECRPushImage@1</span>
<span>displayName</span><span>:</span> <span>Admin - Push Admin API Docker image</span>
<span>inputs</span><span>:</span>
<span>awsCredentials</span><span>:</span> <span>'</span><span>my-azure-service-connection'</span>
<span>regionName</span><span>:</span> <span>'</span><span>$(AWS_REGION)'</span>
<span>imageSource</span><span>:</span> <span>'</span><span>imagename'</span>
<span>sourceImageName</span><span>:</span> <span>'</span><span>$(ADMIN_DOCKER_REPOSITORY_NAME)'</span>
<span>sourceImageTag</span><span>:</span> <span>'</span><span>$(Build.BuildNumber)'</span>
<span>pushTag</span><span>:</span> <span>'</span><span>$(Build.BuildNumber)'</span>
<span>repositoryName</span><span>:</span> <span>'</span><span>$(ADMIN_DOCKER_REPOSITORY_NAME)'</span>
<span>logRequest</span><span>:</span> <span>true</span>
<span>logResponse</span><span>:</span> <span>true</span>
<span>trigger</span><span>:</span>
  <span>-</span> <span>main</span>

<span>resources</span><span>:</span>
  <span>-</span> <span>repo</span><span>:</span> <span>self</span>

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

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

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

        <span>-</span> <span>publish</span><span>:</span> <span>$(Build.ArtifactStagingDirectory)</span>
          <span>displayName</span><span>:</span> <span>Publish all files</span>
          <span>artifact</span><span>:</span> <span>drop</span>

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

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

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

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

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

          <span>-</span> <span>task</span><span>:</span> <span>ECRPushImage@1</span>
            <span>displayName</span><span>:</span> <span>Admin - Push Admin API Docker image</span>
            <span>inputs</span><span>:</span>
              <span>awsCredentials</span><span>:</span> <span>'</span><span>my-azure-service-connection'</span>
              <span>regionName</span><span>:</span> <span>'</span><span>$(AWS_REGION)'</span>
              <span>imageSource</span><span>:</span> <span>'</span><span>imagename'</span>
              <span>sourceImageName</span><span>:</span> <span>'</span><span>$(ADMIN_DOCKER_REPOSITORY_NAME)'</span>
              <span>sourceImageTag</span><span>:</span> <span>'</span><span>$(Build.BuildNumber)'</span>
              <span>pushTag</span><span>:</span> <span>'</span><span>$(Build.BuildNumber)'</span>
              <span>repositoryName</span><span>:</span> <span>'</span><span>$(ADMIN_DOCKER_REPOSITORY_NAME)'</span>
              <span>logRequest</span><span>:</span> <span>true</span>
              <span>logResponse</span><span>:</span> <span>true</span>
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:

Amazon ECR

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

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.

Migrating an Springboot Java API (4 Part Series)

1 Migrating an Springboot Java API: Part 1 – Docker
2 Migrating an Springboot Java API: Part 2 – Azure Build
3 Migrating an Springboot Java API: Part 3 – AWS ElasticBeanstalk
4 Migrating an Springboot Java API: Part 4 – Azure Release

原文链接:Migrating an Springboot Java API: Part 2 – Azure Build

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
Don't give up just because of what people said. Use that as your motivation to push harder.
别因为别人说的话而放弃,把那些话当做加倍努力的动力
评论 抢沙发

请登录后发表评论

    暂无评论内容