Kubernetes ReplicaSet example on Mac using VS Code

A ReplicaSet helps load balance and scale our Application up or down when the demand for it changes. It makes sure the desired number of pods are always running for high availability.

I’m using VS Code on Mac to create the below yaml file.

Create the following yaml file:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myapp-replicaset
  labels:
    app: myapp
spec:
  selector:
    matchLabels:
      app: myapp
  replicas: 3
  template:
    metadata:
      name: nginx-2
      labels:
        app: myapp
    spec:
      containers:
        - name: nginx
          image: nginx

The above yaml file has 4 main properties which are apiVersion, kind, metadata and spec. The spec contains the definition for the number of replicas and the containers to be created inside the pods.

Run the following command:

$ kubectl create -f ReplicaSetsDemo.yaml 

replicaset.apps/myapp-replicaset created

Now let’s check the status of the replicaSet and then the pods:

$ kubectl get replicaset
NAME               DESIRED   CURRENT   READY   AGE
myapp-replicaset   3         3         3       2m29s


$ kubectl get pods

NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-s7jm7   1/1     Running   0          33s
myapp-replicaset-svqvm   1/1     Running   0          33s
myapp-replicaset-xnbbq   1/1     Running   0          33s

The above command shows 3 pods created for nginx with the name of the replicaset prefixed. A replicaset ensures that sufficient number of replicas or pods are available at all times.

Now, let’s delete a pod:

$ kubectl delete pod myapp-replicaset-s7jm7

pod "myapp-replicaset-s7jm7" deleted

Check the status of the pods again:

$ kubectl get pods
                         
NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-d7f88   1/1     Running   0          36s
myapp-replicaset-svqvm   1/1     Running   0          5m53s
myapp-replicaset-xnbbq   1/1     Running   0          5m53s

Notice that there are still 3 pods running as one more pod was created to maintain the desired state. Also, if you try to create a pod with the same label app=myapp outside of the replicaset, it’ll still come under the purview of the replicaset we created and will terminate that pod to maintain the desired state of 3 replicas. This is where it differs from a Replication Controller.

Now, let’s edit the replicaset to scale it up as below:

$ kubectl edit replicaset myapp-replicaset

The above command let’s you edit the in-memory configuration file that Kubernetes creates. Search for the replicas section and edit it to 4 using the vi editor commands and save it with wq!

Upon saving you’ll see the output as:

replicaset.apps/myapp-replicaset edited

Check the pods status and you’ll see and additional pod created:

$ kubectl get pods
                            
NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-d7f88   1/1     Running   0          14m
myapp-replicaset-pnlvh   1/1     Running   0          55s
myapp-replicaset-svqvm   1/1     Running   0          19m
myapp-replicaset-xnbbq   1/1     Running   0          19m

Another way to scale the replicaset is to use the below command and check the status again:

$ kubectl scale replicaset myapp-replicaset --replicas=2

replicaset.apps/myapp-replicaset scaled


$ kubectl get pods

NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-svqvm   1/1     Running   0          22m
myapp-replicaset-xnbbq   1/1     Running   0          22m

Notice that the pods are scaled down to 2 only as other 2 got terminated.

Enable CORS in .Net Core WebAPI

It is a common scenario where a React front-end SPA is calling a .Net Core WebAPI to fetch data. In this case, suppose both Applications are using their respective domains shown below:

React App => http://myfrontend.com
API=> http://myapi.com

CORS (Cross-Origin Resource Sharing) is a standard that works by adding HTTP headers that allow servers to describe the set of origins that are permitted to fetch information using a web browser and the kind of requests that are allowed.
For the API to allow the React App to fetch the data, it has to allow the Origin of the React App. This is a common CORS problem. So if you’re using, say axios or Fetch, to fetch data from the .Net Core WebAPI, it will only succeed if the API allows the domain http://myfrontend.com to call itself.

Below I’ve used the technique of CORS with named policy and middleware:

public class Startup
{
	public Startup(IConfiguration configuration)
	{
		Configuration = configuration;
	}
	readonly string MyAllowedOrigins = "_myAllowedOrigins";
	public IConfiguration Configuration { get; }
	public IContainer ApplicationContainer { get; private set; }
	// This method gets called by the runtime. Use this method to add services to the container.
	public void ConfigureServices(IServiceCollection services)
	{
		services.AddControllers();
		services.AddCors(options =>
		{
			options.AddPolicy(MyAllowedOrigins,
			builder =>
			{
				//Allowing both the localhost and hosted domains.
				builder.WithOrigins("http://localhost:3000",
									"http://www.myfrontend.com");
			});
		});
	}
	
	//Apply CORS policies to all endpoints via CORS Middleware:
	// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
	public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
	{
		//Omitted code
		app.UseCors(MyAllowedOrigins);
		app.UseEndpoints(endpoints =>
		{
			endpoints.MapControllers();
		});
		//Omitted code
	}
}

The example above is how you apply CORS policy globally to all endpoints in your API. You can also use the [EnableCors(“Policy String”)] attribute on your controllers/page-model/action method.

e.g. Applying CORS on Action method:

[EnableCors("_myAllowedOrigins")]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
	return new string[] { "test1", "test2" };
}

It is recommended to enable CORS either locally or globally and not combine the two approaches.

We can also expose the allowed Headers and http methods the following way:

public void ConfigureServices(IServiceCollection services)
{
	services.AddControllers();
	services.AddCors(options =>
	{
		options.AddPolicy(MyAllowSpecificOrigins,
		builder =>
		{
			builder.WithOrigins("http://myfrontend.com", "http://localhost:3000")
			.WithHeaders("token")
			.WithMethods("OPTIONS", "GET", "POST");
		});
	});
}

You can also allow credentials to be passed to the WebAPI from the Client by chaining the .AllowCredentials method.

Pre-flight requests for OPTIONS method call fails with Windows authentication and gives 401 Unauthorized. This can be worked around by enabling both Windows and Anonymous authentication.

If you are using Windows Authentication in the case of an Intranet Application and also enabled Anonymous Authentication for allowing OPTIONS pre-flight request, then make sure to use [Authorize] and [AllowAnonymous] attributes to the respective endpoints where required. Without using [Authorize], the name of the user is returned
null with context.HttpContext.User.Identity.Name where context is the ActionExecutingContext object. These attributes can be used on Controllers or Actions as required.
Also, make sure to setup the middleware in the correct order for using with UseCors, UseAuthentication and UseAuthorization in the Startup.cs file Configuration.