I am attempting to deploy two .Net Core applications to Azure Kubernetes using DevOps. The default pipeline only builds and deploys the first project I've added.
I have created a single solution with two solution folders, each containing a .Net Core project. I have added orchestration support to both projects and created an Azure pipeline using the "wizard" in DevOps. I have added the secondary project after successfully deploying the first project. I thought that the issue exists because the Docker Registry Service Connection did not contain the new project, so I've deleted that and re-created the connection, but it still only deploys the first project. I am using Azure Git.
The azure-pipeline.yaml is pretty standard. I have accepted the default that was created when the pipeline was created. Here is my buildAndPush stage.
stages:
- stage: Build
displayName: Build stage
jobs:
- job: Build
displayName: Build job
pool:
vmImage: $(vmImageName)
steps:
- task: Docker@2
displayName: Build and push an image to container registry
inputs:
command: buildAndPush
repository: $(imageRepository)
dockerfile: $(dockerfilePath)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'manifests'
targetPath: 'manifests'
How do I get it to build and deploy the other remaining project?
I've managed to deploy two projects using a single azure-pipelines.yml file; however, i am almost certain it's incorrect. I have duplicated the Build Stage for each project and also specified two seperate Dockerfiles. In addition, i have added a deployment.yml and service.yml file for each project. See my azure-pipelines.yml below. Any advice on how to do this correctly will be appreciated.
# Deploy to Azure Kubernetes Service
# Build and push image to Azure Container Registry; Deploy to Azure Kubernetes Service
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
trigger:
- master
resources:
- repo: self
variables:
# Container registry service connection established during pipeline creation
dockerRegistryServiceConnection: '<hidden>'
imageRepository1: 'k8spocfront'
imageRepository2: 'k8spocback'
containerRegistry: '<hidden>.azurecr.io'
dockerfilePath1: 'k8sPOC/Dockerfile'
dockerfilePath2: 'k8sPOCApi/Dockerfile'
tag: '$(Build.BuildId)'
# Kubernetes Namespace
k8sNamespace: 'default'
imagePullSecret: '<hidden>'
# Agent VM image name
vmImageName: 'ubuntu-latest'
stages:
- stage: Build_FrontEnd
displayName: Build stage 1
jobs:
- job: Build
displayName: Build job
pool:
vmImage: $(vmImageName)
steps:
- task: Docker@2
displayName: Build and push an image to container registry
inputs:
command: buildAndPush
repository: $(imageRepository1)
dockerfile: $(dockerfilePath1)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
- stage: Build_BackEnd
displayName: Build stage 2
jobs:
- job: Build
displayName: Build job
pool:
vmImage: $(vmImageName)
steps:
- task: Docker@2
displayName: Build and push an image to container registry
inputs:
command: buildAndPush
repository: $(imageRepository2)
dockerfile: $(dockerfilePath2)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'manifests'
targetPath: 'manifests'
- stage: Deploy_FrontEnd
displayName: Deploy stage
dependsOn: Build_BackEnd
jobs:
- deployment: Deploy
displayName: Deploy job
pool:
vmImage: $(vmImageName)
environment: '<hidden>.default'
strategy:
runOnce:
deploy:
steps:
- task: DownloadPipelineArtifact@1
inputs:
artifactName: 'manifests'
downloadPath: '$(System.ArtifactsDirectory)/manifests'
- task: KubernetesManifest@0
displayName: Create imagePullSecret
inputs:
action: createSecret
secretName: $(imagePullSecret)
namespace: $(k8sNamespace)
dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
- task: KubernetesManifest@0
displayName: Deploy to Kubernetes cluster
inputs:
action: deploy
namespace: $(k8sNamespace)
manifests: |
$(System.ArtifactsDirectory)/manifests/deployment1.yml
$(System.ArtifactsDirectory)/manifests/service1.yml
$(System.ArtifactsDirectory)/manifests/deployment2.yml
$(System.ArtifactsDirectory)/manifests/service2.yml
imagePullSecrets: |
$(imagePullSecret)
containers: |
$(containerRegistry)/$(imageRepository1):$(tag)
$(containerRegistry)/$(imageRepository2):$(tag)
You need to delete the azure-pipelines.yml file from your repository should you wish to create a new pipeline. This will allow you to step through the Devops "Deploy to Azure Kubernetes Service" again from the beginning. This should give you the opportunity to add any additional projects to the release.
You need a different build pipeline for each solution. my suggestion is to put both project under one solution. You can have any different type of projects under the same solution.
I haven't tried it myself but you could take advantage of the matrix
strategy.
Jobs will get duplicated and run in parallel, each building and pushing a different image.
Using your yaml, it would look like this:
# Deploy to Azure Kubernetes Service
# Build and push image to Azure Container Registry; Deploy to Azure Kubernetes Service
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
trigger:
- master
resources:
- repo: self
variables:
# Container registry service connection established during pipeline creation
dockerRegistryServiceConnection: '<hidden>'
imageRepository1: 'k8spocfront'
imageRepository2: 'k8spocback'
containerRegistry: '<hidden>.azurecr.io'
dockerfilePath1: 'k8sPOC/Dockerfile'
dockerfilePath2: 'k8sPOCApi/Dockerfile'
tag: '$(Build.BuildId)'
# Kubernetes Namespace
k8sNamespace: 'default'
imagePullSecret: '<hidden>'
# Agent VM image name
vmImageName: 'ubuntu-latest'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: Build
displayName: Build job
strategy:
matrix:
image1:
imageRepository: $(imageRepository1)
dockerfilePath: $(dockerfilePath1)
image2:
imageRepository: $(imageRepository2)
dockerfilePath: $(dockerfilePath2)
maxParallel: 2
pool:
vmImage: $(vmImageName)
steps:
- task: Docker@2
displayName: Build and push an image to container registry
inputs:
command: buildAndPush
repository: $(imageRepository)
dockerfile: $(dockerfilePath)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
- task: PublishPipelineArtifact@0
inputs:
artifactName: 'manifests'
targetPath: 'manifests'
condition: and(succeeded(), eq(variables['imageRepository'], $(imageRepository1)))
- stage: Deploy_FrontEnd
displayName: Deploy stage
dependsOn: Build_BackEnd
jobs:
- deployment: Deploy
displayName: Deploy job
pool:
vmImage: $(vmImageName)
environment: '<hidden>.default'
strategy:
runOnce:
deploy:
steps:
- task: DownloadPipelineArtifact@1
inputs:
artifactName: 'manifests'
downloadPath: '$(System.ArtifactsDirectory)/manifests'
- task: KubernetesManifest@0
displayName: Create imagePullSecret
inputs:
action: createSecret
secretName: $(imagePullSecret)
namespace: $(k8sNamespace)
dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
- task: KubernetesManifest@0
displayName: Deploy to Kubernetes cluster
inputs:
action: deploy
namespace: $(k8sNamespace)
manifests: |
$(System.ArtifactsDirectory)/manifests/deployment1.yml
$(System.ArtifactsDirectory)/manifests/service1.yml
$(System.ArtifactsDirectory)/manifests/deployment2.yml
$(System.ArtifactsDirectory)/manifests/service2.yml
imagePullSecrets: |
$(imagePullSecret)
containers: |
$(containerRegistry)/$(imageRepository1):$(tag)
$(containerRegistry)/$(imageRepository2):$(tag)
As you can see, I've used only one stage for building the Docker images. I've noticed you publish the back-end manifest as a pipeline artifact so I kept it and added a condition so the publish task will only run when it should.