Building .NET projects on jetbrains/teamcity-agent Docker Windows containers

Hi,

we are trying to move from "traditional" build agents hosted in VMs to using Docker. The hole product is run on Windows, thus forcing us to use Windows containers. We have done some exploratory testing using a TeamCity docker agent to obviously host an agent but also to build a .NET application. Here comes the issue. First build works fine, the second fails directly because it cannot remove a binary from the obj-folder from the previous build. If I try to remove it myself I get a prompt saying it is locked by another process, vmwp.exe more specifically. Restarting the container solves the problem but is obviously not a viable solution.

I have tried the following without success

  • Removing any antivirus-program that could cause this. I have read that vmwp.exe could get this behavior with antivirus-programs.
  • Tried to run the docker image in process isolation but for that I need the host to have the same OS version. This might be solved we would create a VM with the exact version as the container. For reference, I get the error "The container operating system does not match the host operating system." from the Docker daemon.

Versions used:

  • TeamCity Docker agent: jetbrains/teamcity-agent:2022.04.3-windowsservercore
  • TeamCity Server: TeamCity Enterprise 2022.04.3 (build 108706)
  • Host OS: Microsoft Windows [Version 10.0.19044.2075]
  • Docker: Docker Desktop 4.12.0 (85629) 

Alternative solution:

We have looked into using TeamCity support in certain runner to use Docker images for build steps. This might be a much better solution with some caveats:

  1. Running one build step for .NET build in Docker, mcr.microsoft.com/dotnet/sdk:6.0.100-nanoserver-20H2, adds an overhead run-time of about 15 seconds. This means that a simple build that earlier took 15 seconds now takes about 30 seconds using Docker Wrapper (version 108706). That is unreasonable and I am not sure if it can be mitigated somehow.
  2. We use for instance the sonnarScanner for SonarQube analysis. This requires steps before and after the build step. Only doing the build step in Docker does not work.

Questions:

  • I am really not sure this is the best way to go about this. What is your recommended way of doing this? How can we move from clunky VMs to a more slim and controlled build environment?
  • Do you have any idea why running a build step in docker container (mcr.microsoft.com/dotnet/sdk:6.0.100-nanoserver-20H2) is so much slower? It certainly seems unreasonable. (see Point 1 under Alternative solution)

Would be great to hear your thoughts!

Thank you,

Patrik Hillbo

Dev. Engineer

2
3 comments

Hi Patrik,

I believe you are experiencing this bug: https://github.com/moby/moby/issues/42803. If using --isolation=process is an option, you can fail back to it as a workaround until the moby bug is fixed. Here are a few other things to consider:

  1. Removing the agent container after the first build and starting a new one for the next build. Kubernetes cloud plugin (by JetBrains) or Docker cloud plugin (3rd-party) can help achieve that. The downside is that the build time would increase.
  2. Running Docker on a Linux host. That should eliminate the bug, although you will not be able to run Windows containers. That can be a viable solution if your builds do not require Windows agents.
  3. Running SonarScanner and dotnet build in the same container using Docker Wrapper should be possible, although both will need to be run in a single Command Line step. That means you will not be able to use TeamCity's SonarScanner and .NET integration benefits.

As for the increased build step duration when using Docker Wrapper, the image will need to be pulled if it is not present on the agent. That can notably increase the build duration. However, the subsequent builds on the same agent should be faster (unless Pull image explicitly is ticked in the build step settings).

Cheers,
Anatoly
Support Engineer, TeamCity

0

Hi Anatoly,

Thank you for the advice. We will try the --isolation=process option later on. And option 3 is the one we are currently aiming for. The possibilities with Docker Wrapper are greater as each step can be done either in Docker or on the host.

Then regarding Docker Wrapper build step duration. I have pulled the image beforehand and Pull image explicitly is not ticked.

The Docker info tab for the Docker Wrapper build configuration looks quite interesting. Why is the container created and destroyed three times? Looking at the build log the .NET build is begun after the container is created the third and last time at 09.22.40. Contents from the Docker info tab:

12 Oct 22 09:22:33 Container 32744e0a5d13… CREATE ; from: mcr.microsoft.com/dotnet/sdk:6.0.100-nanoserver-20H2
09:22:33 Container 32744e0a5d13… ATTACH
09:22:35 Container 32744e0a5d13… START
09:22:36 Container 32744e0a5d13… DIE
09:22:36 Container 32744e0a5d13… DESTROY
09:22:36 Container eff877ed1afc… CREATE ; from: mcr.microsoft.com/dotnet/sdk:6.0.100-nanoserver-20H2
09:22:37 Container eff877ed1afc… ATTACH
09:22:38 Container eff877ed1afc… START
09:22:40 Container eff877ed1afc… DIE
09:22:40 Container eff877ed1afc… DESTROY
09:22:40 Container 650ded34c103… CREATE ; from: mcr.microsoft.com/dotnet/sdk:6.0.100-nanoserver-20H2
09:22:40 Container 650ded34c103… ATTACH
09:22:42 Container 650ded34c103… START
09:22:53 Container 650ded34c103… DIE
09:22:53 Container 650ded34c103… DESTROY
 
Thanks!
Patrik

 

0

For anyone else wondering, this is a problem specific to the .NET runner and Docker Wrapper combination. Related bug: https://youtrack.jetbrains.com/issue/TW-78117

0

Please sign in to leave a comment.