Create an ASP.NET Core Container For a Mikrotik Router
In this post, I will walk through the process of building a container for the React and ASP.NET Core
project template for a Mikrotik L009UiGS.
Prepare the project
Update the default dockerfile
as detailed in the comments within the file below.
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
# This stage is used when running from VS in fast mode (Default for Debug configuration)
# 1) (Optional) Optimize base image size by switching to a chiseled image
FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy-chiseled AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
# This stage is used to build the service project
# 2) Add --platform argument
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS with-node
RUN apt-get update
RUN apt-get install curl
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash
RUN apt-get -y install nodejs
# 3) Add --platform argument
FROM --platform=$BUILDPLATFORM with-node AS build
ARG BUILD_CONFIGURATION=Release
# 4) Save runtime in /tmp/rid for use later
ARG TARGETPLATFORM
RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ] ; then DOTNET_TARGET=linux-arm ; else DOTNET_TARGET=linux-x64 ; fi && echo $DOTNET_TARGET > /tmp/rid
WORKDIR /src
COPY ["Demo.Server/Demo.Server.csproj", "Demo.Server/"]
COPY ["demo.client/demo.client.esproj", "demo.client/"]
# 5) Pass runtime value to restore
RUN dotnet restore "./Demo.Server/Demo.Server.csproj" -r $(cat /tmp/rid)
COPY . .
WORKDIR "/src/Demo.Server"
# 6) Pass runtime value to build
RUN dotnet build "./Demo.Server.csproj" -c $BUILD_CONFIGURATION -o /app/build -r $(cat /tmp/rid)
# This stage is used to publish the service project to be copied to the final stage
# 7) Add --platform argument
FROM --platform=$BUILDPLATFORM build AS publish
ARG BUILD_CONFIGURATION=Release
# 8) Save runtime in /tmp/rid for use later
ARG TARGETPLATFORM
RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ] ; then DOTNET_TARGET=linux-arm ; else DOTNET_TARGET=linux-x64 ; fi && echo $DOTNET_TARGET > /tmp/rid
# 9) Pass runtime to publish
RUN dotnet publish "./Demo.Server.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false -r $(cat /tmp/rid)
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Demo.Server.dll"]
Building the project
First, uncheck the “Use containerd for pulling and storing images” in the Docker Desktop settings (otherwise, in my testing, the exported .tar archive was formatted in such a way that the Mikrotik failed to extract the container image).
Next, to build the project, and export the container into a .tar file, run the following commands in the solution directory.
docker build . -f ./Demo.Server/Dockerfile --target final --platform=linux/arm/v7 -t demo:latest
docker save -o demo.tar demo:latest
Setting up the router
Formatting USB Storage
If you have ext4
formatted USB storage already, skip this section.
Otherwise, insert a drive to format for the router. Run the following command on the router:
/disk format usb1 file-system=ext4 mbr-partition-table=no
The drive format does appear to be important as later steps failed with a fat32
formatted drive.
Prepare drive
Create the following directory structure for container data, run the following commands on the rotuer:
/file add type=directory name=usb1/container
/file add type=directory name=usb1/container/image
/file add type=directory name=usb1/container/rootdir
/file add type=directory name=usb1/container/workdir
Set up the container networking
Following some of the guidance outlined in the Container documentation by Mikrotik:
- create a virtual interface for the container
- a bridge for containers
- an IP address for the container
- an IP address for the container on the local network
These commands assume the following:
- The LAN address space is 192.168.0.0/24
- The virtual network 172.17.0.0/24 has not been configured
- Also - this is an acceptable address space based on the routers current configuration
- 192.168.0.231 is ununsed on the LAN
With those assumptions, the commands ready an IP address of 172.17.0.231/24 for the container and forward traffic from 192.168.0.231 to 172.17.0.231, thus making the container accessible on the LAN.
If necessary change the values below, then execute the commands on the router.
/interface/veth/add name=veth1 address=172.17.0.231/24 gateway=172.17.0.1
/interface/bridge/add name=containers
/ip/address/add address=172.17.0.1/24 interface=containers
/interface/bridge/port add bridge=containers interface=veth1
/ip/address/add address=192.168.0.231/24 interface=ether1
/ip/firewall/nat/add action=src-nat chain=srcnat src-address=172.17.0.231 out-interface-list=WAN to-addresses=192.168.0.231
/ip/firewall/nat/add action=dst-nat chain=dstnat dst-address=192.168.0.231 in-interface-list=WAN to-addresses=172.17.0.231
Env Variables
Below is an example of setting environment values. These values would likely appear within the appsettings.json
and/or appsettings.Development.json
files in the project.
/container/envs/add name=Demo key=DEMO_Authorization__Username value="admin"
/container/envs/add name=Demo key=DEMO_Authorization__Password value="admin"
Note the name
value in the /add
command is the name of the environment value group, this will be used when adding the container.
Sample appsettings.json
{
"Authorization": {
"Username": "admin",
"Password": "password"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Reading configuration
The commands in the previous section assume the environment variables are read with the DEMO_
prefix, adjust as needed.
builder.Configuration.AddEnvironmentVariables(prefix: "DEMO_").AddCommandLine(args);
Install container
Transfer the container .tar archive into the usb1/container/image
directory on the router. For example, using the following scp
command:
scp demo.tar 192.168.0.1:/container/image/demo.tar
Once transferred, run the following command on the router
/container/add file=usb1/container/image/demo.tar interface=veth1 envlist=Demo root-dir=usb1/container/rootdir/demo logging=yes
Start the container.
References
Mikrotik