Skip to main content
492

Overcoming Azure DevOps Build Agent Issues with Docker

Created
Active
Last edited
Viewed 339 times
1 min read
Part of CI/CD Collective
0

Hi community,

As a developer, I love using Azure DevOps in my projects to create robust CI/CD pipelines. It streamlines workflows and ensures consistent, automated deployment processes.

But recently, I faced a challenge with my Azure DevOps pipeline and builds. My build agent had a broken software update, causing dotnet CLI commands to fail consistently.

However, my .NET builds already used Dockerfiles, following the standard Microsoft-recommended approach. Many of us have seen those files; they look like this: Microsoft .NET Docker Build.

This technique is called multi stage build and it can help us to build more structured Dockerfiles and also optimize a build process. But we can use the same idea not only to optimize docker build, but to make different useful things inside the docker build process.

This got me thinking: if we can abstract away the agent environment setup for the image build, why not use the same technique to run tests? So, I started researching.

Turns out, it's possible! You can run the dotnet test command directly in a Dockerfile like this:

Example Dockerfile

Here’s an example Dockerfile that you can use:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["YOUR_PROJECT.Api/YOUR_PROJECT.Api.csproj", "YOUR_PROJECT.Api/"]
COPY ["YOUR_PROJECT.Tests/YOUR_PROJECT.Tests.csproj", "YOUR_PROJECT.Tests/"]
RUN dotnet restore "YOUR_PROJECT.Api/YOUR_PROJECT.Api.csproj"
COPY . .
RUN dotnet build "YOUR_PROJECT.Api/YOUR_PROJECT.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build
RUN dotnet build "YOUR_PROJECT.Tests/YOUR_PROJECT.Tests.csproj" -c $BUILD_CONFIGURATION -o /app
FROM build AS test
RUN dotnet test -l "trx;logfilename=testResults.trx"; exit 0;
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "YOUR_PROJECT.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "YOUR_PROJECT.Api.dll"]

Pipeline for Extracting Test Results

One challenge with this approach is retrieving the final test results since we can't use Docker volumes in this context. The workaround is to use the docker cp command.

Here's the final azure-pipeline.yml file you can use:

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: 'Build'
  jobs:
   - job: 'Build'
     steps:
     - task: Docker@2
       displayName: Build an image
       inputs:
         command: 'build'
         Dockerfile: './Dockerfile'
         arguments: '--target build'
              
  - job: 'Test'
    steps:
    - task: PowerShell@2
      inputs:
        targetType: inline
        script: |
          docker build --target test -t familybudget:$env:BUILDID .
          docker create -ti --name testcontainer YOUR_PROJECT_NAME:$env:BUILDID
          docker cp testcontainer:/src/YOUR_PROJECT_NAME.Tests/TestResults/ $env:ARTIFACTDIR/testresults
          docker rm -fv testcontainer
      env:
        BUILDID: $(Build.BuildId)
        ARTIFACTDIR: $(Build.ArtifactStagingDirectory)
              
    - task: PublishTestResults@2
      inputs:
        testResultsFormat: 'VSTest'
        testResultsFiles: '**/*.trx'
        searchFolder: '$(Build.ArtifactStagingDirectory)/testresults'
        failTaskOnFailedTests: true

- stage: 'Deploy_DEV'
  dependsOn: Build
  jobs:
  - job: 'Approve'
    pool: server
    steps:
    - task: ManualValidation@0
      displayName: Wait for external validation
      inputs:
        notifyUsers: |
          [email protected]
        instructions: 'Please validate the build configuration and resume'
              
  - job: 'Deploy'
    dependsOn: Approve
    steps:
    - task: CmdLine@2
      inputs:
        script: echo deployed successfully

You can find the full example on GitHub.

Pros and Cons

Pros:

  • No need to worry about the agent environment.

  • Consistent builds.

Cons:

  • Requires creating a container to extract test results.

  • Involves manual steps and a PowerShell script, which can't be done with the built-in DotNetCoreCLI@2 task.

Additional resources