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.
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();
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();
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 build -t user-API
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
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
Step 5. Apply YAML files to the Kubernetes cluster.
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.
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.