What’s the whale got to do with it?

A google search for ‘Docker’ will inevitably throw up a lot of results and the fact that it’s cross platform, whilst a positive, can mean that a lot of ‘Getting started’ guides make an assumption that the reader is coming from a Linux background. For .net developers such as myself, used to a Windows & Visual Studio environment, this can quickly become a quagmire of information that, at times, feels complex, contradictory and full of choices that leave you feeling a little lost.

For the sake of my own sanity, I’ve started these posts as a quick reference for myself to be able to note down what I’ve learnt and cut through any extraneous information that does not apply to the majority of my scenarios.

Here’s an example of the sort of use-case I want to understand better:

  • I’m developing one or more web apis written in C# using dotnet core 2.0+ in Visual Studio on a Windows 10 Pro machine
  • I want to be able to consider the option that each api maybe deployed in a load balanced way.
  • I may be targeting Azure as a host provider
  • I want to be able to understand the deployment pipeline

The first step in this process is to enable Docker support, which will be an option when creating a new Asp.Net core web application or api in VS2019:

As suggesting in the footnote, you need to install Docker Desktop

Docker Desktop requires Windows 10 Professional to work as it uses the Hyper-V feature to create a VM on your local machine. If you open up the Hyper-V manager after installation you will see a new Docker Desktop VM. Docker Desktop includes Docker Swarm as its Container Orchestrator by default (although you can now change this setting to use Kubernetes). Keeping the default target OS as Linux will provide more options for you when it comes to orchestration and deployment scenarios.

Dockerfile

The two changes you may notice having enabled Docker Support are the extra Dockerfile in your solution and the target Debug environment will be set to docker instead of running as a dotnet exe. What this means is that, on debug, instead of building the solution’s dlls and then launching the exe, the Docker engine is used to construct an image of both the application and all of its dependencies required to run the app within host that supports docker containers. I’ve added in some comments to explain the various parts of the docker file:

# this is the 'BASE' image from which the application can be run. 
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base
#set up the app folder (creates if it does not exist)
WORKDIR /app 
# expose standard http and https ports
EXPOSE 80
EXPOSE 443

# the dotnet core 3.0 sdk is required to build the solution
FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build
# sets up the source code folder
WORKDIR /src
# now copy the csproj file into a new folder, 'DockerWeb'
COPY ["DockerWeb/DockerWeb.csproj", "DockerWeb/"]
# restore any nuget packages for the project
RUN dotnet restore "DockerWeb/DockerWeb.csproj"
# copies all the source files
COPY . .
# navigate to the new 'DockerWeb' folder
WORKDIR "/src/DockerWeb"
# build the application into the build folder
RUN dotnet build "DockerWeb.csproj" -c Release -o /app/build

# starting with the previous build stage, create a publish stage
FROM build AS publish
# publish all the necessary artefacts for the project
RUN dotnet publish "DockerWeb.csproj" -c Release -o /app/publish

# now reset back to the original aspnet core 3.0 runtime base image
FROM base AS final
# navigate to the app folder
WORKDIR /app
# copy the results of the previous publish stage into the app/publish folder
COPY --from=publish /app/publish .
# declare the app is launched by calling dotnet on the DockerWeb.dll
ENTRYPOINT ["dotnet", "DockerWeb.dll"]

If you hit F5 to launch the debugger, you will see that the app will load under another port other than 443. That’s because the port was set as an environment variable within a Docker run command as part of the debug launch. Check your Build output and you will see a docker run command with a number of environment variables being set using the ‘-e’, as well as the **port** numbers being mapped between the standard 80/443 ports:

docker run -dt -v ...
-e "ASPNETCORE_ENVIRONMENT=Development" 
-p 51136:80 -p 44394:443 
--entrypoint tail dockerweb:dev -f /dev/null

If you are using https then your launch url will end up using whatever has been set to map to port 443.

Container View

From VS 2019 version 16.4, you can now view the containers running on your docker host. This is a great addition to see your settings, ports and even a log stream as well as a file browsing tree.

Ports

The ssl port used when launching in Docker mode is determined by the Docker:sslPort variable you can find in the Properties/launchSettings.json file.

"Docker": {
  "commandName": "Docker",
  "launchBrowser": true,
  "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/weatherforecast",
  "environmentVariables": {
    "ASPNETCORE_URLS": "https://+:443;http://+:80",
    "ASPNETCORE_HTTPS_PORT": "44399"
  },
  "httpPort": 51136,
  "useSSL": true,
  "sslPort": 44355
}

Note that the environment variable ASPNETCORE_HTTPS also declares an ssl port (which will be set to be the same as the sslPort when creating your project), but this has no effect on the port used in simple Docker mode. Launch settings allow you to switch between different debug targets from IIS Express to running as a dotnet exe as well as simple Docker mode but are not a realistic reflection on how you would approach deployment in a production scenario. You could look into creating docker scripts that define multiple containers and set up the necessary network bridges between those containers, or you could consider moving towards a more Infrastructure-As-Code (IAC) scenario. That’s what I’m looking at in my next post.


Ben

Certified Azure Developer, .Net Core/ 5, Angular, Azure DevOps & Docker. Based in Shropshire, England, UK.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: