NGINX Ingress with the .NET Core Microservices and Kubernetes

Introduction

In this article, we will discuss the basics and use cases of ingress. Also, step-by-step implementation and configuration of different services with the help of the .NET Core 8 Web API, Docker, Kubernetes, and Ingress.

Agenda

  • What is NGINX Ingress?
  • Use cases for NGINX Ingress
  • Implementation of Services using the .NET Core 8 Web API
  • Containerization of Services and NGINX Ingress Configuration.

Prerequisites

  • Visual Studio 2022
  • .NET Core 8 SDK
  • Docker and Kubernetes

What is NGINX Ingress?

In simple terms, Nginx Ingress is a way to manage incoming internet traffic to your web applications or services. Think of it as a traffic cop for your web servers. It sits in front of your applications and directs traffic, like a router for web requests.

NGINX Ingress
Fig Ref- docs.nginx.com

Nginx Ingress examines incoming requests and decides where to send them based on the rules you define. This could mean directing requests to different services based on the requested URL or other criteria. It can also handle things like load balancing, SSL termination (decrypting HTTPS traffic), and other tasks related to managing incoming web traffic.

Overall, Nginx Ingress helps you efficiently manage and route web traffic to your applications, improving performance, security, and scalability.

Use cases for NGINX Ingress

Following are a few use cases for Ingress

  • Load Balancing: Spread incoming web traffic evenly across multiple servers to prevent overloading and ensure better performance.
  • Routing: Direct web requests to different parts of your application based on factors like URLs or domain names.
  • SSL Termination: Manage HTTPS encryption and decryption centrally to secure web traffic without burdening individual servers.
  • Virtual Hosting: Host multiple websites or applications on the same servers, each with its own domain name.
  • Authentication and Authorization: Control access to your applications based on user credentials or permissions before requests reach the servers.
  • Rate Limiting: Limit the number of requests from individual clients or IP addresses to prevent abuse or overuse of services.
  • Web Application Firewall (WAF): Protect applications from common web security threats by inspecting and filtering incoming traffic based on predefined security rules.

Implementation of Services using .NET Core 8 Web API

Step 1. Create a new Product class.

Product Service

namespace ProductAPI.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

Step 2. Add a new product controller with different endpoints.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ProductAPI.Models;

namespace ProductAPI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ProductsController : ControllerBase
    {
        private static List<Product> _products = new List<Product>
        {
            new Product { Id = 1, Name = "Product 1", Price = 10.99m },
            new Product { Id = 2, Name = "Product 2", Price = 20.49m }
        };

        private readonly ILogger<ProductsController> _logger;

        public ProductsController(ILogger<ProductsController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public IEnumerable<Product> Get()
        {
            return _products;
        }

        [HttpGet("{id}")]
        public ActionResult<Product> Get(int id)
        {
            var product = _products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return product;
        }

        [HttpPost]
        public ActionResult<Product> Post(Product product)
        {
            product.Id = _products.Count + 1;
            _products.Add(product);
            return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
        }

        [HttpPut("{id}")]
        public IActionResult Put(int id, Product product)
        {
            var existingProduct = _products.FirstOrDefault(p => p.Id == id);
            if (existingProduct == null)
            {
                return NotFound();
            }
            existingProduct.Name = product.Name;
            existingProduct.Price = product.Price;
            return NoContent();
        }

        [HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {
            var product = _products.FirstOrDefault(p => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            _products.Remove(product);
            return NoContent();
        }
    }
}

Step 3. Register the services and middleware in the program class.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

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();

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

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Product API

User Service

Step 1. Create a new User class.

namespace UserAPI.Models
{
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
    }
}

Step 2. Add a new user controller with different endpoints.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using UserAPI.Models;

namespace UserAPI.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class UsersController : ControllerBase
    {
        private static List<User> _users = new List<User>
        {
            new User { Id = 1, Name = "User 1", Email = "[email protected]" },
            new User { Id = 2, Name = "User 2", Email = "[email protected]" }
        };

        private readonly ILogger<UsersController> _logger;

        public UsersController(ILogger<UsersController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public IEnumerable<User> Get()
        {
            return _users;
        }

        [HttpGet("{id}")]
        public ActionResult<User> Get(int id)
        {
            var user = _users.FirstOrDefault(u => u.Id == id);
            if (user == null)
            {
                return NotFound();
            }
            return user;
        }

        [HttpPost]
        public ActionResult<User> Post(User user)
        {
            user.Id = _users.Count + 1;
            _users.Add(user);
            return CreatedAtAction(nameof(Get), new { id = user.Id }, user);
        }

        [HttpPut("{id}")]
        public IActionResult Put(int id, User user)
        {
            var existingUser = _users.FirstOrDefault(u => u.Id == id);
            if (existingUser == null)
            {
                return NotFound();
            }
            existingUser.Name = user.Name;
            existingUser.Email = user.Email;
            return NoContent();
        }

        [HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {
            var user = _users.FirstOrDefault(u => u.Id == id);
            if (user == null)
            {
                return NotFound();
            }
            _users.Remove(user);
            return NoContent();
        }
    }
}

Step 3. Register the services and middleware in the program class.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

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();

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

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

User API

Containerization of Services and Ingress Configuration

Step 1. Add a docker image for product and user service.

Product Service

# Use the official .NET Core SDK as a parent image
FROM mcr.microsoft.com/dotnet/sdk:8.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:8.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", "ProductAPI.dll"]

User Service

# Use the official .NET Core SDK as a parent image
FROM mcr.microsoft.com/dotnet/sdk:8.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:8.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", "UserAPI.dll"]

Step 2. Build a Docker image.

docker build -t product-API

Docker image

docker build -t user-API

Docker build

Step 3. Create a deployment, service, and ingress YAML file for Kubernetes.

Deployments

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-app-deployment  # Name of the deployment
spec:
  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: product-api:latest  # Docker image to use
          imagePullPolicy: Never
          ports:
          - containerPort: 80  # Port to expose within the pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-app-deployment  # Name of the deployment
spec:
  selector:
    matchLabels:
      app: user-app  # Label selector to match pods controlled by this deployment
  template:
    metadata:
      labels:
        app: user-app  # Labels applied to pods created by this deployment
    spec:
      containers:
        - name: user-app  # Name of the container
          image: user-api:latest  # Docker image to use
          imagePullPolicy: Never
          ports:
          - containerPort: 80  # Port to expose within the pod

Services

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: 8081
      targetPort: 8080
  type: NodePort  # Type of service (other options include ClusterIP, LoadBalancer, etc.)
apiVersion: v1
kind: Service
metadata:
  name: user-app-service  # Name of the service
spec:
  selector:
    app: user-app  # Label selector to target pods with this label
  ports:
    - protocol: TCP
      port: 8082
      targetPort: 8080
  type: NodePort  # Type of service (other options include ClusterIP, LoadBalancer, etc.)

NGINX Ingress

First, you need to install ingress on your Kubernetes cluster.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

Kubernetes cluster

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: ingress.demo.com
    http:
      paths:
      - path: /products
        pathType: Prefix
        backend:
          service:
            name: product-app-service
            port:
              number: 8081
      - path: /users
        pathType: Prefix
        backend:
          service:
            name: user-app-service
            port:
              number: 8082

Next, create an ingress file with different rules

This YAML file sets up rules for directing web traffic coming to a specific domain (ingress.demo.com) to different services within a Kubernetes cluster.

  • When someone goes to ingress.demo.com/products, their request goes to a service called product-app-service running on port 8081.
  • When someone goes to ingress.demo.com/users, their request goes to a service called user-app-service running on port 8082.

This configuration is managed by an Ingress resource using the NGINX Ingress controller. It’s a way to manage external access to services in Kubernetes.

Step 4. Configure the Ingress host with IP inside the host file, which is situated under the /etc/host

Note. The host file path is different for every provider. Here, I am using Kubernetes with Docker Desktop. So, the file path is C:\Windows\System32\drivers\etc

 IP inside

Step 5. Apply YAML files to the Kubernetes cluster.

Kubernetes files

Cluster file

CMD

Result

Step 6. You can hit the different endpoints with the help of an ingress host, and based on the request URL, ingress will pass your request to a particular service.

Particular service

Github

GitHub

Conclusion

In this article, we learned the basics of ingress and its use cases. Also, step-by-step implementation of services and containerization with the help of Docker and Kubernetes. Also, ingress installation and configuration with the Kubernetes cluster.


Similar Articles