Running ASP.NET core web application on Raspberry Pi

I always wanted to get more familiar with Raspberry Pi and Kubernetes. In December during my time off, I finally bought a Pi and experimented few things. One such experiment was to run ASP.NET core web application on Raspberry Pi. This was a fun experiment and I definitely learned more about Raspberry Pi, Kubernetes, ASP.NET core, Traefik, Docker and associated technologies. In this post, I would describe the steps that are required to run ASP.NET web app on Pi.

Create ASP.NET core web application

Platform: Windows, Tools: Visual Studio 2019

The process for creating a basic ASP.NET core web application is shown below. It has following steps:

  1. Create a new project in Visual Studio (I used Visual Studio 2019)
    • Select ASP.NET Core Web Application
  2. Give it a name (I gave the name HelloWebApp)
  3. Select Web Application (Model-View-Controller) (You can use different type as well)
    • In Advanced section, select Enable Docker Support and Linux in the drop down below that.
  4. Right click on project, select Add and choose Docker Support.
  5. Select Target OS as Linux
  6. This will generate a Dockerfile in the project
    • Update docker file to use ARM architecture
    • The changes are shown in image 6 where older lines are commented with # and new lines are added below it.
    • For example:
    • #FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
    • FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim-arm32v7 AS base
  7. After that build the solution, right click on project and select publish
  8. Provide information about your docker hub account etc. and click publish

The whole process is shown in the images below as well.

Install Kubernetes

Platform: Raspberry Pi, Tools: k3s

I used k3s for Kubernetes as it seems to be the commonly used lightweight Kubernetes based distribution suitable for Raspberry Pi. SSH into your Raspberry Pi and run following command to install Kubernetes:

curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.19.5+k3s2 sh -s - --write-kubeconfig-mode 644 --disable=metrics-server

I used specific version because newer version of k3s had some stability issues as mentioned here: https://github.com/k3s-io/k3s/issues/2798

I also use –write-kubeconfig-mode, this allows running kubectl without requiring sudo.

Finally I had to use –disable=metrics-server because I was having some problem deleting namespaces without it.

As I didn’t need metrics for this experiment, I didn’t spend time debugging it either. You may want to try without disabling metrics-server though and disable it only if you hit problems.

Enable Traefik Dashboard

k3s uses Traefik as reverse proxy for routing incoming traffic to the pods. To enable Traefik dashboard, I needed to do following:

kubectl -n kube-system edit cm traefik

This will open up a VIM window with configuration for Traefik. In that, add the lines as commented below and save it using :wq

# Traefik configuration (partial snippet shown below).
 [ping]
   entryPoint = "http"
   [kubernetes]
     [kubernetes.ingressEndpoint]
     publishedService = "kube-system/traefik"
   [traefikLog]
     format = "json"
   # Add these two lines and save it using :wq
   [api]
     dashboard = true

Scale Traefik in and out to make configuration change take effect:

kubectl -n kube-system scale deploy traefik --replicas 0
kubectl -n kube-system scale deploy traefik --replicas 1

Once this is done, Traefik dashboard should be active. You need one more step before you can view the dashboard:

kubectl -n kube-system port-forward deployment/traefik 8080

Now you can go to http://localhost:8080/dashboard and see the Traefik dashboard. Dashboard looks something like as shown below, but initially FRONTENDS and BACKENDS would be blank and once we are done with deploying our HelloWebApp, it would look similar to picture below.

Create FQDN for localhost

Create a FQDN for localhost by adding following line to /etc/hosts

Note: I used the name picloud0.pg.us. You can use a different name. Just make sure you update the name correctly in all the files used in this tutorial.

127.0.0.1       picloud0.pg.us

Deploy HelloWebApp application

1. Create a new namespace

kubectl create namespace aspnetcore0

2. Create Yaml file for web application

#Create file hellowwebapp.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellowebapp
  namespace: aspnetcore0
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hellowebapp
  template:
    metadata:
      labels:
        app: hellowebapp
    spec:
      containers:
      - name: hellowebapp
        image: ipankajg/hellowebapp:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hellowebapp
  namespace: aspnetcore0
spec:
  ports:
  - name: http
    port: 80
  selector:
    app: hellowebapp
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hellowebapp
  namespace: aspnetcore0
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
  - host: picloud0.pg.us
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hellowebapp
            port:
              number: 80

3. Deploy the application

#Deploy application in aspnetcore0 namespace
kubectl -n aspnetcore0 apply -f hellowebapp.yml

4. Verify the application deployment

Go to http://picloud0.pg.us and see that webpage is served (as shown below). You can also try https://picloud0.pg.us but you would get security warning as certificate is invalid as Traefik supplies some default example.com certificate.

Use Traefik generated self-signed certificate

You can also make Traefik generate a self-signed certificate if that works for your use case. This can be done by editing the configuration of Traefik and commenting out the default certificate.

kubectl -n kube-system edit cm traefik
  traefik.toml:
    # traefik.toml
    logLevel = "info"
    defaultEntryPoints = ["http","https"]
    [entryPoints]
      [entryPoints.http]
      address = ":80"
      compress = true
      [entryPoints.https]
      address = ":443"
      compress = true
        [entryPoints.https.tls]
          #The following 3 lines needs to be commented out
          #[[entryPoints.https.tls.certificates]]
          #CertFile = "/ssl/tls.crt"
          #KeyFile = "/ssl/tls.key"
kubectl -n kube-system scale deploy traefik --replicas 0
kubectl -n kube-system scale deploy traefik --replicas 1

At this point, you should see that the certificate served by the website has changed to a Traefik generated certificate instead of example.com certificate.

Final: Generate self-signed certificate for picloud0.pg.us

1. Install cert-manager for the cluster

#Install cert-manager
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.yaml

2. Create a certificate issuer spec to issue self signed certificates

#Create a file selfSigned-issuer.yml with this content
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
## Create selfsigned issuer for the cluster
kubectl apply -f selfSigned-Issuer.yml

3. Modify application spec to include certificate generation

Next we need to modify hellowebapp.yml so a self signed certificate is issued automatically by cert manager. This is done by adding a cluster-issuer line and tls section in the application deployment yaml spec.

#File hellowebapp.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellowebapp
  namespace: aspnetcore0
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hellowebapp
  template:
    metadata:
      labels:
        app: hellowebapp
    spec:
      containers:
      - name: hellowebapp
        image: ipankajg/hellowebapp:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hellowebapp
  namespace: aspnetcore0
spec:
  ports:
  - name: http
    port: 80
  selector:
    app: hellowebapp
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hellowebapp
  namespace: aspnetcore0
  annotations:
    #Add this line to use selfSigned-issuer for our certificate
    cert-manager.io/cluster-issuer: selfsigned-issuer
    kubernetes.io/ingress.class: traefik
spec:
  rules:
  - host: picloud0.pg.us
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hellowebapp
            port:
              number: 80
  #Add this section to have a certificate generated for our given host/FQDN
  tls:
  - hosts:
    - picloud0.pg.us
    secretName: hellowebapp-local-cert

4. Redeploy the application using modified yml file

kubectl -n aspnetcore0 apply -f hellowebapp.yml

5. Verify that new certificate is issued

6. Make chrome accept self signed certificate

https://peacocksoftware.com/blog/make-chrome-auto-accept-your-self-signed-certificate

7. Redirect HTTP to HTTPS

Using Traefik, you can also redirect HTTP to HTTPS. This is again done by modify Traefik configuration as shown below.

kubectl -n kube-system edit cm traefik
    [entryPoints]
      [entryPoints.http]
      address = ":80"
      compress = true
        # Add next two lines to redirect HTTP to HTTPS
        [entryPoints.http.redirect]
        entryPoint = "https"
      [entryPoints.https]
      address = ":443"
      compress = true

Conclusion

At this point, you have an ASP.NET core web application running on Raspberry Pi. This web application is serving traffic over HTTPS and using self-signed certificate. Your browser is also trusting the self-signed certificate. Pretty simple and cool!

As next steps, you can explore cert-manager further to see how to generate LetsEncrypt certificate automatically and be able to serve this website from your Pi to external users over Internet. You would need a valid domain name for that.

Happy hacking!

Share