In this ASP.NET Core Kubernetes tutorial I will host a Multi-Container ASP.NET Core app to Multiple Kubernetes Pods. I will also add a Service to expose the pods. The Pods will use this service to communicate with one another.
ASP.NET Core Kubernetes series consists of the following articles:
Page Contents
My ASP.NET Core app has 2 projects, one is Razor pages type and the other is of type Web API. The razor pages project will call the Web API project to get a random Joke which it then displays on the browser. I will be creating 2 Docker containers for containing these 2 projects. Then I will host these 2 containers on 2 Separate Kubernetes Pods. Finally, I will create a Service which will expose the Web API project running on a Pod. This will help the other project to call the web api through this service. I have tried to explain this concept by the below image.
In this image the Service 2 is exposing the Web API running on Container 2 (container 2 is running web api project). The app running on container 1 (container 1 is running razor pages project) will call this service 2 to access the web API. There is also another service, which is service 1, and it exposes the app on container 1 to the host i.e. to the web browser.
The app is very simple. It’s a multi-project ASP.NET Core app. I build this app from start in my previous tutorial. If you want to go into the creation part then visit – Kubernetes: Host Multi-Container ASP.NET Core app to Single Pod.
If you want to skip the development part of the app then just download the app (link given at the bottom of this tutorial).
Now open the app, and go inside the to the MultiApp project folder. There you will find the Pages folder, inside this folder find the Index.cshtml.cs file. Here on the OnGet() method the call to the web api is made. You have to change the URL of the Web API to:
http://service-2:8080/api/Joke
This is done because the Web API will be running from a Docker container hosted on a Kubernetes Pod and this Kubernetes pod is exposed by this service.
Here service-2 is the name of the service and 8080 is the exposed port of the service.
I have shown this change in the highlighted way below.
public async Task OnGet()
{
using (var client = new System.Net.Http.HttpClient())
{
var request = new System.Net.Http.HttpRequestMessage();
request.RequestUri = new Uri("http://service-2:8080/api/Joke");
var response = await client.SendAsync(request);
var joke = await response.Content.ReadAsStringAsync();
var details = JObject.Parse(joke);
ViewData["Joke"] = details["name"] + ";;" + details["text"] + ";;" + details["category"];
}
}
Also check the below image where I have explained the port mappings of the service and the container running inside the Pod.
I will build this “service-2” in just a moment so kindly be with me.
Open your command prompt window and enter the Minikube VM. The command is:
minikube docker-env
Then it will show you some list of texts, copy the last line, paste it and press enter:
@FOR /f "tokens=*" %i IN ('minikube -p minikube docker-env') DO @%i
You will enter Minikube Virual Machine.
In the ASP.NET Core app there is a docker-compose.yml given on the root folder. So from your command prompt, navigate to this folder and build it by running the docker build command:
docker-compose build
Two images of the 2 projects will be created which you can see by running the docker images command on the command prompt. The images names are:
Now I will create 2 Kubernetes Deployments that will create 2 Pods. Inside these Pods, one container each will run. As already discussed multiple times these containers will contain one project each of the ASP.NET Core app.
So, these are the 2 deployments configuration files (given below). One file is named as “mydep1.yaml” while the other is “mydep2.yaml”. On downloading the source code you will find all the deployments and services inside files inside the k8s Objects folder.
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep1
labels:
app: s1
spec:
replicas: 1
selector:
matchLabels:
app: mc-app1
template:
metadata:
labels:
app: mc-app1
spec:
containers:
- name: mc1
image: multiapp
imagePullPolicy: Never
ports:
- containerPort: 80
The deployment 1 creates a deployment by the name of dep1. It also specifies to create a pod which should contain a docker container by the name of mc1. The image for the docker container (as you might have guessed) is specified as multiapp.
Also, container port 80 is exposed.
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep2
labels:
app: s2
spec:
replicas: 1
selector:
matchLabels:
app: mc-app2
template:
metadata:
labels:
app: mc-app2
spec:
containers:
- name: mc2
image: multiapi
imagePullPolicy: Never
ports:
- containerPort: 80
In the deployment the following things are specified:
Next, in the command prompt window, go to the directory of these deployment files and apply them with kubectl apply. The command will be:
kubectl apply -f mydep1.yaml
kubectl apply -f mydep2.yaml
Now coming to the service which will expose the “myapi” project running on a Pod. The service config file called service2.yaml is given below.
apiVersion: v1
kind: Service
metadata:
name: service-2
spec:
type: NodePort
selector:
app: mc-app2
ports:
- port: 8080
targetPort: 80
The metadata.name field specifies the name of the service as “service-2”
metadata:
name: service-2
Note the port 8080 of the service is exposed and the service targets port 80 of the container. The ASP.NET Core app url has a default port of 80 so we don’t need to perform any extra settings here. Had this targetPort been different then we would have to tell asp.net core to expose another port for the url. That is, expose the port that has been assigned by the targetPort field. This can be done by the use of environment variables.
ports:
- port: 8080
targetPort: 80
Now notice the selector field of the service.
selector:
app: mc-app2
It says to apply this service to the deployment having a matchLabels as – app: mc-app2.
If you see the matchLabels field of the deployment 2, you will find the value as app: mc-app2.
matchLabels:
app: mc-app2
In this way the service targets a particular deployment.
Now apply this service by running the following command
kubectl apply -f service2.yaml
Now I will show how to call Web API from the service -2. For this I will go inside the container “mc1” which is containing the “multiapp” image.
So, run kubectl get pods command which will show you 2 pods are running.
These pods are dep1-86bb5964fd-bkgw2 and dep2-6668bf576f-9h985.
Next, I get inside the mc1 container which is contained inside the dep1-86bb5964fd-bkgw2 pod and start a shell. The command to run is given below:
kubectl exec -it dep1-86bb5964fd-bkgw2 -c mc1 -- /bin/bash
Next, I install curl in the container by running the following 2 command:
apt-get update
apt-get install curl
Finally run a curl command to the service-2:
curl http://service-2:8080/api/Joke
You will see a Joke json as a result. This shows the web api is called by the curl command. See below image:
I will now create another Kubernetes Service called service-1 and this will expose the Pod 1. Note that Pod 1 is containing “multiapp” image inside “mc1” docker container. See the below image which explains this:
The service-1 configuration file is given below. The file name is service1.yaml.
apiVersion: v1
kind: Service
metadata:
name: service-1
spec:
type: NodePort
selector:
app: mc-app1
ports:
- port: 8080
targetPort: 80
See the selector field of this service:
selector:
app: mc-app1
And it is same as the value of matchLabels field of the deployment 1.
matchLabels:
app: mc-app1
Now apply this service by running the following command:
kubectl apply -f service1.yaml
Finally, you can run the following command to open the service-1 on a proxy on the browser:
minikube service service-1
I have shown this in the below image.
I can use environment variables to pass the “service-2” url (which is used to call the web api) from the deployment configuration yaml file. This will make our code dynamic. Let us see how to do it:
So, add an environment variable called WebApiBaseAddress and provide it the value of the web api in terms of service-2 i.e. http://service-2:8080/api/Joke.
I have shown the updated code in highlighted way.
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep1
labels:
app: s1
spec:
replicas: 1
selector:
matchLabels:
app: mc-app1
template:
metadata:
labels:
app: mc-app1
spec:
containers:
- name: mc1
image: multiapp
imagePullPolicy: Never
ports:
- containerPort: 80
env:
- name: WebApiBaseAddress
value: http://service-2:8080/api/Joke
And now in the asp.net core app, change the code which is calling the web api as shown by the highlighted way:
public async Task OnGet()
{
using (var client = new System.Net.Http.HttpClient())
{
var request = new System.Net.Http.HttpRequestMessage();
string url = Environment.GetEnvironmentVariable("WebApiBaseAddress");
request.RequestUri = new Uri(url);
var response = await client.SendAsync(request);
var joke = await response.Content.ReadAsStringAsync();
var details = JObject.Parse(joke);
ViewData["Joke"] = details["name"] + ";;" + details["text"] + ";;" + details["category"];
}
}
Well, that’s it for this tutorial. Enjoy!
Download the source code:
In this ASP.NET Core Kubernetes Multi Pods tutorial I covered the networking between apps running on different pods. I also covered this scenario by creating an example with Multi-container ASP.NET Core app. If you like this tutorial kindly share on fb, twitter and other social community websites.