ASP.NET Core with k8s hosting - Part 1.3

Posted on October 27, 2020 in Develop
Updated: January 17, 2021

This blog is part of a serie:


This blog is an example of a development cycle developing in C# on Windows using VSCode editor - also for debugging docker containers - and deploying to k8s.

This blog is inspired by content from an eBook and some tutorials.
You find its code in this git repo: rasor/eBook-UsingNETCoreDockerKubernetes.

Main Sources are:


Chapters from eBook Using .NET Core, Docker, and Kubernetes

Chapter 1 ASP.NET and Docker Together

Chapter 1.1 Execute .NET Core application with Docker

See Part 1.1: Run docker containers

Chapter 2 Create Your Application with Docker

Chapter 2.1 Develop your ASP.NET Core application using Docker

See Part 1.2: Develop .NET docker images

Chapter 3 Deploy Your Application on Kubernetes

Chapter 3.2 Deploy your images in Kubernetes

I am using kind for creating a k8s cluster. I did that in this blog.

kubectl is a CLI using API to access k8s.

# check if k8s cluster is running
kubectl cluster-info
# Unable to connect to the server: dial tcp connectex: No connection could be made

# Check if there are any clusters
kind get clusters
# kind

# Check it the container is running
docker ps -a | grep kind
# CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                   PORTS                       NAMES
# 0b9a7220b4f8        kindest/node:v1.19.1   "/usr/local/bin/entr…"   5 weeks ago         Exited (0) 3 weeks ago>6443/tcp   kind-control-plane

# start the k8s cluster
docker start kind-control-plane

# check if k8s cluster is running
kubectl cluster-info
# Kubernetes master is running at
# KubeDNS is running at

Testing starting the image in a pod manually:

# Manually start the container in k8s - and never restart it, when it dies - let proxy know that pod should listen to :5000 and give container env-vars.  
kubectl run frontend2 --image=rasor/usingnetcoredockerkubernetes:frontend2-v1 --port=5000 --restart=Never --env="ASPNETCORE_URLS=http://+:5000"
# pod/frontend2 created

kubectl get pods
# frontend2   1/1     Running   0          24s

kubectl describe pod frontend2
# Name:         frontend2
# Namespace:    default
# Priority:     0
# Node:         kind-control-plane/
# Start Time:   Tue, 27 Oct 2020 12:24:42 +0100
# Labels:       run=frontend2
# Annotations:  <none>
# Status:       Running
# IP: 
# IPs:
#   IP:
# Containers:
#   frontend2:
#     Container ID:   containerd://6134c6c308d49c4c49835009333ca5aaedf18d235712bec49a4ae4fbe9606c12
#     Image:          rasor/usingnetcoredockerkubernetes:frontend2-v1
#     Image ID:       
#     Port:           5000/TCP
#     Host Port:      0/TCP
#     State:          Running
#       Started:      Tue, 27 Oct 2020 12:24:42 +0100
#     Ready:          True
#     Restart Count:  0
#     Environment:    <none>
#     Mounts:
#       /var/run/secrets/ from default-token-95gtj (ro)
# Conditions:
#   Type              Status
#   Initialized       True
#   Ready             True
#   ContainersReady   True
#   PodScheduled      True
# Volumes:
#   default-token-95gtj:
#     Type:        Secret (a volume populated by a Secret)
#     SecretName:  default-token-95gtj
#     Optional:    false
# QoS Class:       BestEffort
# Node-Selectors:  <none>
# Tolerations: for 300s
#         for 300s
# Events:
#   Type    Reason     Age        From                         Message
#   ----    ------     ----       ----                         -------
#   Normal  Scheduled  <unknown>                               Successfully assigned default/frontend2 to kind-control-plane
#   Normal  Pulled     11s        kubelet, kind-control-plane  Container image "rasor/usingnetcoredockerkubernetes:frontend2-v1" already present 
# on machine
#   Normal  Created    11s        kubelet, kind-control-plane  Created container frontend2
#   Normal  Started    11s        kubelet, kind-control-plane  Started container frontend2

# And what does that mean?
kubectl explain pods

# read all deployed in default namespace
kubectl get all
# pod/frontend2   1/1     Running   0          6m57s

# NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
# service/kubernetes   ClusterIP    <none>        443/TCP   38d

API into k8s

Start proxy in another terminal.
This gives HTTP access to the k8s API on port 8001

kubectl proxy

Then open browser

Try to reach pod via api:

Nice - the pod responds with a webpage :-)

# print stdout from the container
kubectl logs frontend2
#       Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
#       No XML encryptor configured. Key {d848e8f7-df39-44b3-902c-b11019a1d9ab} may be persisted to storage in unencrypted form.
#       Now listening on: http://[::]:5000
#       Application started. Press Ctrl+C to shut down.
#       Hosting environment: Production
#       Content root path: /app
#       Failed to determine the https port for redirect.

Stop the proxy with ctrl-c and instead use port forwarding:

# Give localhost TCP access from port 5000 to the POD port 5000
kubectl port-forward pod/frontend2 5000:5000
# Forwarding from -> 5000
# Forwarding from [::1]:5000 -> 5000

# browse to pod
start http://localhost:5000

Web responds from pod

# Delete the pod
kubectl delete pod frontend2
# pod "frontend2" deleted


UI into k8s

To manage k8s you could use many different apps like

  • Kubernetes VSCode plugin
    • Double-click on a resource to see its yaml
    • View helm repos
    • View cloud clusters
  • kubernetes-dashboard - just read - install via arkade or from remote yml file
# install kubernetes-dashboard as a k8s app
kubectl create -f
# get login token
kubectl -n kube-system get secret
kubectl -n kube-system describe secret deployment-controller-token-?????
kubectl proxy
# paste login token into
start http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login
  • portainer - install via arkade
  • Rancher
    • docker run -d --restart=unless-stopped -p 80:80 -p 443:443 rancher/rancher
  • Kubernetic

(Image from Rancher)

Declare your infrastructure using yaml

To define managed k8s infrastructure you write k8s yaml.
Install Kubernetes Support in VSCode.
This will provide you with snippets for pods, services, deployments, etc.

Now create a new file called k8s-deploy-dev.yml

  • Write kind: De then
  • Press ctrl-space and select a Deployment template.

With yml created you create or apply the file using -f.
When you apply k8s will create-if-not-exist or change-if-not-correct.
If you create you should create --save-config. Create will throw error, if resource exists.

These are the resources you can create (with the snippets):

clusterrole         - Create a ClusterRole.
clusterrolebinding  - Create a ClusterRoleBinding for a particular ClusterRole
configmap           - Create a configmap from a local file, directory or literal value
cronjob             - Create a cronjob with the specified name.
deployment          - Create a deployment with the specified name.
job                 - Create a job with the specified name.
namespace           - Create a namespace with the specified name
poddisruptionbudget - Create a pod disruption budget with the specified name.
priorityclass       - Create a priorityclass with the specified name.
quota               - Create a quota with the specified name.
role                - Create a role with single rule.
rolebinding         - Create a RoleBinding for a particular Role or ClusterRole
secret              - Create a secret using specified subcommand
service             - Create a service using specified subcommand.
serviceaccount      - Create a service account with the specified name

Besides those above snippets you can find C# helm chart templates for

  • deployment
  • ingress
  • service ... used by the k8s App builder tool Draft.
    Note: draft is not yet upgraded to core 3.1, but is using core 2.1. The project is archieved!

You can also just grap the whole yaml from this blog: Build ASP.NET Core applications deployed as Linux containers into AKS/Kubernetes clusters
and correct then names.

Here is the file:

# k8s-deploy-dev.yml
apiVersion: apps/v1
kind: Deployment
  name: frontend2
    app: usingnetcoredockerkubernetes
  replicas: 1
      service: frontend2
        app: usingnetcoredockerkubernetes
        service: frontend2
        - name: frontend2
          image: rasor/usingnetcoredockerkubernetes:frontend2-v1
          imagePullPolicy: IfNotPresent
            - containerPort: 5000
              protocol: TCP
            - name: ASPNETCORE_URLS
              value: http://+:5000
apiVersion: v1
kind: Service
  name: frontend2
    app: usingnetcoredockerkubernetes
    service: frontend2
  type: LoadBalancer
    - port: 5000
      targetPort: 5000
      protocol: TCP
    service: frontend2

You can print all resource types with
kubectl api-resources --sort-by=kind
They are

NAME                              SHORTNAMES   APIGROUP                       NAMESPACED   KIND
bindings                                                                      true         Binding
componentstatuses                 cs                                          false        ComponentStatus
configmaps                        cm                                          true         ConfigMap
endpoints                         ep                                          true         Endpoints
limitranges                       limits                                      true         LimitRange
namespaces                        ns                                          false        Namespace
nodes                             no                                          false        Node
persistentvolumes                 pv                                          false        PersistentVolume
persistentvolumeclaims            pvc                                         true         PersistentVolumeClaim
pods                              po                                          true         Pod
podtemplates                                                                  true         PodTemplate
replicationcontrollers            rc                                          true         ReplicationController
resourcequotas                    quota                                       true         ResourceQuota
secrets                                                                       true         Secret
services                          svc                                         true         Service
serviceaccounts                   sa                                          true         ServiceAccount

mutatingwebhookconfigurations           false        MutatingWebhookConfiguration
validatingwebhookconfigurations         false        ValidatingWebhookConfiguration
customresourcedefinitions         crd,crds           false        CustomResourceDefinition
apiservices                                   false        APIService
controllerrevisions                            apps                           true         ControllerRevision
daemonsets                        ds           apps                           true         DaemonSet
deployments                       deploy       apps                           true         Deployment
replicasets                       rs           apps                           true         ReplicaSet
statefulsets                      sts          apps                           true         StatefulSet
tokenreviews                                   false        TokenReview
selfsubjectaccessreviews                        false        SelfSubjectAccessReview
selfsubjectrulesreviews                         false        SelfSubjectRulesReview
subjectaccessreviews                            false        SubjectAccessReview
localsubjectaccessreviews                       true         LocalSubjectAccessReview
horizontalpodautoscalers          hpa          autoscaling                    true         HorizontalPodAutoscaler
cronjobs                          cj           batch                          true         CronJob
jobs                                           batch                          true         Job
certificatesigningrequests        csr            false        CertificateSigningRequest
leases                                           true         Lease
endpointslices                                      true         EndpointSlice
events                            ev                   true         Event
nodes                                                 false        NodeMetrics
pods                                                  true         PodMetrics
ingresses                         ing              true         Ingress
ingressclasses                                     false        IngressClass
networkpolicies                   netpol              true         NetworkPolicy
runtimeclasses                                           false        RuntimeClass
poddisruptionbudgets              pdb          policy                         true         PodDisruptionBudget
podsecuritypolicies               psp          policy                         false        PodSecurityPolicy
clusterroles                               false        ClusterRole
clusterrolebindings                        false        ClusterRoleBinding
roles                                      true         Role
rolebindings                               true         RoleBinding
priorityclasses                   pc               false        PriorityClass
csidrivers                                            false        CSIDriver
csinodes                                              false        CSINode
storageclasses                    sc                  false        StorageClass
volumeattachments                                     false        VolumeAttachment

With the yaml in place you can now create or apply it:

# deploy image to k8s
kubectl create -f k8s-deploy-dev.yml
# deployment.apps/frontend2 created
# service/frontend2 created

# print
kubectl get services | grep frontend2
# NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
# frontend2    LoadBalancer   <pending>     5000:30300/TCP   53s

# remove service
kubectl delete service frontend2
# service "frontend2" deleted

# remove replicaset, pod and deployment
kubectl delete deployment frontend2
# deployment.apps "frontend2" deleted
# stop the k8s cluster
docker stop kind-control-plane


The End