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
- Azure DevOps agents (I used
ubuntu-latest
pools) - An AWS account with ECR (Elastic Container Registry) access
- Setup AWS Toolkit for Microsoft Azure DevOps
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
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
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
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.
Top comments (0)