Azure DevOps pipeline: Use docker as build environment
Published: 2022-05-19 20:06:12

CI/CD is the most important part of DevOps, and I use Azure DevOps for this. To run DevOps pipeline, we need "DevOps Agent" which does various tasks, such as:

- checkout source
- compile source
- run test
- publish test
- create artifact, etc.

To successfully complete these tasks, we need all dependencies installed on the agent, and Microsoft hosted agent has quite a lot of known dependencies for many languages and platforms. You can find more info [here](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml) about Microsoft hosted agents and what dependencies each agent has.

However, in some scenario, we need more granular controls and we used to use self-hosted agent, which is still valid. However, managing additional VM (or computer) is tedious task. Therefore, I use docker container to achieve the same.

See [here](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/v2-linux?view=azure-devops) about self-hosted agent.

# Create Azure DevOps repo

I won't explain how to as I assume you already use it. I create .NET 6 console app as an example app. This is folder structure.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a96cjoyve9anbdv2yfx4.png)
 
# CI pipeline yaml

I created build pipeline as below.

```yaml
trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- script: |
    docker run --name $(Build.BuildNumber) -d -t mcr.microsoft.com/dotnet/sdk:6.0
    docker cp $(Build.Repository.LocalPath)/ $(Build.BuildNumber):/app
    docker exec $(Build.BuildNumber) bash -c 'cd /app/MyDotNetProject; dotnet publish -c Release -o out'
    docker cp $(Build.BuildNumber):/app/MyDotNetProject/out $(Build.ArtifactStagingDirectory)
  displayName: 'Build in docker and export artifacts'
  
- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'drop'
    publishLocation: 'Container'
```

In this yaml, I did followings:

- Pull and run the docker image to build the code. I use .NET 6 SDK image this time.
- Copy source code into running docker by using its name and path.
- Build the source and copy out the compiled assemblies.
- Publish it to pipeline artifact.

As a result, I see the output as below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prksh416s3cu0tr7y50x.png)

I gave a name to running container so that it's easier to interact with it. I use Build.BuildNumber but you can use any string.

# What does this mean?

I used .NET 6 SDK image this time, but this infers we can create our own docker image which contains all necessary dependencies for our build. We simply need to create such image and push it to somewhere the agent can pull and run.

# When to use self-hosted agent?

As Microsoft-hosted agent will be assigned dynamically, and it won't have docker container cache, it may take time to download the image every time it runs. To save these time, self-hosted agents are useful.

# Summary

In this article, I use CLI to run docker commands, but most commands can be replaced to docker task or, you can write re-usable scripts and call it. The point is we can use container as build environment inside DevOps build pipeline!