Demystifying Kubernetes for developers

As a developer, what should we know to work with Kubernetes? Also How much we should know?

In this blog post, I will be using eShop, a reference .NET application implementing an eCommerce site. The reference application is structured upon a service-centric architecture.

Assumption: It is assumed that reader is aware of Docker, Image, Container, Container Registry, Docker Compose and Docker Desktop .

eShop application's Architecture and Home page

This diagram depicts application architecture and it's components.

This is the homepage of the eShop application. Here, users have the opportunity to explore product catalog and utilize filtering options based on brand and type.

Prerequisites

  • .NET 8.0
  • .NET Aspire workload
  • Docker Desktop
  • Visual Studio Community 2022 - Preview version 17.10.0. Preview 5.0

External Dependencies

  • Redis
  • RabbitMQ
  • PostgreSQL

NOTE: The above dependencies will be managed as a container using Docker Desktop, during development.

Visual Studio Solution Structure

The below table describes, all available projects in eShop.Web.slnf.

ProjectTypeAccess Type
Basket.APIASP.Net Core gRPC ServiceInternal
PaymentProcessorASP.NET Web AppInternal
Catalog.APIASP.NET Core WebAPIInternal
Identity.APIASP.NET Core MVCInternal
Ordering.APIASP.NET Core WebAPIInternal
Ordering.DomainClass LibraryInternal
Ordering.InfrastructureClass LibraryInternal
OrderProcessorClass LibraryInternal
WebAppBlazor Server AppExternal
WebAppComponentsBlazor Web ComponentsInternal
Webhooks.APIASP.NET Core WebAPIInternal
WebhookClientASP.NET Core Web AppExternal
eShop.AppHost.NET AspireFor development only
eShop.ServiceDefaults.NET AspireInternal
EventBusClass LibraryInternal
EventBus.RabbitMQClass LibraryInternal
IntegrationEventLogEFClass LibraryInternal
Mobile.Bff.ShoppingASP.NET Web AppExternal

.NET Aspire dashboard and Docker Desktop Dashboard

The Aspire Dashboard will be available after running application using F5 or CTRL + F5 or

# From terminal
dotnet run --project eShop-main/src/eShop.AppHost

From the Aspire dashboard, you can launch various applications, such as Web Apps and APIs, to understand application functionalities.

NOTE: Upon careful review, you'll notice RabbitMQ, Postgres, and Redis are running as containers. Additionally, you'll find four databases: catalogdb, identitydb, orderingdb, and webhookdb. Please refer to the code snippet below from eShop.AppHost, where it is defined.

var redis = builder.AddRedis("redis");
var rabbitMq = builder.AddRabbitMQ("eventbus");
var postgres = builder.AddPostgres("postgres")
    .WithImage("ankane/pgvector")
    .WithImageTag("latest");
 
var catalogDb = postgres.AddDatabase("catalogdb");
var identityDb = postgres.AddDatabase("identitydb");
var orderDb = postgres.AddDatabase("orderingdb");
var webhooksDb = postgres.AddDatabase("webhooksdb");

Within the Docker Desktop dashboard, you can easily view both available (or downloaded) images and currently running containers.

Preparation of Local Docker Container Registry

We will create a local Docker Container Registry to facilitate the push/pull of container images without depending on external registries. This setup will enable us to seamlessly share container images with our Kubernetes cluster without relying on external services. We are using official Distribution image and it is based on OCI Distribution spec.

docker run -d -p 5000:5000 --restart always --name my-registry registry:2

After running above command, you can verify that 'my-registry' container is running.

Using below commands, you can verify content of registry.

curl http://localhost:5000/v2/_catalog

curl http://localhost:5000/v2/bakset-api/tags/list

NOTE: In place of basket-api, you can use other api(s) to see the details.

Preparation of Kind Kubernetes Cluster

We will be using Kind (Kubernetes IN Docker), a tool for creating local multi-node Kubernetes clusters using Docker container nodes.

On Windows, we can install Kind via Chocolaty, for others you can refer Kind's documentation.

choco install kind

After successful installation, you can create Kubernetes cluster using

kind create cluster # default cluster context name is 'kind'
kind create cluster --name kind-2 # custom name
kind create cluster --config kind-config.yaml # we will be using this approach
kind get cluster # for getting cluster
kind delete cluster # for deleting cluster

By default, Kind creates a single Kubernetes node, named 'kind-control-plane', you can use docker ps command to see the running container.

Since we have already created a local container registry e.g., my-registry earlier, we are now creating a multi-node cluster (e.g., control-plane, worker) and configuring Kind to use the registry for pulling container images during deployments.

# You can create a kind-config.yaml containing below content.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"]
    endpoint = ["http://my-registry:5000"]
nodes:
- role: control-plane
- role: worker
# Run this command to create Kind cluster
kind create cluster --config kind-config.yaml

Using this command, we are establishing network between Kind cluster and local docker registry.

docker network connect "kind" "my-registry" # Refer your local container registry

NOTE: Docker Desktop comes with single-node Kubernetes cluster. However, to replicate a real-world environment more accurately, I've chosen to work with a multi-node cluster using Kind.

Build and Deploy

Both the local container registry and Kind Kubernetes cluster are ready, now we can build/deploy the eShop application.

Run this command in eShop.AppHost folder.

# This command allows you to bootstrap certain settings for an Aspire project that Aspir8 will use.
# ContainerRegistry, ContainerTag, TemplatePath
aspirate init
# For reference
PS D:\source\eshop-53\eshop-main\src\eshop.Apphost> aspirate init
 
     _                    _           ___
    / \     ___   _ __   (_)  _ __   ( _ )
   / _ \   / __| | '_ \  | | | '__|  / _ \
  / ___ \  \__ \ | |_) | | | | |    | (_) |
 /_/   \_\ |___/ | .__/  |_| |_|     \___/
                 |_|
Handle deployments of a .NET Aspire AppHost
 
 
── Handle Initialization Defaults ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
(?) Done: Set 'Container builder' to 'docker'.
Aspirate supports setting a fall-back value for projects that have not yet set a 'ContainerRegistry' in their csproj file.
Would you like to set a fall-back value for the container registry? [y/n] (n): y
Please enter the container registry to use as a fall-back value: localhost:5000
(?) Done: Set 'Container fallback registry' to 'localhost:5000'.
Aspirate supports setting a repository prefix for all for projects.
Would you like to set this value? [y/n] (n): n
Aspirate supports setting a fall-back value for projects that have not yet set a 'ContainerTag' in their csproj file.
Would you like to set a fall-back value for the container tag? [y/n] (n): n
Aspirate supports setting a custom directory for 'Templates' that are used when generating kustomize manifests.
Would you like to use a custom directory (selecting 'n' will default to built in templates ? [y/n] (n): n
(?) Done: Configuration for aspirate has been bootstrapped successfully at 'D:\source\eshop-53\eshop-main\src\eshop.Apphost\.\aspirate.json'.
 
 ?? Execution Completed ??
# This command (by default) builds selected projects, pushes the containers to the interpolated ContainerRegistry e.g., **my-registry**, and publishes manifests in the **AppHost/aspirate-output** directory by default.
aspirate generate
# For reference
PS D:\source\eshop-53\eshop-main\src\eshop.Apphost> aspirate generate
 
     _                    _           ___
    / \     ___   _ __   (_)  _ __   ( _ )
   / _ \   / __| | '_ \  | | | '__|  / _ \
  / ___ \  \__ \ | |_) | | | | |    | (_) |
 /_/   \_\ |___/ | .__/  |_| |_|     \___/
                 |_|
Handle deployments of a .NET Aspire AppHost
 
 
── Handling Configuration ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────Successfully loaded existing aspirate bootstrap settings from '.'.
 
── Handling Aspire Manifest ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────Generating Aspire Manifest for supplied App Host:
 
Generating Aspire Manifest...
Building...
info: Aspire.Hosting.DistributedApplication[0]
      Aspire version: 8.0.0-preview.6.24214.1+6596fdc41a8d419876a6bf4abc17b7c66b9ef63a
info: Aspire.Hosting.Publishing.ManifestPublisher[0]
      Published manifest to: D:\source\eshop-53\eshop-main\src\eshop.Apphost\manifest.json
(?) Done:  Created Aspire Manifest At Path: D:\source\eshop-53\eshop-main\src\eshop.Apphost\manifest.json
 
── Selecting Required Components ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 
── Handling Aspire Dashboard ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Would you like to deploy the aspire dashboard and connect the OTLP endpoint? [y/n] (y): n
(!) Skipping Aspire Dashboard deployment
 
── Handling private registry credentials ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 
── Handling Inputs ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Applying values for all automatically generated secrets.
 
Successfully generated a value for eventbus-password's Input Value 'value'
 
Successfully generated a value for postgres-password's Input Value 'value'
You will now be prompted to enter values for any secrets that are not generated automatically.
(?) Done:  Input values have all been assigned.
 
── Handle Value and Parameter Substitution ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 
── Handling DAPR Components ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
No Dapr components selected, skipping Dapr annotations.
 
── Gathering Information about deployables ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Gathering container details for each project in selected components
(?) Done:  Populated container details cache for project identity-api
(?) Done:  Populated container details cache for project basket-api
(?) Done:  Populated container details cache for project catalog-api
(?) Done:  Populated container details cache for project ordering-api
(?) Done:  Populated container details cache for project order-processor
(?) Done:  Populated container details cache for project payment-processor
(?) Done:  Populated container details cache for project webhooks-api
(?) Done:  Populated container details cache for project mobile-bff
(?) Done:  Populated container details cache for project webhooksclient
(?) Done:  Populated container details cache for project webapp
Gathering Tasks Completed - Cache Populated.
 
── Handling Projects ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Building all project resources, and pushing containers
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../Identity.API/Identity.API.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="identity-api" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project identity-api
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../Basket.API/Basket.API.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="basket-api" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project basket-api
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../Catalog.API/Catalog.API.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="catalog-api" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project catalog-api
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../Ordering.API/Ordering.API.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="ordering-api" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project ordering-api
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../OrderProcessor/OrderProcessor.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="order-processor" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project order-processor
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../PaymentProcessor/PaymentProcessor.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="payment-processor" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project payment-processor
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../Webhooks.API/Webhooks.API.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="webhooks-api" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project webhooks-api
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../Mobile.Bff.Shopping/Mobile.Bff.Shopping.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true"
-p:PublishTrimmed="false" --self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="mobile-bff" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project mobile-bff
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../WebhookClient/WebhookClient.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false"
--self-contained "true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="webhooksclient" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project webhooksclient
 
Executing: dotnet publish "D:\source\eshop-53\eshop-main\src\eshop.Apphost\../WebApp/WebApp.csproj" -p:PublishProfile="DefaultContainer" -p:PublishSingleFile="true" -p:PublishTrimmed="false" --self-contained
"true" --verbosity "quiet" --nologo -r "linux-x64" -p:ContainerRegistry="localhost:5000" -p:ContainerRepository="webapp" -p:ContainerImageTag="latest"
(?) Done:  Building and Pushing container for project webapp
Building and push completed for all selected project components.
 
── Handling Dockerfiles ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
No Dockerfile components selected. Skipping build and publish action.
 
── Handle Image Pull Policy ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 
── Populating Secrets File ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Aspirate Secrets already exist for manifest.
Existing Secrets are protected by a password.
 
Please enter it now to confirm secret actions: ***
Using existing secrets for provider Password
 
── Handle Kustomize Manifests ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Generating kustomize manifests to run against your kubernetes cluster:
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\redis
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\eventbus
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\postgres
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\identity-api
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\basket-api
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\catalog-api
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\ordering-api
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\order-processor
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\payment-processor
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\webhooks-api
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\mobile-bff
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\webhooksclient
(?) Done:  Generating D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output\webapp
 
── Handling Final Manifest ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Would you like to generate the top level kustomize manifest to run against your kubernetes cluster? [y/n] (y): y
Generating final manifest with name 'kustomization.yml'
(?) Done:  Generating ./aspirate-output/kustomization.yml
 
── Handling Helm Support ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Would you like to generate a helm chart based on the generated kustomize manifests? [y/n] (n): y
(?) Done:  Generating helm chart at ./aspirate-output\Chart

NOTE: By default Aspirate creates a deployment.yml, kustomization.yml and service.yml for each deployable unit. Important to note that replicas value for all is 1.

# This command applies the manifest to the cluster.
aspirate apply
# For reference
PS D:\source\eshop-53\eshop-main\src\eshop.Apphost> aspirate apply
 
     _                    _           ___
    / \     ___   _ __   (_)  _ __   ( _ )
   / _ \   / __| | '_ \  | | | '__|  / _ \
  / ___ \  \__ \ | |_) | | | | |    | (_) |
 /_/   \_\ |___/ | .__/  |_| |_|     \___/
                 |_|
Handle deployments of a .NET Aspire AppHost
 
 
── Loading Existing Secrets ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Secrets are protected by a password. Please enter it now: ***
(?) Done:  Secret State populated successfully from /aspirate-secrets.json
 
── Handle Deployment to Cluster ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Would you like to deploy the generated manifests to a kubernetes cluster defined in your kubeconfig file? [y/n] (y): y
(?) Done: Successfully set the Active Kubernetes Context to 'kind-kind'
 
Executing: kubectl apply -k D:\source\eshop-53\eshop-main\src\eshop.Apphost\aspirate-output against kubernetes context kind-kind.
configmap/basket-api-env created
configmap/catalog-api-env created
configmap/eventbus-env created
configmap/identity-api-env created
configmap/mobile-bff-env created
configmap/order-processor-env created
configmap/ordering-api-env created
configmap/payment-processor-env created
configmap/postgres-env created
configmap/redis-env created
configmap/webapp-env created
configmap/webhooks-api-env created
configmap/webhooksclient-env created
secret/basket-api-secrets created
secret/catalog-api-secrets created
secret/identity-api-secrets created
secret/order-processor-secrets created
secret/ordering-api-secrets created
secret/payment-processor-secrets created
secret/postgres-secrets created
secret/webapp-secrets created
secret/webhooks-api-secrets created
service/basket-api created
service/catalog-api created
service/eventbus created
service/identity-api created
service/mobile-bff created
service/order-processor created
service/ordering-api created
service/payment-processor created
service/postgres created
service/redis created
service/webapp created
service/webhooks-api created
service/webhooksclient created
deployment.apps/basket-api created
deployment.apps/catalog-api created
deployment.apps/eventbus created
deployment.apps/identity-api created
deployment.apps/mobile-bff created
deployment.apps/order-processor created
deployment.apps/ordering-api created
deployment.apps/payment-processor created
deployment.apps/postgres created
deployment.apps/redis created
deployment.apps/webapp created
deployment.apps/webhooks-api created
deployment.apps/webhooksclient created
(?) Done: Deployments successfully applied to cluster 'kind-kind'
 
 ?? Execution Completed ??

NOTE: Aspirate (Aspir8) parses the eShop.AppHost project and generates all necessary artifacts for Kubernetes without requiring extensive knowledge of Kubernetes internals. In essence, it enables developers to concentrate on addressing business challenges.

Octant

Octant is an open source developer-centric web interface for Kubernetes that lets you inspect a Kubernetes cluster and its applications.

After installation, Octant can be launched to explore Applications, Namespace Overview, and Cluster Overview. As depicted in the images below, you will notice that each of your projects is represented as an individual green circle. The green color signifies that there are no issues and the applications are working fine.

Clicking on application's link, you can see more details. Refer, below image for webapp.

A keen observation reveals that there are 13 Deployments, 13 Pods, 13 ReplicaSets, 13 Services, 13 ConfigMaps, and 9 Secrets. Fear not, as we will delve into these intricacies in the following section.

Setting up an Ingress Controller

An Ingress Controller provides functionality that forward ports from host (e.g., your laptop) to an ingress controller (e.g., NGINX) running in a node. Create a kind cluster with extraPortMappings and node-labels.

  • extraPortMappings allow the local host to make requests to the Ingress controller over ports 80/443
  • node-labels only allow the ingress controller to run on a specific node(s) matching the label selector
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"]
    endpoint = ["http://my-registry:5000"]
nodes:
- role: control-plane
- role: worker
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: "ingress-ready=true"
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP

We will be using Ingress NGINX as an ingress Controller.

# Run this command
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

Using Ingress

# Create an ingress.yaml with below content
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: eshop-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: webapp
            port:
              number: 8080
# Run this command
kubectl apply -f ingress.yaml

Launch eShop Web App

Now, you can verify that eShop Web Application is accessible via browser

Review Aspirate (Aspir8) generated output

Let's review the Aspirate output for the WebApp to gain insight into the contents of the deployment, kustomization, and service files.

# deployment
# Read comments for more clarity
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  minReadySeconds: 60
  replicas: 1 # You increase this number to create more instance of Web App Pod.
  selector:
    matchLabels:
      app: webapp
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: localhost:5000/webapp:latest # Container image of Web App
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        - containerPort: 8443
        envFrom:
        - configMapRef: # Environment variables
            name: webapp-env
        - secretRef: # Secret e.g., Password, ConnectionString etc.
            name: webapp-secrets
      terminationGracePeriodSeconds: 180
# service
# Read comments for more clarity
---
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  type: ClusterIP
  selector:
    app: webapp
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  - name: https
    port: 8443
    targetPort: 8443
# kustomization
# Read comments for more clarity
resources:
- deployment.yml
- service.yml
 
generatorOptions:
  disableNameSuffixHash: true
 
configMapGenerator:
- name: webapp-env # Reference in deployment
  literals:
    - OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES=true
    - OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES=true
    - OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY=in_memory
    - ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
    - services__basket-api__http__0=http://basket-api:8080
    - services__catalog-api__http__0=http://catalog-api:8080
    - services__ordering-api__http__0=http://ordering-api:8080
    - IdentityUrl=http://identity-api:8080 # Manually add this line, otherwise Pod will crash.
    - CallBackUrl=http://webapp:8080 # # Manually add this line, otherwise Pod will crash.
    - ASPNETCORE_URLS=http://+:8080;
 
secretGenerator:
- name: webapp-secrets # Reference in deployment
  envs:
  - .webapp.secrets

Similar outputs are generated for other applications.

The Kubernetes components for the developer

Kubernetes is a popular platform for orchestrating containers. In simple word, it is deployment platform for containers. All code deployed into Kubernetes, needs to first packaged into containers.

NOTE: Instead of delving into the complexity of every component within a Kubernetes cluster, I'll simplify by highlighting just few fundamental concepts, which is important for developer:

Cluster

The Kubernetes cluster is collection of Virtual Machines or Physical Machines.

Nodes

The Nodes in a Kubernetes cluster are Virtual Machines or Physical Machines. It is where Kubernetes actually runs your containers. There are two types of Node

Master Node, which contains all the "control plane" services required to run a cluster.

Worker Nodes, which are used to run your applications. A single node can run many containers, depending on the resource constraints (memory, CPU) of the node.

In development environment, while running Kubernetes locally, it's common to have single node (your working laptop, desktop or VM), for master node as well worker node.

Pods

Before run you application in Kubernetes, you creates container image and stores in container registry.

A pod is smallest unit that you are going ask to Kubernetes to run. A Pod can contains container one or more containers. The additional containers (using sidecar pattern) can be used for cross-cutting concerns. A node can run multiple pods and each pod can contains one or more container.

Deployments

Deployments are used to define how Pods are deployed, which Docker images they should use, how many replicas should be running, and so on. They represent the desired state for the cluster, which Kubernetes works to maintain.

Services

A service is an in-cluster load-balancer for your pods. A service acts as an internal load-balancer for the pods. Pods talk to other pods via services, instead of contacting them directly.

ConfigMap

A ConfigMap is a Kubernetes object that decouples configuration data from pod definitions.

Secret

Secrets are similar to ConfigMap, but are designed to decouple sensitive information.

Ingresses

An Ingress exposes HTTP/HTTPs endpoints for a service that external clients (e.g., out of cluster) can call. HTTP/HTTPs requests to the endpoint are forwarded by the Ingress to the associated service on a node, which passes the request to the Pod.

Happy Learning & Coding...