docker and dot net core

By | January 25, 2022

This post will be about creating a dot net core application in a docker container. This allows us to run .NetCore in linux so we don’t need a windows server.

Todo this we will use docker. Docker desktop is a great tool for the mac and PC for personal use but no longer free for large organizations. This post will show you how to use docker in linux instead.

Linux

This post uses linux. We recommend using virtual box https://www.virtualbox.org/ to install Ubuntu. Get that set up and return here when complete.

Install dot net

We need to set up a few things before we get started. First we need packages from Microsoft. Then we can search the packages for the dot net version we want. Do this with the following commands.

wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get update
apt search dotnet-sdk

Sorting... Done
Full Text Search... Done
...

dotnet-sdk-3.1/bionic 3.1.416-1 amd64
  Microsoft .NET Core SDK 3.1.416

dotnet-sdk-5.0/bionic 5.0.404-1 amd64
  Microsoft .NET SDK 5.0.404

dotnet-sdk-6.0/bionic 6.0.101-1 amd64
  Microsoft .NET SDK 6.0.101

Next install dotnet 6.0 sdk using the following commands.

sudo add-apt-repository universe
sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-6.0

Check the version you have installed by using the following commands. You will see that the dotnet sdk is installed as well as the needed AspNetCore and NetCore runtimes.

dotnet --list-sdks  
6.0.101 [/usr/share/dotnet/sdk]

dotnet --list-runtimes             
Microsoft.AspNetCore.App 6.0.1 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.1 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

Install docker using the repository

Before you install Docker Engine for the first time on a new host machine, you need to set up the Docker repository. Afterward, you can install and update Docker from the repository. The following is a condensed set of instructions from https://docs.docker.com/engine/install/ubuntu/

Set up the repository

Update the apt package index and install packages to allow apt to use a repository over HTTPS:

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release

Add Docker’s official GPG key:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Use the following command to set up the stable repository. 

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine

Update the apt package index, and install the latest version of Docker Engine as follows:

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

Verify that Docker Engine is installed correctly by running the hello-world image.

$ sudo docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Setup permissions for docker

Next setup your user to have permission to use docker using the following command. This remove the need to use sudo before every docker command. After you run this command restart Ubuntu for the changes to take effect.

sudo usermod -a -G docker $USER

Create dotnet app

To create a dotnet app we need to set up a working folder using the following commands.

cd ~
mkdir Developer
cd Developer

Next make a precanned “Hello World” console app using the following command.

dotnet new console -o App -n DotNet.Docker

The template "Console App" was created successfully.

This will create several files in the the working folder.

We can see the apps directory structure from the command line by installing tree using the following command.

sudo apt-get install tree

Then list the apps directory structure use the following command.

tree App
App
├── DotNet.Docker.csproj
├── obj
│   ├── DotNet.Docker.csproj.nuget.dgspec.json
│   ├── DotNet.Docker.csproj.nuget.g.props
│   ├── DotNet.Docker.csproj.nuget.g.targets
│   ├── project.assets.json
│   └── project.nuget.cache
└── Program.cs

To run the app, navigate to the app folder, and enter dotnet run and the following will be displayed.

dotnet run

Hello, World!

The app is not interesting but it is useful to demonstrate creating a dot net core app in a docker container.

Publish app

To do this first publish our app using.

dotnet publish -c Release 

Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  DotNet.Docker -> /home/jbailey/Developer/App/bin/Release/net6.0/DotNet.Docker.dll
  DotNet.Docker -> /home/jbailey/Developer/App/bin/Release/net6.0/publish/

Notice that a DotNet.Docker.dll is created. This is will later be used as an ENTRYPOINT by docker.

Install vim

If you have vim installed already you can skip this step. Vim will be used to create our docker file. You can install vim using the following.

sudo apt install vim

Build image

To build our image we need to create a docker file. The docker file will contain commands which will be processed with docker build. This will result with an image with our app in it.

To create one use touch Dockerfile then vim into it using vim Dockerfile.

touch Dockerfile
vim Dockerfile

Activate insert mode in vim by pressing i and then paste in the following code. When finished press esc then :wq to save your file. You can confirm your changes using cat Dockerfile .

FROM mcr.microsoft.com/dotnet/aspnet:6.0
COPY bin/Release/net6.0/publish/ App/
WORKDIR /App
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

The FROM command gets the dot net image from the docker image store. The COPY command copies our published app. WORKDIR sets where the app is located and the ENTRYPOINT sets the target dll.

Now that the Dockerfile is created we can create a image that includes the dotnet runtime and our app using the following command.

docker build -t counter-image -f Dockerfile .

Sending build context to Docker daemon  1.013MB
Step 1/4 : FROM mcr.microsoft.com/dotnet/aspnet:6.0
 ---> 53451db35067
Step 2/4 : COPY bin/Release/net6.0/publish/ App/
 ---> 445ac43d6a88
Step 3/4 : WORKDIR /App
 ---> Running in c7cb0bceb8fd
Removing intermediate container c7cb0bceb8fd
 ---> f1ec37c57944
Step 4/4 : ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
 ---> Running in 017e7ff6b231
Removing intermediate container 017e7ff6b231
 ---> 2cf2ec73576f
Successfully built 2cf2ec73576f
Successfully tagged counter-image:latest

Check out the images that were created using docker image ls. Notice that counter-image was created. This will be used later to create a container.

docker image ls
                            
REPOSITORY                        TAG       IMAGE ID       CREATED         SIZE
counter-image                     latest    d8c0d20a6d8b   2 minutes ago   208MB
mcr.microsoft.com/dotnet/aspnet   6.0       53451db35067   18 hours ago    208MB

Run app in docker container

Next lets run the container once with the image we just created using the following command.

docker run -it --rm counter-image

Hello, World!

Congratulations! You have created your first dotnet core app in a docker container.

More advance stuff

Things get more complicated when you need nuget packages that are required for more complex dotnet projects.

Install mono

nuget.exe is a windows implemetation. You can run it in linux but first install mono using: see https://www.mono-project.com/download/stable/#download-lin for more information.

sudo apt install gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee/etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
sudo apt install mono-devel

Verify mono is installed by running a simple “Hello World” example. Navigate to your development folder

cd ~/Documents/Developer
mkdir mono-example
cd mono-example

Create a file called hello.cs using the following commands.

touch hello.cs
vim hello.cs

Paste the following into your file by pressing i to insert, paste and press esc :wq to save. Confirm your changes are made using cat hello.cs .

using System;

public class HelloWorld
{
    public static void Main(string[] args)
    {
        Console.WriteLine ("Hello Mono World");
    }
}

To compile, use csc:

    mcs hello.cs

The compiler will create “hello.exe”, which you can run using:

    mono hello.exe

The program should run and output:

Hello Mono World

HTTPS connections

To make sure HTTPS connections work, run the following command to check whether you can connect to nuget.org:

csharp -e 'new System.Net.WebClient ().DownloadString ("https://www.nuget.org")'

The program prints the website contents if everything works or throws an exception if it doesn’t.

WinForms Hello World

The following program tests writing a System.Windows.Forms application.
Create a file called helloforms.cs using the following commands.

touch helloforms.cs
vim helloforms.cs

Paste the following into your file by pressing i to insert, paste and press esc :wq to save. Confirm your changes are made using cat hello.cs .

using System;
using System.Windows.Forms;

public class HelloWorld : Form
{
    static public void Main ()
    {
        Application.Run (new HelloWorld ());
    }

    public HelloWorld ()
    {
        Text = "Hello Mono World";
    }
}

To compile, use mcs with the -r option to tell the compiler to pull in the WinForms libraries:

    mcs helloforms.cs -r:System.Windows.Forms.dll

The compiler will create “hello.exe”, which you can run using:

    mono hello.exe

ASP.NET Hello World

Create a text file with the name hello.aspx and the content:

<%@ Page Language="C#" %>
<html>
<head>
   <title>Sample Calendar</title>
</head>
<asp:calendar showtitle="true" runat="server">
</asp:calendar>

Then run the xsp4 command from that directory:

xsp4 --port 9000

Use a web browser to contact http://localhost:9000/hello.aspx

Install nuget

Install nuget using the following command:

  1. Install Mono 4.4.2 or later.
  2. Execute the following command at a shell prompt:BashCopy# Download the latest stable `nuget.exe` to `/usr/local/bin` sudo curl -o /usr/local/bin/nuget.exe https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
  3. Create an alias by adding the following script to the appropriate file for your OS (typically ~/.bash_aliases or ~/.bash_profile):BashCopy# Create as alias for nuget alias nuget="mono /usr/local/bin/nuget.exe"
  4. Reload the shell. Test the installation by entering nuget with no parameters. NuGet CLI help should display.
sudo apt install nuget

Verify nuget is installed using.

nuget

NuGet Version: 2.8.7.0
usage: NuGet <command> [args] [options] 
Type 'NuGet help <command>' for help on a specific command.

Available commands:

 config      Gets or sets NuGet config values.

 delete      Deletes a package from the server.

 help (?)    Displays general help information and help information about other commands.

 install     Installs a package using the specified sources. If no sources are specified, all sources def
             ined in the NuGet configuration file are used. If the configuration file specifies no source
             s, uses the default NuGet feed.

 list        Displays a list of packages from a given source. If no sources are specified, all sources de
             fined in %AppData%\NuGet\NuGet.config are used. If NuGet.config specifies no sources, uses t
             he default NuGet feed.

 pack        Creates a NuGet package based on the specified nuspec or project file.

 push        Pushes a package to the server and publishes it.
             NuGet's default configuration is obtained by loading %AppData%\NuGet\NuGet.config, then load
             ing any nuget.config or .nuget\nuget.config starting from root of drive and ending in curren
             t directory.

 restore     Restores NuGet packages.

 setApiKey   Saves an API key for a given server URL. When no URL is provided API key is saved for the Nu
             Get gallery.

 sources     Provides the ability to manage list of sources located in %AppData%\NuGet\NuGet.config

 spec        Generates a nuspec for a new package. If this command is run in the same folder as a project
             file (.csproj, .vbproj, .fsproj), it will create a tokenized nuspec file.

 update      Update packages to latest available versions. This command also updates NuGet.exe itself.

For more information, visit http://docs.nuget.org/docs/reference/command-line-reference