.NET Core + Docker on Raspberry Pi. Is it legal?

Original author: Scott Hanselman
  • Transfer
The open .NET Core platform works on almost everything: Windows, Mac and a dozen Linux systems. But there is still the SDK and Runtime. Previously, the .NET Core SDK was not supported on ARMv7 / ARMv8 chips that run Raspberry Pi. But everything has changed. Read more about how to run under the cut!

This article was written by Scott Hanselman . Give him the word.

I like the Raspberry Pi. These are great compact learning machines, and kids love to play with them. Even if these children are adults, and they create a cluster of six Kubernetes Raspberry Pi nodes .

NET Core is now supported in Linux distributions of ARM32, such as Raspbian and Ubuntu !

Note. .NET Core 2.1 is supported on Raspberry Pi 2+. It is not supported on Pi Zero or other devices with an ARMv6 chip. An ARMv7 or ARMv8 chip, such as ARM Cortex-A53, is required to work with .NET Core. Experts from the Azure IoT Edge team use .NET Core Bionic ARM32 Docker images to support developers writing in C # on Edge devices .

There are two ways to launch .NET Core on the Raspberry Pi.

The first way is to use Docker . This is literally the fastest and easiest way to get .NET Core to work on Pi. This seems incredible, but such small Raspberry Pi devices perfectly support Docker platform containers. This can be done in minutes. To quickly install Docker you will need:

curl -sSL https://get.docker.com | sh
sudo usermod -aG docker pi

After installing Docker, you need to log in and log out. See a small example to make sure that .NET Core works correctly. You can see the available Docker tags at , as well as learn more about .NET Core Docker templates here .

Now I can just run Docker, and then execute the “dotnet --info” command to find out about dotnet on my Pi.

pi@raspberrypi:~ $ docker run --rm -it microsoft/dotnet:2.1-sdk dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   2.1.300-rc1-008673
 Commit:    f5e3ddbe73
Runtime Environment:
 OS Name:     debian
 OS Version:  9
 OS Platform: Linux
 RID:         debian.9-x86
 Base Path:   /usr/share/dotnet/sdk/2.1.300-rc1-008673/
Host (useful for support):
  Version: 2.1.0-rc1
  Commit:  eb9bc92051
.NET Core SDKs installed:
  2.1.300-rc1-008673 [/usr/share/dotnet/sdk]
.NET Core runtimes installed:
  Microsoft.NETCore.App 2.1.0-rc1 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET Core runtimes or SDKs:

This is just great! Now I just request dotnet: 2.1-sdk on Raspberry Pi (RPi), and since they use Docker multi-architecture files, the platform works correctly. If you want to use .NET Core with Docker on the ARM32 , you can use any of the following tags.

Note. The first three tags are multi-arch and bionic for Ubuntu 18.04. The codename is stretch for Debian 9. So I use 2.1-sdk, and it works fine on my RPi, but I can be more specific if I want.

  • 2.1-sdk
  • 2.1-runtime
  • 2.1-aspnetcore-runtime
  • 2.1-sdk-stretch-arm3
  • 2.1-runtime-stretch-slim-arm32v7
  • 2.1-aspnetcore-runtime-stretch-slim-arm32v7
  • 2.1-sdk-bionic-arm32v7
  • 2.1-runtime-bionic-arm32v7
  • 2.1-aspnetcore-runtime-bionic-arm32v7

Try to do it in minutes like this:

docker run --rm microsoft/dotnet-samples:dotnetapp

Here it downloads the image ...

In previous versions of the .cker Core Docker files, this would not have happened if you ran the x64 image on ARM.

standard_init_linux.go:190: exec user process caused "exec format error"

Different processors! But with Microsoft's multi- architecture tags from Kendra Havens from Microsoft, everything works fine in version 2.1.

Docker has a multi -architecture feature that has recently begun to be used in microsoft / dotnet-nightly . In the near future it is planned to transfer it to the official Microsoft / dotnet repository .. The multiarch feature allows the use of a single tag for multiple machine configurations. Without this function, a unique tag is required for each architecture / OS / platform. For example, the microsoft / dotnet tag: 1.0-runtime is based on Debian, and microsoft / dotnet: 1.0-runtime-nanoserver on the Nano Server. With multiarchitecture, only one common microsoft / dotnet tag will be needed: 1.0-runtime. If you take this tag from the Linux container environment, you will get a Debian based image, whereas if you take it from a Windows container environment, you will get an image based on the Nano Server. This helps ensure consistency of tags in the Docker environment, thus avoiding confusion.

In the examples above, I can do the following:

Run a pre-configured application inside a Docker image, for example:
docker run --rm microsoft/dotnet-samples:dotnetapp

Run dotnet commands in an SDK image, for example:
docker run --rm -it microsoft/dotnet:2.1-sdk dotnet --info

Run an interactive terminal in an SDK image, for example:
docker run --rm -it microsoft/dotnet:2.1-sdk

As a small example, here I will go to the container and I will add a small console application and run it, just to prove that it is possible. Everything will be deleted when I exit the container.

pi@raspberrypi:~ $ docker run --rm -it microsoft/dotnet:2.1-sdk
root@063f3c50c88a:/# ls
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@063f3c50c88a:/# cd ~
root@063f3c50c88a:~# mkdir mytest
root@063f3c50c88a:~# cd mytest/
root@063f3c50c88a:~/mytest# dotnet new console 
The template "Console Application" was created successfully.
Processing post-creation actions...
Running 'dotnet restore'on /root/mytest/mytest.csproj...
  Restoring packages for /root/mytest/mytest.csproj...
  Installing Microsoft.NETCore.DotNetAppHost 2.1.0-rc1.
  Installing Microsoft.NETCore.DotNetHostResolver 2.1.0-rc1.
  Installing NETStandard.Library 2.0.3.
  Installing Microsoft.NETCore.DotNetHostPolicy 2.1.0-rc1.
  Installing Microsoft.NETCore.App 2.1.0-rc1.
  Installing Microsoft.NETCore.Platforms 2.1.0-rc1.
  Installing Microsoft.NETCore.Targets 2.1.0-rc1.
  Generating MSBuild file /root/mytest/obj/mytest.csproj.nuget.g.props.
  Generating MSBuild file /root/mytest/obj/mytest.csproj.nuget.g.targets.
  Restore completed in15.8 sec for /root/mytest/mytest.csproj.
Restore succeeded.
root@063f3c50c88a:~/mytest# dotnet run
Hello World!
root@063f3c50c88a:~/mytest# dotnet exec bin/Debug/netcoreapp2.1/mytest.dll
Hello World!

If you try it yourself, you will notice that the dotnet run command is running slowly. This is because it performs restoration, build, and launch. The compilation doesn't go too fast on these tiny devices. Therefore it is worth doing as little work as possible. Instead of running “dotnet run” all the time, I will execute the “dotnet build” command, and then “dotnet exec”, which run very quickly.

If you are going to try Docker and .NET Core, then these resources will be extremely useful to you .

Creating .NET Core Applications with Docker

Creating .NET Core applications in a container

Container size optimization


It seems to me that these examples are very useful ... Be sure to see the Docker files themselves, as they will give you a wealth of information on how to structure your own files. The ability to create multistep Docker files is crucial when working on a small device, such as RPi. It’s advisable to do as little work as possible, and let Docker automatically cache as many layers as possible. If you don’t think about this point, you’ll end up spending 10 times more time creating image layers for each assembly.

Translation of a real ASP.NET site to Docker with tests!

Can I translate my site with podcasts to Docker, as well as build / test / run it on Raspberry Pi? Yes!

FROM microsoft/dotnet:2.1-sdk AS build
# copy csproj and restore as distinct layers
COPY *.sln .
COPY hanselminutes.core/*.csproj ./hanselminutes.core/
COPY hanselminutes.core.tests/*.csproj ./hanselminutes.core.tests/
RUN dotnet restore
# copy everything else and build app
COPY . .
WORKDIR /app/hanselminutes.core
RUN dotnet build
FROM build AS testrunner
WORKDIR /app/hanselminutes.core.tests
ENTRYPOINT ["dotnet", "test", "--logger:trx"]
FROM build AS test
WORKDIR /app/hanselminutes.core.tests
RUN dotnet test
FROM build AS publish
WORKDIR /app/hanselminutes.core
RUN dotnet publish -c Release -o out
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
COPY --from=publish /app/hanselminutes.core/out ./
ENTRYPOINT ["dotnet", "hanselminutes.core.dll"]

Wonderful. Now I can execute the “docker build.” Command on the Raspberry Pi. The device will restore, test and build it. If the test fails, the Docker build fails.

You see, there is an additional section called "testrunner", and then after it "test?" This section does not work. He sets ENTRYPOINT, but no one ever uses it ... yet. ENTRYPOINT is an implicit start if it is in the last line in the Docker file. This is done so that I can find it if I want.

I can just build it and run it like this:

docker build -t podcast .
docker run --rm -it -p 8000:80 podcast

The note.Note that the runtime image is microsoft / dotnet: 2.1-aspnetcore-runtime, not microsoft / dotnet: 2.1-runtime. Also, aspnetcore one tentatively includes the binary code that is needed to run an ASP.NET application, so I can simply include one link to the "/>" in the csproj. If you do not use the base aspnetcore-runtime image, you would have to manually activate all the ASP.NET Core packages I need. Using a base image can make the final image files larger, but this is just a balance between convenience and size. You decide. You can manually include only the packages you need or use the “Microsoft.AspNetCore.App” meta-package for convenience. My final podcast image weighed 205 megabytes, which is not bad, but, of course, if I wanted to, I could reduce it in several ways.

And if I just need the Docker test results , I can get them! This means that I can run the test in the Docker container, mount the volume between the Linux container and the (theoretical) Windows node, and then open the resulting .trx file in Visual Studio!

docker build --pull --target testrunner -t podcast:test .
docker run --rm -v D:\github\hanselminutes-core\TestResults:/app/hanselminutes.core.tests/TestResults podcast:test

Just look! These are the test results that were performed in the Linux container:

This is the result. Now I have a podcast site running Docker on the ARM32 Raspberry Pi 3, and I spent only one hour at work (writing the Docker file)!

The second way. Have you got to this point? You can simply install the .NET Core 2.1 SDK on hardware. Docker is not needed, just download tar.gz and configure it. I can install it on an RPI ARM32v7 Dockerfile device . Notice that I have a common ASP.NET Core * runtime and the * .NET Core SDK. In the final version you will receive an SDK in which everything will be included, including ASP.NET.

$ sudo apt-get -y update
$ sudo apt-get -y install libunwind8 gettext
$ wget https://dotnetcli.blob.core.windows.net/dotnet/Sdk/2.1.300-rc1-008673/dotnet-sdk-2.1.300-rc1-008673-linux-arm.tar.gz
$ wget https://dotnetcli.blob.core.windows.net/dotnet/aspnetcore/Runtime/2.1.0-rc1-final/aspnetcore-runtime-2.1.0-rc1-final-linux-arm.tar.gz
$ sudo mkdir /opt/dotnet
$ sudo tar -xvf dotnet-sdk-2.1.300-rc1-008673-linux-arm.tar.gz -C /opt/dotnet/
$ sudo tar -xvf aspnetcore-runtime-2.1.0-rc1-final-linux-arm.tar.gz -C /opt/dotnet/
$ sudo ln -s /opt/dotnet/dotnet /usr/local/bin
$ dotnet --info

Long live cross-platform!

Also popular now: