Introduction

In this article, we are going to discuss a few basics of Docker and Kubernetes, as well as the step-by-step implementation of product services using the .NET Core 7 Web API and the containerization of product services with the help of Docker and Kubernetes.

Agenda

Prerequisites

What is Docker?

Why use Docker?

Benefits of Docker

If you want to learn more about Docker and its basic components, then check out the following article:

https://www.c-sharpcorner.com/article/docker

What is Kubernetes?

Why Kubernetes?

Benefits of Kubernetes

If you want to learn more about Kubernetes and its basic components, then check out the following article:

https://www.c-sharpcorner.com/article/kubernetes

Step-by-step implementation of Product Service

Step 1

Create a new .NET Core Web API Project.

Step 2

Install the following NuGet packages for the newly created project:

.NET Core Web API Project

Step 3

Add a new Product Details entity with different properties related to the product.

namespace NET_Core_WebAPI_Kubernetes_Demo.Entities
{
    public class ProductDetails
    {
        public int Id { get; set; }
        public string ProductName { get; set; }
        public string ProductDescription { get; set; }
        public int ProductPrice { get; set; }
        public int ProductStock { get; set; }
    }
}

Step 4

Create a DbContextClass class inside the project for managing database-related things and configuration and a SeedData class to insert some data initially while running an application.

DbContextClass.cs

using Microsoft.EntityFrameworkCore;
using NET_Core_WebAPI_Kubernetes_Demo.Entities;

namespace NET_Core_WebAPI_Kubernetes_Demo.Data
{
    public class DbContextClass : DbContext
    {

        public DbContextClass(DbContextOptions<DbContextClass>
options) : base(options)
        {

        }

        public DbSet<ProductDetails> Products { get; set; }
    }
}

SeedData.cs

using Microsoft.EntityFrameworkCore;
using NET_Core_WebAPI_Kubernetes_Demo.Entities;

namespace NET_Core_WebAPI_Kubernetes_Demo.Data
{
    public class SeedData
    {
        public static void Initialize(IServiceProvider serviceProvider)
        {
            using (var context = new DbContextClass(
                serviceProvider.GetRequiredService<DbContextOptions<DbContextClass>>()))
            {
                if (context.Products.Any())
                {
                    return;
                }

                context.Products.AddRange(
                    new ProductDetails
                    {
                        Id = 1,
                        ProductName = "IPhone",
                        ProductDescription = "IPhone 14",
                        ProductPrice = 120000,
                        ProductStock = 100
                    },
                    new ProductDetails
                    {
                        Id = 2,
                        ProductName = "Samsung TV",
                        ProductDescription = "Smart TV",
                        ProductPrice = 400000,
                        ProductStock = 120
                    });
                context.SaveChanges();
            }
        }
    }
}

Step 5

Add product service repository to perform different database-related operations using DbContext after simply injecting from the Product Controller.

IProductService

using NET_Core_WebAPI_Kubernetes_Demo.Entities;

namespace NET_Core_WebAPI_Kubernetes_Demo.Repositories
{
    public interface IProductService
    {
        public Task<List<ProductDetails>> ProductListAsync();

        public Task<ProductDetails> GetProductDetailByIdAsync(int productId);

        public Task<bool> AddProductAsync(ProductDetails productDetails);

        public Task<bool> UpdateProductAsync(ProductDetails productDetails);

        public Task<bool> DeleteProductAsync(int productId);
    }
}

ProductService

using Microsoft.EntityFrameworkCore;
using NET_Core_WebAPI_Kubernetes_Demo.Data;
using NET_Core_WebAPI_Kubernetes_Demo.Entities;

namespace NET_Core_WebAPI_Kubernetes_Demo.Repositories
{
    public class ProductService : IProductService
    {
        private readonly DbContextClass dbContextClass;

        public ProductService(DbContextClass dbContextClass)
        {
            this.dbContextClass = dbContextClass;
        }

        public async Task<List<ProductDetails>> ProductListAsync()
        {
            return await dbContextClass.Products.ToListAsync();
        }

        public async Task<ProductDetails> GetProductDetailByIdAsync(int productId)
        {
            return await dbContextClass.Products.Where(ele => ele.Id == productId).FirstOrDefaultAsync();
        }

        public async Task<bool> AddProductAsync(ProductDetails productDetails)
        {
            await dbContextClass.Products.AddAsync(productDetails);
            var result = await dbContextClass.SaveChangesAsync();
            if (result > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public async Task<bool> UpdateProductAsync(ProductDetails productDetails)
        {
            var isProduct = ProductDetailsExists(productDetails.Id);
            if (isProduct)
            {
                dbContextClass.Products.Update(productDetails);
                var result = await dbContextClass.SaveChangesAsync();
                if (result > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            return false;
        }

        public async Task<bool> DeleteProductAsync(int productId)
        {
            var findProductData = dbContextClass.Products.Where(_ => _.Id == productId).FirstOrDefault();
            if (findProductData != null)
            {
                dbContextClass.Products.Remove(findProductData);
                var result = await dbContextClass.SaveChangesAsync();
                if (result > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            return false;
        }

        private bool ProductDetailsExists(int productId)
        {
            return dbContextClass.Products.Any(e => e.Id == productId);
        }
    }
}

Step 6

Create a new Product Controller with different endpoints.

using Microsoft.AspNetCore.Mvc;
using NET_Core_WebAPI_Kubernetes_Demo.Entities;
using NET_Core_WebAPI_Kubernetes_Demo.Repositories;

namespace NET_Core_WebAPI_Kubernetes_Demo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly IProductService _productService;

        public ProductsController(IProductService productService)
        {
            _productService = productService;
        }

        /// <summary>
        /// Product List
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<IActionResult> ProductListAsync()
        {
            var productList = await _productService.ProductListAsync();
            if (productList != null)
            {
                return Ok(productList);
            }
            else
            {
                return NoContent();
            }
        }

        /// <summary>
        /// Get Product By Id
        /// </summary>
        /// <param name="productId"></param>
        /// <returns></returns>
        [HttpGet("{productId}")]
        public async Task<IActionResult> GetProductDetailsByIdAsync(int productId)
        {
            var productDetails = await _productService.GetProductDetailByIdAsync(productId);
            if (productDetails != null)
            {
                return Ok(productDetails);
            }
            else
            {
                return NotFound();
            }
        }

        /// <summary>
        /// Add a new product
        /// </summary>
        /// <param name="productDetails"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> AddProductAsync(ProductDetails productDetails)
        {
            var isProductInserted = await _productService.AddProductAsync(productDetails);
            if (isProductInserted)
            {
                return Ok(isProductInserted);
            }
            else
            {
                return BadRequest();
            }
        }

        /// <summary>
        /// Update product details
        /// </summary>
        /// <param name="productDetails"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task<IActionResult> UpdateProductAsync(ProductDetails productDetails)
        {
            var isProductUpdated = await _productService.UpdateProductAsync(productDetails);
            if (isProductUpdated)
            {
                return Ok(isProductUpdated);
            }
            else
            {
                return BadRequest();
            }
        }

        /// <summary>
        /// Delete product by id
        /// </summary>
        /// <param name="productId"></param>
        /// <returns></returns>
        [HttpDelete]
        public async Task<IActionResult> DeleteProductAsync(int productId)
        {
            var isProductDeleted = await _productService.DeleteProductAsync(productId);
            if (isProductDeleted)
            {
                return Ok(isProductDeleted);
            }
            else
            {
                return BadRequest();
            }
        }
    }
}

Step 7

Configure a few services inside the program class related to DbContext, Product Service In-Memory database, etc.

using Microsoft.EntityFrameworkCore;
using NET_Core_WebAPI_Kubernetes_Demo.Data;
using NET_Core_WebAPI_Kubernetes_Demo.Repositories;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddDbContext<DbContextClass>
(o => o.UseInMemoryDatabase("NET_Core_WebAPI_Kubernetes_Demo"));

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<DbContextClass>();

    SeedData.Initialize(services);
}

// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Step 8

Run and verify the product service.

.NET Core Web API Kubernetes Deomo

Containerization of Applications using Docker and Kubernetes

Note: Please make sure Docker and Kubernetes are running on your system.

Step 1

Create a docker image for our newly created application.

# Use the official .NET Core SDK as a parent image
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /app

# Copy the project file and restore any dependencies (use .csproj for the project name)
COPY *.csproj ./
RUN dotnet restore

# Copy the rest of the application code
COPY . .

# Publish the application
RUN dotnet publish -c Release -o out

# Build the runtime image
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
WORKDIR /app
COPY --from=build /app/out ./

# Expose the port your application will run on
EXPOSE 80

# Start the application
ENTRYPOINT ["dotnet", "NET_Core_WebAPI_Kubernetes_Demo.dll"]

Explanation

# Use the official .NET Core SDK as a parent image
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /app

Step 2

Build the docker image.

docker build -t web-api .

The docker build command is used to build a Docker image from a Docker file. It includes a variety of options, including the -t option to specify a tag for an image.

Build the docker image

This command creates a Docker image that uses the Dockerfile in the current directory (.) and marks it as web-api.

Step 3

Run the docker image inside a docker container.

docker run -d -p 5001:80 — name web-api-container web-api

-d: Detached mode (run in the background).

-p 5001:80: Map port 5001 on your local machine to port 80 inside the container.

— name web-api-container: Assign a name to the container.

web-api: Use the image you built earlier.

Run the docker image inside a docker container

Step 4

Open the browser and hit the API URL to execute different endpoints.

API URL

Step 5

Create a deployment and service YAML file for Kubernetes to create deployments, pods, and services for our product service.

Deployment.YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-app-deployment  # Name of the deployment
spec:
  replicas: 3  # Number of desired replicas (pods)
  selector:
    matchLabels:
      app: product-app  # Label selector to match pods controlled by this deployment
  template:
    metadata:
      labels:
        app: product-app  # Labels applied to pods created by this deployment
    spec:
      containers:
        - name: product-app  # Name of the container
          image: web-api:latest  # Docker image to use
          imagePullPolicy: Never #Docker image pull policy
          ports:
            - containerPort: 80  # Port to expose within the pod

Service.YAML

apiVersion: v1
kind: Service
metadata:
  name: product-app-service  # Name of the service
spec:
  selector:
    app: product-app  # Label selector to target pods with this label
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: NodePort  # Type of service (other options include ClusterIP, LoadBalancer, etc.)

Step 6

Apply deployment and service YAML files with kubectl commands.

kubectl apply -f deployment.yml
kubectl apply -f service.yml

Step 7

Check and verify deployment, instances, services, pods, logs, etc.

verify deployment, instances, services, pods, logs

Step 8

Open the browser and hit the localhost URL with the Kubernetes service port as shown in the above image.

localhost URL with the Kubernetes service port

In a real-time scenario, you can create multiple instances of service, versioning with deployment, and apply different load balancing techniques as per requirements.

GitHub

https://github.com/Jaydeep-007/NET_Core_WebAPI_Kubernetes_Demo

Conclusion

In this article, we discussed about a few basics of Docker and Kubernetes. Also, the step-by-step implementation and creation of product service using .NET Core 7 Web API and containerization for the same with Docker and Kubernetes.