<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcin Wolnik</title>
    <description>The latest articles on DEV Community by Marcin Wolnik (@wolnikmarcin).</description>
    <link>https://dev.to/wolnikmarcin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F282839%2Fa5cb7694-3bfc-4ca2-a892-f14661ef299f.jpg</url>
      <title>DEV Community: Marcin Wolnik</title>
      <link>https://dev.to/wolnikmarcin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wolnikmarcin"/>
    <language>en</language>
    <item>
      <title>How to health check your TopShelf service in Kubernetes</title>
      <dc:creator>Marcin Wolnik</dc:creator>
      <pubDate>Thu, 26 Dec 2019 21:55:30 +0000</pubDate>
      <link>https://dev.to/wolnikmarcin/how-to-health-check-your-topshelf-service-in-kubernetes-3phl</link>
      <guid>https://dev.to/wolnikmarcin/how-to-health-check-your-topshelf-service-in-kubernetes-3phl</guid>
      <description>&lt;p&gt;Recently I worked on migrating a legacy TopShelf service from VM to Kubernetes. &lt;/p&gt;

&lt;p&gt;The service was pretty unstable and from time to time transitioned into a broken state and had to be restarted manually, which was less than ideal.&lt;/p&gt;

&lt;p&gt;However, moving the service to Kubernetes won't solve the issue in itself, because no matter how many pods would be defined in &lt;em&gt;Deployment&lt;/em&gt; they would eventually fail one by one due to deadlock, infinite loop, etc and Kubernetes would never know since the processes would appear as still happily running.&lt;/p&gt;

&lt;h1&gt;
  
  
  Health checks in Kubernetes
&lt;/h1&gt;

&lt;p&gt;To remedy such situations we can use one of the Kubernetes's health checks the &lt;em&gt;liveness probe&lt;/em&gt; which will signal Kubernetes if your service is dead or alive. The other one is the &lt;em&gt;readiness probe&lt;/em&gt; which tells the Kubernetes when the container is ready to accept the traffic. In this post, I'll focus on the former.&lt;/p&gt;

&lt;p&gt;There are 3 types of liveness probes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP&lt;/li&gt;
&lt;li&gt;TCP&lt;/li&gt;
&lt;li&gt;Command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll show how to set up and use the most popular one - HTTP type. &lt;/p&gt;

&lt;p&gt;What does it do? &lt;/p&gt;

&lt;p&gt;The probe performs an HTTP GET request on the pod's IP address, a port, and path specified in the pod's specification. If the probe receives 2xx or 3xx HTTP response code, then the pod is considered as healthy. If the server returns an error response code &amp;gt;= 400 or if it doesn’t respond at all, the probe is considered a failure and the pod will be restarted.&lt;/p&gt;

&lt;h1&gt;
  
  
  Self-host TopShelf
&lt;/h1&gt;

&lt;p&gt;Since TopShelf by default is not an HTTP Server, we have to self-host it using OWIN (Open Web Interface for .NET).&lt;/p&gt;

&lt;p&gt;To do so I've used &lt;a href="https://github.com/dennisroche/TopShelf.Owin"&gt;TopShelf.Owin&lt;/a&gt; NuGet package.&lt;/p&gt;

&lt;p&gt;Start by installing the package:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Install-Package Topshelf.Owin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then change your startup code and configure OWIN endpoint to run on &lt;code&gt;8080&lt;/code&gt; port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HostFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHeadacheService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConstructUsing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MyHeadacheService&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenStarted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenStopped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Stop&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OwinEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, create a new file &lt;code&gt;HealthcheckController&lt;/code&gt; and define the API Controller for health checks. I'm using &lt;code&gt;Route("")&lt;/code&gt; which means the health check endpoint will run at  &lt;code&gt;localhost:8080&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The health check endpoint will assume the healthiness of our service as long as a static property &lt;code&gt;HealthCheckStatus.IsHealthy&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One issue with this kind of check is that it may not verify the responsiveness of the service thus it's important the check performs its task in a similar way as the dependent services would do. On the other hand, it shouldn't check its dependencies since it's the &lt;em&gt;readiness probe&lt;/em&gt; task as I've mentioned earlier.&lt;/p&gt;

&lt;p&gt;For testing purposes, we will add an additional endpoint which will put our service into an artificial broken state so the health check will return &lt;code&gt;500&lt;/code&gt; status code instead of &lt;code&gt;200&lt;/code&gt;. Please note I'm using a &lt;code&gt;Serilog&lt;/code&gt; logger that will output to console every time the endpoint is hit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HealthcheckController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ApiController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IHttpActionResult&lt;/span&gt; &lt;span class="nf"&gt;Healthcheck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Healthcheck invoked using User-Agent: {UserAgent}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserAgent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HealthCheckStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsHealthy&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpActionResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;InternalServerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I'm not healthy. Restart me."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"break"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IHttpActionResult&lt;/span&gt; &lt;span class="nf"&gt;Break&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ups!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;HealthCheckStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsHealthy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HealthCheckStatus&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsHealthy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Defining liveness probe
&lt;/h1&gt;

&lt;p&gt;Our facelifted TopShelf service is now ready to serve Kubernetes probes. Now we have to let Kubernetes know where to health check our service.&lt;/p&gt;

&lt;p&gt;In pod specification we add the section to tell Kubernetes to perform the liveness probe on pod's path &lt;code&gt;/&lt;/code&gt; and port &lt;code&gt;8080&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;livenessProbe:
  httpGet:
    path: /
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initialDelaySeconds&lt;/code&gt; - This is the delay which tells kubelet (node agent) to wait for 5 seconds before performing the first probe&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;periodSeconds&lt;/code&gt; -  specifies that the kubelet should perform a liveness probe every 5 seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The default and minimal timeout for the probe request is 1 second and can be configured using &lt;code&gt;timeoutSeconds&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To see other configuration options, have a look at the official &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we can deploy our pod(s) on a cluster and see the probes being sent to the pod.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl logs -f podname&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[20:25:41 INF] Healthcheck invoked using User-Agent: kube-probe/1.15
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With our breaking endpoint (&lt;code&gt;:8080/break/&lt;/code&gt;) we can now simulate an artificial broken state.&lt;/p&gt;

&lt;p&gt;Let's shell into a running pod:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl exec -it podname cmd&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and trigger failure:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -v localhost:8080/break&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Wait long enough so that the probe had a chance to perform and see the failed probe in pod's events:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl describe pod podname&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Events:
Type     Reason     Age                  From                     Message
----     ------     ----                 ----                     -------
Warning  Unhealthy  40s (x3 over 70s)    kubelet, *** Liveness probe failed: HTTP probe 
failed with statuscode: 500
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Verify it has been restarted by listing the pod:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl get pod podname&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME      READY   STATUS    RESTARTS   AGE
podname   1/1     Running   1          1h

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see the RESTARTS column shows just that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;By self-hosting a TopShelf service and adding a health check endpoint to it you can leverage Kubernetes liveness probes, which can greatly improve the robustness and resilience of your service.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>csharp</category>
      <category>devops</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Run ASP.NET Core 3 on Kubernetes with Helm.</title>
      <dc:creator>Marcin Wolnik</dc:creator>
      <pubDate>Sun, 01 Dec 2019 13:34:10 +0000</pubDate>
      <link>https://dev.to/wolnikmarcin/run-asp-net-core-3-on-kubernetes-with-helm-1o01</link>
      <guid>https://dev.to/wolnikmarcin/run-asp-net-core-3-on-kubernetes-with-helm-1o01</guid>
      <description>&lt;p&gt;In this post I'm going to show you step-by-step how to create, build and host ASP.NET Core 3 web API on Kubernetes with a little help from Helm 3.&lt;/p&gt;

&lt;p&gt;Whole sample is available on GitHub: &lt;a href="https://github.com/Crokus/aspnet-core-helm-sample" rel="noopener noreferrer"&gt;https://github.com/Crokus/aspnet-core-helm-sample&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;p&gt;I've been using Windows 10 Pro but you should be fine running it on Linux or MacOS. &lt;/p&gt;

&lt;p&gt;There's a few things you have to install before going further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hub.docker.com/?overlay=onboarding" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt; - version 2.1.0.5 or higher&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/download/dotnet-core/3.0" rel="noopener noreferrer"&gt;.Net Core 3.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v3.helm.sh/docs/intro/install/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; - version 3.0.0 or higher&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not necessary but using &lt;a href="https://code.visualstudio.com/Download" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; will help a lot going through the example.&lt;/p&gt;

&lt;h1&gt;
  
  
  Goal
&lt;/h1&gt;

&lt;p&gt;Create three instances of ASP.NET Core 3 web API behind a 'load balancer' that will route to any of them on Kubernetes local cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F73e1ozkebvb456mlxoqa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F73e1ozkebvb456mlxoqa.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Create ASP.NET Core 3 Web API
&lt;/h1&gt;

&lt;p&gt;Let's start by creating a new Web API using &lt;code&gt;dotnet new&lt;/code&gt; command. Open Git Bash as an administrator and type command which will create a project template in &lt;code&gt;app&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new webapi &lt;span class="nt"&gt;-o&lt;/span&gt; app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, open the &lt;code&gt;app&lt;/code&gt; directory and launch the web API&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;app/
dotnet run watch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;localhost:5000/weatherforecast&lt;/code&gt; to make sure the app is running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;:&lt;span class="s2"&gt;"2019-12-01T18:13:28.0973127+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"temperatureC"&lt;/span&gt;:35,&lt;span class="s2"&gt;"temperatureF"&lt;/span&gt;:94,&lt;span class="s2"&gt;"summary"&lt;/span&gt;:&lt;span class="s2"&gt;"Warm"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;:&lt;span class="s2"&gt;"2019-12-02T18:13:28.0975531+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"temperatureC"&lt;/span&gt;:-14,&lt;span class="s2"&gt;"temperatureF"&lt;/span&gt;:7,&lt;span class="s2"&gt;"summary"&lt;/span&gt;:&lt;span class="s2"&gt;"Scorching"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;:&lt;span class="s2"&gt;"2019-12-03T18:13:28.0975584+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"temperatureC"&lt;/span&gt;:4,&lt;span class="s2"&gt;"temperatureF"&lt;/span&gt;:39,&lt;span class="s2"&gt;"summary"&lt;/span&gt;:&lt;span class="s2"&gt;"Cool"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;:&lt;span class="s2"&gt;"2019-12-04T18:13:28.0975588+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"temperatureC"&lt;/span&gt;:-1,&lt;span class="s2"&gt;"temperatureF"&lt;/span&gt;:31,&lt;span class="s2"&gt;"summary"&lt;/span&gt;:&lt;span class="s2"&gt;"Mild"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"date"&lt;/span&gt;:&lt;span class="s2"&gt;"2019-12-05T18:13:28.0975591+01:00"&lt;/span&gt;,&lt;span class="s2"&gt;"temperatureC"&lt;/span&gt;:-14,&lt;span class="s2"&gt;"temperatureF"&lt;/span&gt;:7,&lt;span class="s2"&gt;"summary"&lt;/span&gt;:&lt;span class="s2"&gt;"Hot"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add environment variables to local host
&lt;/h2&gt;

&lt;p&gt;Weather data are interesting but let's try to output environment variables that will later prove that we're in a different host.  &lt;/p&gt;

&lt;p&gt;For localhost we'll use below variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;APPENVIRONMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;APPHOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop the app and add a new controller that will output these variables. Note the routing attribute which is set to root path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;app.Controllers&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InfoController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InfoController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;InfoController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InfoController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;InfoModel&lt;/span&gt; &lt;span class="nf"&gt;GetInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;InfoModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;AppEnvironment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"APPENVIRONMENT"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;AppHost&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"APPHOST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Getting environment variable '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUpper&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the app again and check the response on &lt;code&gt;localhost:{port}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"appEnvironment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"appHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have our web API ready to ship to another host. To do so, we need to create a docker image of it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a docker image
&lt;/h1&gt;

&lt;p&gt;First step of containerization is to create a Dockerfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/core/sdk:3.0-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# copy csproj and restore&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app/*.csproj ./aspnetapp/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ./aspnetapp/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; dotnet restore 

&lt;span class="c"&gt;# copy all files and build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app/. ./aspnetapp/&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app/aspnetapp&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; out


&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/core/aspnet:3.0-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runtime&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/aspnetapp/out ./&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "dotnet", "app.dll" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having Dockerfile ready, we can build our image which will be named &lt;code&gt;aspnet3k8s&lt;/code&gt; and tagged as &lt;code&gt;v1&lt;/code&gt;. We need to navigate one level up from &lt;code&gt;app&lt;/code&gt; directory and run following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image build &lt;span class="nt"&gt;--pull&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; aspnet3k8s:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having an image built we can run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:80 aspnet3k8s:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browse &lt;code&gt;localhost:9000&lt;/code&gt; and you should see &lt;code&gt;null&lt;/code&gt; values for both variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"appEnvironment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"appHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a good sign. After all we're now in a different environment running on a docker host.&lt;/p&gt;

&lt;p&gt;Stop the command and try passing new pair of environment variables for the new host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;APPENVIRONMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;APPHOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docker &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 9000:80 aspnet3k8s:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and check the response again on &lt;code&gt;localhost:9000&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"appEnvironment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"appHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good!&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a Helm's Chart
&lt;/h1&gt;

&lt;p&gt;Finally we've got to the meat of the matter. We have our ASP.NET app containerized and we can deploy it on a Kubernetes cluster. We won't do it directly but use a popular "package manager" meant for Kubernetes - Helm.&lt;/p&gt;

&lt;p&gt;Remember, our goal is to create three instances of our ASP.NET Core 3 web API behind a 'load balancer' that will route to any of them.&lt;/p&gt;

&lt;p&gt;Translating it to a Kubernetes nomenclature we're going to create a Deployment that will in turn create a ReplicaSet consisted of 3 pods (instances) and a Service which will serve as a 'load balancer' forwarding requests to any of them from outside world.&lt;/p&gt;

&lt;p&gt;If you haven't done it already, it's high time for installing both Docker Desktop and Helm.&lt;/p&gt;

&lt;p&gt;Before going further it might be good to read &lt;a href="https://v3.helm.sh/docs/intro/using_helm/" rel="noopener noreferrer"&gt;Helm's three big concepts&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize Chart
&lt;/h3&gt;

&lt;p&gt;We could do it using &lt;code&gt;helm create NAME&lt;/code&gt; command but it would create a lot of files we don't need for the purposes of this guide.&lt;/p&gt;

&lt;p&gt;Thus, let's create a following structure on a root folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chart/
  Chart.yaml
  values.yaml
  templates/
    deployment.yaml
    service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First let's have a look at &lt;code&gt;Chart.yaml&lt;/code&gt; which specifies name and version of the Chart. There are other fields you can add too, but only these are required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aspnet3-demo&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Chart values
&lt;/h3&gt;

&lt;p&gt;Then, there's &lt;code&gt;values.yaml&lt;/code&gt;, which specifies our default settings that will be parsed and injected into templates inside &lt;code&gt;/templates&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;We define our environment name there. You can also see our image name &lt;code&gt;aspnet3k8s&lt;/code&gt; and &lt;code&gt;replicas: 3&lt;/code&gt; field which specifies how many instances of our web API we'd like to have. &lt;br&gt;
Please note I've added a field &lt;code&gt;pullPolicy: IfNotPresent&lt;/code&gt; because our image is only on our local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;

&lt;span class="na"&gt;apphost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k8s&lt;/span&gt;

&lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aspnet3core&lt;/span&gt;

&lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aspnet3&lt;/span&gt;
  &lt;span class="na"&gt;pullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aspnet3k8s&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8888&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good, but you might ask yourself how do these &lt;code&gt;values&lt;/code&gt; YAMLs bring as closer to our goal? Answer is, these values are referenced in our templates. Let's examine them now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chart templates
&lt;/h3&gt;

&lt;p&gt;We'll start with &lt;code&gt;deployment.yaml&lt;/code&gt;. This template will create a Deployment object that will in turn create ReplicaSet consisted of 3 pods each running our web API container image. Have a look at all references to values we defined in &lt;code&gt;values.yaml&lt;/code&gt;. Note a Helm's built-in object &lt;code&gt;.Release&lt;/code&gt; that we'll use for naming our deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-deployment&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.label.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.replicas&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.label.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.label.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.environment&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.container.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.container.image&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;:{{ .Values.container.tag }}&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.container.pullPolicy&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.container.port&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apphost&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.apphost&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appenvironment&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.environment&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will make sure 3 instances of our web API will be running on our local cluster. But without a 'load balancer' we won't access any of them. For that we need a Service which template is defined in &lt;code&gt;service.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;-service&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.label.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.port&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.container.port&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.label.name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.service.type&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're all set and we can finally deploy our Chart on our local cluster!&lt;/p&gt;

&lt;h1&gt;
  
  
  The grand finale
&lt;/h1&gt;

&lt;p&gt;Run the following command to install our Chart as a release named &lt;code&gt;aspnet3release&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;aspnet3release ./chart/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a successful response&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME: aspnet3release
LAST DEPLOYED: Sun Dec  1 12:07:53 2019
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see what exactly has been deployed on a Kubernetes cluster using &lt;code&gt;kubectl&lt;/code&gt; command. Note that we're using a selector to match only those resources which have label &lt;code&gt;app=aspnet3core&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all &lt;span class="nt"&gt;--selector&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aspnet3core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which should output following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                            READY   STATUS    RESTARTS   AGE
pod/aspnet3release-deployment-77686884b-fq5lq   1/1     Running   0          3m43s
pod/aspnet3release-deployment-77686884b-llcsv   1/1     Running   0          3m43s
pod/aspnet3release-deployment-77686884b-qswsj   1/1     Running   0          3m43s

NAME                             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/aspnet3release-service   ClusterIP   10.107.172.66   &amp;lt;none&amp;gt;        8888/TCP   3m43s

NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/aspnet3release-deployment   3/3     3            3           3m43s

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/aspnet3release-deployment-77686884b   3         3         3       3m43s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we have here. Three pods that runs our web API a ReplicaSet and a Service that listens on &lt;code&gt;8888&lt;/code&gt; port. Great, let's browse then &lt;code&gt;localhost:8888&lt;/code&gt; to see our API in action!&lt;/p&gt;

&lt;p&gt;Nothing happens. Why? Because the Service's port is not on your local machine but inside the cluster. Thus, we have to forward a port on our local machine to Service's port using &lt;code&gt;kubectl port-forward&lt;/code&gt; command. We could reuse 8888 port, but let's use 9999 instead to make a clear distinction what's where. &lt;/p&gt;

&lt;p&gt;From the previous command &lt;code&gt;kubectl get all&lt;/code&gt; we know that our service can be identified as &lt;code&gt;service/aspnet3release-service&lt;/code&gt;. Let's try.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward service/aspnet3release-service 9999:8888
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browse &lt;code&gt;localhost:9999&lt;/code&gt; and... Success!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"appEnvironment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"appHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"k8s"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Updating our deployment
&lt;/h3&gt;

&lt;p&gt;You might think why I've used Helm instead of creating standard Kuberentes definitions for both Deployment and Service object. It's quite handy that some values are not copied by value but referenced but still - why make such a fuss of it? &lt;/p&gt;

&lt;p&gt;Imagine we'd like to go live with our application. We could copy our development YAMLs and change the values here and there but that would be very error-prone and hard to maintain. Instead using Helm we can simply create a new set of values for production and store it in a new file &lt;code&gt;production-values.yaml&lt;/code&gt;. Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For simplicity we'll just upgrade our development using &lt;code&gt;helm upgrade&lt;/code&gt; command and pass &lt;code&gt;production-values.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade aspnet3release ./chart &lt;span class="nt"&gt;--values&lt;/span&gt; ./chart/production-values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Release &lt;span class="s2"&gt;"aspnet3release"&lt;/span&gt; has been upgraded. Happy Helming!
NAME: aspnet3release
LAST DEPLOYED: Sun Dec  1 12:40:00 2019
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, let's verify how it looks like on the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get all &lt;span class="nt"&gt;--selector&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aspnet3core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you should see 5 instances of our app. That was easy, isn't it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME                                            READY   STATUS    RESTARTS   AGE
pod/aspnet3release-deployment-774c478b4-64jgp   1/1     Running   0          12m
pod/aspnet3release-deployment-774c478b4-fx8bs   1/1     Running   0          12m
pod/aspnet3release-deployment-774c478b4-mkplm   1/1     Running   0          12m
pod/aspnet3release-deployment-774c478b4-mnwgb   1/1     Running   0          12m
pod/aspnet3release-deployment-774c478b4-xnd9b   1/1     Running   0          12m

NAME                             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;    AGE
service/aspnet3release-service   ClusterIP   10.107.172.66   &amp;lt;none&amp;gt;        8888/TCP   44m

NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/aspnet3release-deployment   5/5     5            5           44m

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/aspnet3release-deployment-774c478b4   5         5         5       12m
replicaset.apps/aspnet3release-deployment-77686884b   0         0         0       44m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've also changed the environment variable, let's verify that too. Again we have to port-forward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward service/aspnet3release-service 9999:8888
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and browse &lt;code&gt;localhost:9999&lt;/code&gt; to see the enivronment has been changed to &lt;code&gt;production&lt;/code&gt;. Cool!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"appEnvironment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"appHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"k8s"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was quite a journey. We've gone through creating a new ASP.NET Core app, containerizing it, creating Helm's chart,deploying it on Kubernetes cluster and finally updating it.&lt;/p&gt;

&lt;p&gt;Oh, almost forgot. To clean our cluster use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm uninstall aspnet3release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Credits
&lt;/h1&gt;

&lt;p&gt;The idea to create this guide was inspired by a great book written by John Arundel and Justin Domingus: &lt;a href="http://shop.oreilly.com/product/0636920175131.do" rel="noopener noreferrer"&gt;Cloud Native DevOps with Kubernetes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading. I hope you liked it.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>dotnet</category>
      <category>webdev</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
