Visual Studio docker support doesn't work with CI/CD - visual-studio

I have a solution that has the following projects:
ServiceStackDockerTest
ServiceStackDockerTest.ServiceModel
ServiceStackDockerTest.ServiceInterface
When I add docker support inside visual studio this is the docker file that is added.
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ServiceStackDockerTest/ServiceStackDockerTest.csproj ServiceStackDockerTest/
COPY ServiceStackDockerTest.ServiceModel/ServiceStackDockerTest.ServiceModel.csproj ServiceStackDockerTest.ServiceModel/
COPY ServiceStackDockerTest.ServiceInterface/ServiceStackDockerTest.ServiceInterface.csproj ServiceStackDockerTest.ServiceInterface/
RUN dotnet restore ServiceStackDockerTest/ServiceStackDockerTest.csproj
COPY . .
WORKDIR /src/ServiceStackDockerTest
RUN dotnet build ServiceStackDockerTest.csproj -c Release -o /app
FROM build AS publish
RUN dotnet publish ServiceStackDockerTest.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "ServiceStackDockerTest.dll"]
It adds the docker file inside the startup project (/ServiceStackDockerTest).
This works perfectly fine inside visual studio. The image builds and runs when I debug and my breakpoints are hit.
However when I try to build this image normally using docker it doesn't work as it cannot find the path of the .csproj files.
I think this happens because Visual Studio is using volume sharing so it has access to the parent folders or it is changing the scope the Dockerfile is run in somehow.
However, in my CI pipeline it runs docker build on ServiceStackDockerTest/Dockerfile which copies this folder to docker and does not have access to it's parent folders. The paths in VS dockerfile are all from the parent directory to where the docker file is located.
Why has Microsoft built in their support like this? Surely building the dockerfile inside a CI pipeline is a common task.
What is the best way to fix this so I still can debug inside the container with live reload inside Visual Studio but also build the image in my CI pipeline?

So the answer to this is actually very simple. In CI pipeline just change the build context to the parent folder.
To build locally run this from parent folder docker build -f SSDockerTest2\Dockerfile . and then it runs it in context of parent direcory.

Related

Failed to launch debug adapter, when starting Docker-compose in Visual Studio 2019?

I have a Visual Studio solution in which I want to run various API microservices. Each microservice has the required Dockerfiles individually. I want to run the project using docker-compose, so I added container orchestration support. I also modified the necessary things in the docker-compose.yml and override files. I then set up docker-compose as the Startup Project. (Set as Startup Project). However, when I try to start with F5, the debugger does not start and I get the following error message:
One or more errors occured.
Failed to launch debug adapter. Additional information may be available in the output window.
The operation was canceled.
Output window:
The program '' has exited with code -1 (0xffffffff).
On the Docker Desktop I can see the containers, but my API projects' logs are empty. They won't start.
(I have virtualization and HyperV enabled too.)
How could this problem be solved?
According to Microsoft, by default the debugger runs in Fast Mode to speed up the building process of your docker containers. In this mode, your Dockerfile is only partially built.
https://learn.microsoft.com/en-us/visualstudio/containers/container-build?view=vs-2019
In Fast mode, Visual Studio calls docker build with an argument that tells Docker to build only the base stage. Visual Studio handles the rest of the process without regard to the contents of the Dockerfile. So, when you modify your Dockerfile, such as to customize the container environment or install additional dependencies, you should put your modifications in the first stage. Any custom steps placed in the Dockerfile's build, publish, or final stages will not be executed.
This means that if you bring in any other image in your Dockerfile before the image you are using for your runtime, it will try to use this image for your container.
Solution
If you want to keep all the steps in your Dockerfile, while still being able to debug, bring your runtime image in at the top of the file and still use it where you need it. For example:
FROM mcr.microsoft.com/dotnet/aspnet:3.1 as base
...
FROM base
WORKDIR /src
...
Alternatively you can add a PropertyGroup item to your project file, telling it to build the docker file in Regular Mode. This will slow down your builds.
<PropertyGroup>
<ContainerDevelopmentMode>Regular</ContainerDevelopmentMode>
</PropertyGroup>
Reproducing the error
I verified this by creating a new empty .NET core project with Docker Support and Container Orchestration.
The original dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["TestApplication/TestApplication.csproj", "TestApplication/"]
RUN dotnet restore "TestApplication/TestApplication.csproj"
COPY . .
WORKDIR "/src/TestApplication"
RUN dotnet build "TestApplication.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "TestApplication.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TestApplication.dll"]
Runs fine. When adding a different image, node in this case, at the top of the Dockerfile, the debugger breaks:
FROM node:14-alpine as node-base
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["TestApplication/TestApplication.csproj", "TestApplication/"]
RUN dotnet restore "TestApplication/TestApplication.csproj"
COPY . .
WORKDIR "/src/TestApplication"
RUN dotnet build "TestApplication.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "TestApplication.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TestApplication.dll"]

Running tests for .NET Core in Docker during local dev

I understand that we can build our Dockerfile with a stage to execute dotnet test and run our unit tests during the docker build.
My question is regarding whether or not we would want those tests to run during local dev (e.g. with Visual Studio).
If we run (F5) a .NET Core app that is set up to build into a Docker image, and then use the VS tools for Docker to debug, etc., are we going to also be running our unit tests at that point EVERYTIME we run/debug locally?
What if I already use the test runner built-into Visual Studio (or even the Live Unit Testing feature) during dev/debug time? Am I still forced to run those same unit tests during the docker build that is triggered from the IDE's Run/F5 command if I define a stage in the Dockerfile for running tests?
If not, what is the recommended approach to running tests in Docker but only execute that stage during a CI build and not during a local dev build?
To me, it would make the most sense to run the test project directly by VS outside the context of Docker, as long as your tests do not require being run in a Docker environment of course. And then have CI run tests via the Dockerfile.
Even if your Dockerfile is built with every F5, you don't need to have the tests run. In a multi-stage Dockerfile you don't need to have a single line of stage dependencies. By that I mean, you can have a test stage that is purely opt-in and is not run by default. It would only be run if you explicitly set the target stage as the test stage when running docker build. More details on this approach can be found here: https://github.com/dotnet/dotnet-docker/tree/main/samples/complexapp#running-tests-as-an-opt-in-stage. Here's an example Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /source
# copy csproj and restore as distinct layers
COPY app/*.csproj app/
RUN dotnet restore app/app.csproj
# copy and build app
COPY app/ app/
WORKDIR /source/app
RUN dotnet build -c release --no-restore
# test stage -- exposes optional entrypoint
# target entrypoint with: docker build --target test
FROM build AS test
WORKDIR /source/tests
COPY tests/ .
ENTRYPOINT ["dotnet", "test", "--logger:trx"]
FROM build AS publish
RUN dotnet publish -c release --no-build -o /app
# final stage/image
FROM mcr.microsoft.com/dotnet/core/runtime:3.1
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "app.dll"]

ASPNET Core 3.1 - Dockerfile changes don´t work when run in Visual Studio

I run a web api (ASPNETCore 3.1 - API) in Visual Studio 2019 (F5 in Debug mode), I use Docker (for Linux), when I change Dockerfile and run application, the changes don´t work.
Example:
I put the RUN touch test.txt and ENV my_variable value in Dockerfile, but the file and variable are not created.
Does anyone know why?
Nothing like reading the official documentation...
https://learn.microsoft.com/en-us/visualstudio/containers/container-build?view=vs-2019#debugging
Debugging
When building in Debug configuration, there are several optimizations that Visual Studio does that help with the performance of the build process for containerized projects. The build process for containerized apps is not as straightforward as simply following the steps outlined in the Dockerfile. Building in a container is much slower than building on the local machine. So, when you build in the Debug configuration, Visual Studio actually builds your projects on the local machine, and then shares the output folder to the container using volume mounting. A build with this optimization enabled is called a Fast mode build.
In Fast mode, Visual Studio calls docker build with an argument that tells Docker to build only the base stage. Visual Studio handles the rest of the process without regard to the contents of the Dockerfile. So, when you modify your Dockerfile, such as to customize the container environment or install additional dependencies, you should put your modifications in the first stage. Any custom steps placed in the Dockerfile's build, publish, or final stages will not be executed.
This performance optimization only occurs when you build in the Debug configuration. In the Release configuration, the build occurs in the container as specified in the Dockerfile.
I put RUN touch test.txt and ENV my_variable value in the first lines (where Dockerfile build base stage) and it works.
A tip refers to RUN touch test.txt, this needs to be executed (WORKDIR) in another folder different from the folder mapped with the source code in the hostlocal (/app).
Example:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /tmp
RUN touch teste.txt
WORKDIR /app
ENV my_variable value
EXPOSE 80
EXPOSE 443
My final Dockerfile is:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /tmp
RUN touch teste.txt
RUN echo "teste1" > teste1.txt
WORKDIR /app
ENV my_variable value
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["MyTestProject/TestProject.csproj", "MyTestProject/"]
RUN dotnet restore "MyTestProject/TestProject.csproj"
COPY . .
WORKDIR "/src/MyTestProject"
RUN dotnet build "TestProject.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "TestProject.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV my_variable value
RUN touch teste.txt
ENTRYPOINT ["dotnet", "TestProject.dll"]
You can choose to include additional environment files to be passed to the docker run command by adding the following tag DockerfileRunEnvironmentFiles inside the propertygroup in your .csproj file:
<PropertyGroup>
<DockerfileRunEnvironmentFiles>your_env_file.env</DockerfileRunEnvironmentFiles>
...
...
<PropertyGroup>
your_env_file.env:
PAT=[Personal Access Token]
PROJECT=[Azure DevOps Project URL]
VARIABLE=[Filename of the variable file including extension]
WORKSPACE=[Name of the default workspace]
For the problem about the arguments not being passed you can add this to your csproj:
<PropertyGroup>
<DockerDebuggeeArguments>touch test.txt</DockerDebuggeeArguments>
...
...
</PropertyGroup>
Found here: https://learn.microsoft.com/en-us/visualstudio/containers/container-msbuild-properties?view=vs-2019

Need assistance with Dockerfile and Kubernetes for .AspNetCore service

My docker build is failing due to the following error:
COPY failed: CreateFile \?\C:\ProgramData\Docker\tmp\docker-builder117584470\Aeros.Services.Kubernetes\Aeros.Services.Kubernetes.csproj: The system cannot find the path specified.
I am fairly new to docker and have went with the basic project template that is set up when you create a Kubernetes container project template, so I'd figure it would work out of the box, but I'm mistaken.
I'm having problems trying to figure out what it's attempting to due in the temp directory structure and the reason it is failing. Can anyone offer some assistance? I've done some searching and others have said the default docker template was incorrect in Visual Studio, but I'm not seeing any of the files being copied over to the temp directory to begin with, so figuring out what is going on is being rather problematic at the time.
Here is the docker file, the only thing I've added is a publishingProfile arg so I can tell it which profile to use in the Build and Publish steps :
ARG publishingProfile
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ["Aeros.Services.Kubernetes/Aeros.Services.Kubernetes.csproj", "Aeros.Services.Kubernetes/"]
RUN dotnet restore "Aeros.Services.Kubernetes/Aeros.Services.Kubernetes.csproj"
COPY . ./
WORKDIR "/src/Aeros.Services.Kubernetes"
RUN dotnet build "Aeros.Services.Kubernetes.csproj" -c $publishingProfile -o /app
FROM build AS publish
RUN dotnet publish "Aeros.Services.Kubernetes.csproj" -c $publishingProfile -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Aeros.Services.Kubernetes.dll"]
I haven't touched the yaml file, but if you need that I can provide it as well. Again, all I've done with this is add a few NuGet packages to the project reference. Build in VisualStudio runs fine, but the docker command:
docker build . --build-arg publishingProfile=Release
is failing with the error mentioned above.
Can someone be so kind as to offer some enlightenment? Thanks!
Edit 1:
I am executing this from the project's folder via a PowerShell command line.
Leandro's comments helped come across the solution.
So first a rundown of that COPY command, it takes two parameters, source and destination.
Within the template for the Dockerfile for Visual Studio, it includes the folder location of the .csproj file it is attempting to copy. In my case, the command read as follows:
COPY ["Aeros.Services.Kubernetes/Aeros.Services.Kubernetes.csproj", "Aeros.Services.Kubernetes/"]
So it is looking for my Aeros.Services.Kubernetes.csproj file in the Aeros.Services.Kubernetes project folder and copying it to the Aeros.Services.Kubernetes folder in the src folder of Docker.
The problem with this is that if you use the default setup, your dockerfile is included inside the project folder. If you are executing the docker build from within the project folder, the syntax for the COPY command is actually looking in the wrong file location. For instance, if your project is TestApp.csproj located in the TestApp project folder, and you are executing the Docker build command for the dockerfile within the same folder, the syntax for that COPY command:
COPY ["TestApp/TestApp.csproj", "TestApp/"]
is actually looking for: TestApp/TestApp/TestApp.csproj.
The correct syntax for the COPY command in this situation should be:
COPY ["TestApp.csproj", "TestApp/"]
since you are already within the TestApp project folder.
Another problem with the default template that may trouble some is that it doesn't copy the web files for the project either, so once you get past the COPY and dotnet restore steps, you will fail during the BUILD with a:
CSC : error CS5001: Program does not contain a static 'Main' method
suitable for an entry point
This is resolved by adding:
COPY . ./
following your RUN dotnet restore command to copy your files.
Once these pieces have been addressed in the default template provided, everything should be functioning as expected.
Thanks for the help!
In which line the problem happens? I do not remember if docker build shows it.
Where are you executing this build? The problem is that it is not finding the file you are trying to copy. It should be local to where the command is executed.
I saw now, the problem is on the first COPY.

How to containerize a .net core app that has dependencies in the same solution?

I am trying to containerize a simple .net core console application that depends on a .net Standard library.
My solution layout is like this:
+
|- Solution.sln
|- Library
| +- Files in Library project
+- App
|- Files in App
+- Dockerfile
App depends on Library.
So far I have something like this:
FROM microsoft/dotnet:2.1-sdk
WORKDIR /app
# copy fsproj and restore as distinct layers
COPY *.fsproj ./
RUN dotnet restore
# copy and build everything else
COPY . ./
RUN dotnet publish -c Release -o out
But this, obviously, only containerizes App.
How do I achieve putting Library in, too, without having to put the Dockerfile into the root besides the solution file?
You can change the docker build context to the parent directory.
Change your dockerfile to this:
FROM microsoft/dotnet:2.1-sdk
WORKDIR /app
# copy fsproj and restore as distinct layers
COPY App/*.fsproj ./
RUN dotnet restore
# copy and build everything else
COPY App/. ./
RUN dotnet publish -c Release -o out
And then run (from the folder containing Add and Library)
docker build -f App/Dockerfile .
But yes, it's usually more common to have your dockerfile located where you want your build context to be. So at the location that has all the files you'll need.
On docker for Mac/Windows the docker daemon will copy every file from the build context into the VM. So there is a performance hit here. Something to consider when you're structuring your projects.

Resources