Photo by Leone Venter on Unsplash
Recently, I got a note from an attendee I met at the Getting Function'l with Kubernetes event about running .NET core applications on Kubernetes. It got me thinking, "how far has the Windows and .NET world come regarding running cloud native apps in the last year?" I remembered when I filmed my LinkedIn Learning tutorial on Kubernetes; it wasn't that difficult to run Kubernetes with Minikube on Windows, but what about running .NET apps?
I took some time to explore the ecosystem, and in this post, I'll show you how to run a sample .NET application on the Oracle Container Engine. If you're a developer and want to get straight to the code, you can check out my entire walkthrough on GitHub. I built all of these examples on my mac, but you should be able to do all of this on any platform you choose. If for some reason you get stuck, comment below, and I can try to replicate your issues!
It's been a good five years since I wrote my last .NET application, so I had to install all the things on my mac. I started with the simple .NET tutorial published on Microsoft to create a .NET Core Web API application. I thought about adding more endpoints and code to it, but in the end, kept it the same as the generated code to keep it simple. I didn't want to confuse the reader with .NET REST API magic, but instead, get the application running in the Kubernetes platform. As a side note, it is SUPER cool to have the dotnet command now. It makes development, building, and testing all very seamless and is innate to cloud native developers who run docker and kubectl commands all day long.
After installing the needful, I created a simple ASP.NET Core Web API application with the command: ~$ dotnet new webapi --auth None --no-https
For a full-fledged app, you will most likely want to set up authentication if you're working with sensitive data, and turn on https as well. Running the command will create a new webapp that creates a simple REST API. The output looks something like:
~$ dotnet new webapi --auth None --no-https The template "ASP.NET Core Web API" was created successfully. Processing post-creation actions... Running 'dotnet restore' on /Users/karthik/dev/src/github.com/karthequian/dotnet-example/dotnet-example.csproj... Restoring packages for /Users/karthik/dev/src/github.com/karthequian/dotnet-example/dotnet-example.csproj... Generating MSBuild file /Users/karthik/dev/src/github.com/karthequian/dotnet-example/obj/dotnet-example.csproj.nuget.g.props. Generating MSBuild file /Users/karthik/dev/src/github.com/karthequian/dotnet-example/obj/dotnet-example.csproj.nuget.g.targets. Restore completed in 1.42 sec for /Users/karthik/dev/src/github.com/karthequian/dotnet-example/dotnet-example.csproj. Restore succeeded.
Great! Now, to run this newly created application, I can type dotnet run, and it should launch my API as shown below:
~$ dotnet run Using launch settings from /Users/karthik/dev/src/github.com/karthequian/dotnet-example/Properties/launchSettings.json... : Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] User profile is available. Using '/Users/karthik/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. Hosting environment: Development Content root path: /Users/karthik/dev/src/github.com/karthequian/dotnet-example Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down.
If I wanted to test, I could curl the /api/values/ endpoint as shown below. If I get a list of values back, I know that the application is working as expected.
~$ curl localhost:5000/api/values/ ["value1","value2"]
Now that I have a .NET webapp running on my mac, I can do the fun stuff - dockerize and kubify!
To dockerize, I followed the guidelines to build a Docker image for a .NET application from the Docker documentation. I changed the name of the entrypoint to dotnet-example.dll because that was the name of my project. You can find my final Dockerfile here. After running and testing again with curl, I pushed the application to the Docker store as karthequian/dotnetexample, so that anyone can just run the example as a test.
Finally, to run this in Kubernetes, I created a deployment and service here: https://github.com/karthequian/dotnet-example/blob/master/Deployment.yaml. This yaml exposes the container as a deployment with a nodePort service running on port 32000 of the host. For ease of use, you can type
kubectl apply -f https://raw.githubusercontent.com/karthequian/dotnet-example/master/Deployment.yamlto run the deployment and service on Kubernetes. I chose to run my application in the Oracle Container Engine as a proof of concept, but it should work in any Kubernetes distro out there. I've tested this on a Kubernetes 1.9.7 cluster, but it will be forward compatible with the latest version as well.
Following is an example to run this:
~$ kubectl apply -f https://raw.githubusercontent.com/karthequian/dotnet-example/master/Deployment.yaml deployment "dotnetworld" created service "dotnetworld" created ~$ kubectl get deployments NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE dotnetworld 1 1 1 1 20s ~$ kubectl get services NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE dotnetworld 10.96.79.93 80:32080/TCP 26s kubernetes 10.96.0.1 443/TCP 30d
Finally, to test this out, we can once again run a curl against the node IP and port as shown below:
~$ kubectl get services NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE dotnetworld 10.96.79.93 80:32080/TCP 26s kubernetes 10.96.0.1 443/TCP 30d ~/dev/src/github.com/karthequian/dotnet-example$ kubectl get nodes NAME STATUS AGE VERSION 129.146.123.174 Ready 30d v1.9.7 129.146.133.234 Ready 30d v1.9.7 129.146.162.102 Ready 30d v1.9.7 ~$ curl 129.146.162.102:32080/api/values ["value1","value2"]
So there you have it - a .NET core web application, built on a mac, containerized with Docker and running in Kubernetes 1.9.7, managed by the Oracle Container Service for Kubernetes. It might sound time consuming, but I was able to get all of this running in about an hour, so it wasn't that much work at all.
Let me know if you run into issues or have any questions by commenting below, finding me on twitter @iteration1 or in the GitHub repo itself. Good luck!!