Tuesday, August 31, 2021

Deploy React App With .NET Backend to Azure (Azure DevOps)

Summay: 

Basic configuration of react and dotnet api application.

Excellent article to deploy react app with .net api as back end application. 

(React js, .net core web api, azure web app)

Steps: 

1. Build basic react app . with npm install and npm run build.move the build result to the ASP.NET Core wwwroot directory

2. Build .net api with dotnet restore, dotnet build and dotnet publish  tasks in build pipe line. 

3. Create required web app in azure portal. 

4. Crete release pipe line and publish all build pipe line artifacts to azure web app. 

Basic react and web api creted to demo above build and deploy steps. 

React app: 

    npx create-react-app react-demo --template typescript

Web api backend app

dotnet new sln -n react-demo-backend

dotnet new webapi -n react-demo-backend -f netcoreapp3.1

dotnet sln react-demo-backend.sln add react-demo-backend/react-demo-backend.csproj



There are various ways of deploying React applications to Microsoft Azure. It can be hosted in multiple ways. If you are designing an application which is using a server to process the data, and that backend is using ASP.NET Core, you can easily deploy a React application together with a server under one resource.

In this article, I am going to show you how to:

  • Create an ASP.NET Core application that is going to host a React Application
  • Build the application using Azure DevOps
  • Deploy the application to Microsoft Azure using Azure DevOps
  • Set up build & deploy pipelines for providing CI/CD

Want React and Azure specialists to implement solutions like this for you? - talk with us to see how we can help you.


Creating and Adjusting Applications to Deploy React to Azure

1. Prepare Directory Structure

Let’s get started by creating proper directory structure.

We need to create the main directory which is going to hold our applications. Then, inside it, we are going to create our applications.

2. Create React App (Front-end)

Time to create applications so we can deploy React to Azure!

We are going to start by creating a new React application using the CLI tool called ‘create-react-app’. Simply put, create-react-app is going to do all of the magic required for setting up the application.

Let’s create an application called ‘react-demo’ using typescript template with a command:

npx create-react-app react-demo --template typescript

Great. Now that our application was created, we can modify the App.tsx file and add some information. (I’ve just replaced the paragraph text with ‘Hello! This is React-Demo app which was built and deployed to Azure with Azure DevOps).

We can check if the application was created successfully by running it:

npm start

After running it we should see something like this:

That is it for the React application part.

For the purpose of this demo, we want to keep it super simple.

3. Create ASP.NET Core application (backend)

It’s time to create a backend. We are going to do it with dotnet cli tools, but you can do the same with the .NET IDE which you are using. In our main directory, let’s run a command to create a solution first.

dotnet new sln -n react-demo-backend

After creating the solution, it’s time to create our ASP.NET Core project.

dotnet new webapi -n react-demo-backend -f netcoreapp3.1

Please note that we are targeting .NET Core 3.1. That is because, at the time of writing the article, it is the LTS version that is recommended to use on production scenarios.

After creating the project, it’s time to link it to the solution:

dotnet sln react-demo-backend.sln add react-demo-backend/react-demo-backend.csproj

4. Adjusting ASP.NET Core application

Now this is when magic is going to happen. We are going to adjust our ASP.NET Core application. 

First, under the Controller directory, we need to create a Fallback controller. Whenever the route is not going to be matched, all of the load is going to be forwarded to this controller.

Controllers/FallbackController.cs

using System.IO;
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc;

namespace react_demo_backend.Controllers
{
   public class FallbackController : Controller
   {
       public IActionResult Index()
       {
           return PhysicalFile(Path.Combine(Directory.GetCurrentDirectory(),
               "wwwroot", "index.html"), MediaTypeNames.Text.Html);
       }  
   }
}

The wwwroot/index.html is going to be our entrypoint for the React application. So whenever a route has not been matched, it is going to forward the request to wwwroot/index.html. That’s where React will take care of itself, and should display built applications.

As we created our FallbackController, it’s time to tell the framework that it should use it. This is going to be done in Startup.cs.

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace react_demo_backend
{
   public class Startup
   {
       public Startup(IConfiguration configuration)
       {
           Configuration = configuration;
       }

       public IConfiguration Configuration { get; }

       public void ConfigureServices(IServiceCollection services)
       {
           services.AddCors(options =>
           {
               options.AddDefaultPolicy(policy =>
               {
                   policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().Build();
               });
           });
           services.AddControllers();
       }

       public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
       {
           if (env.IsDevelopment())
           {
               app.UseDeveloperExceptionPage();
           }
          
           app.UseCors();
           app.UseRouting();
           app.UseDefaultFiles();
           app.UseStaticFiles();

           app.UseEndpoints(endpoints =>
           {
               endpoints.MapControllers();
               endpoints.MapFallbackToController("Index", "Fallback");
           });
       }
   }
}

This Startup code makes the framework:

  • Define and use the default CORS policy to allow everyone and anything
  • Add support for static files
  • Indicate a fallback controller action according to what we have prepared previously

Important: Make sure to push the main directory to the Azure DevOps repository so we can use it in the next stages


Creating Azure Resource

We are going to use Azure Portal to create the resources. This is how we will deploy React to Azure.

Go to https://portal.azure.com, search for App Services and click the "Create" button and follow the steps:

  1. Create Resource Group by using the "Create new" action
  2. Provide a unique name for the resource (for example: apps-reactdemo-prod-neu)
  3. Pick "Code" as a publish type
  4. Pick .NET Core 3.1 as runtime stack
  5. Pick Linux as Operating System
  6. Pick the region which is closest to you (in my case North Europe
  7. For the App Service plan use the "Create new" action.
  8. Click on the Deployment (Preview) button to go to the next step

Under the Deployment (Preview) section, make sure to have it disabled, and go to Monitoring.

Skip the Monitoring and Tags tabs, and go directly to the Review + create tab.

You can see the summary, and if everything is set correctly, just click "Create".

Et voila! Our resource has been created.


Creating Build Pipeline For The Application

Go to the Azure DevOps, and under Pipelines hit the "New pipeline" button.

To make it more friendly for reading purposes, we are going to use the classic editor instead of YAML.

First, we have to pick a repository. Just pick the one to which you have pushed your code.

Now we are going to prepare our build pipeline. Don’t choose any template: just pick "Empty job".

For the default pipelines pick:

  • A name which is going to be unique and related to the application (I have called it ‘react-demo CI’)
  • Agent pool - Azure Pipelines
  • Agent specification - ubuntu-20.04

It’s time to add tasks to our job.

At first, we want to install all node packages for the react application and build it. We have to create two separate tasks for that.

Add a task called ‘npm’.

And configure it accordingly: 

“Working folder” is where we have our React Application.

As we have a task for installing node packages, it’s time to add a task for building the application and configuring it accordingly:

Now that the React application was built properly, it’s time to move the build result to the ASP.NET Core wwwroot directory, so the fallback controller can use it.

We can accomplish it by adding a Command line task: 

And configuring it accordingly:

Great! We are finished with the React Application part. Now it’s time to build the ASP.NET Core backend.

ASP.NET Core Backend

We can accomplish it using dotnet tasks.

 We need to:

  • Restore
  • Build
  • Publish

So we need three tasks.

(1/3) Restore Task

(2/3) Build Task

(3/3) Publish Task

To finish the pipeline, we need to publish its result to the artifact.

We can accomplish this adding the ‘Publish build artifacts’ task.

And configuring it properly:

That’s all for the build pipeline configuration.

Pipeline Tasks Review

Below you can see the pipeline tasks. Make sure that they are in the same order.

Continuous Integration

Additionally, we can add the Continuous integration feature.

Go to the ‘Triggers’ tab.

Check the "Enable continuous integration" checkbox.

Under branch filters, you can configure to which branch the pipeline should be enabled after push. By default, it is the main branch.

That’s all! We can save the pipeline and queue it automatically - so we can automatically get a build artifact for future use (click on a ‘Save & queue’ button).


Creating Release Pipeline For The Application

Under release pipelines click on the "New" button and pick "New release pipeline".

Pick ‘Azure App service deployment’, name the stage ‘Production’, and close the sidebar.

Now, under the Artifacts section click on the "Add" button.

On the sidebar which has opened on the right side, under Source, pick react-demo CI.

The Azure DevOps will automatically link the artifact with its latest version. Click on the "Add" button.

Continuous Deployment Trigger

We are going to add a Continuous Deployment trigger. Click on the ‘lighting’ icon to do that - a sidebar on the right should open.

In order to enable CD, just click on the switch control.

That’s all for CD, just close the sidebar.

Production Stage

Now on the top, click on the ‘Tasks’ tab to set up the ‘Production’ stage.

Under stage settings:

  1. Pick your subscription
  2. Pick App type ‘Web App on Linux’
  3. Under app service name, select the application which was created previously in the Azure portal (in this article it’s: apps-reactdemo-prod-neu).

Under the ‘Run on agent’ task, select ubuntu-20.04 from ‘Agent specification’.

Under Deploy Azure App Service task in Package or folder field, click on the three dots to pick a package location to be deployed.

Pick the .zip file located under our linked artifact.

Saving and Deploying React to Azure

The pipeline is ready, it’s time to save it and run it - click on the "Save" button.

Under releases, go to the newly created release pipeline and click on the "Create release" button. Then on the right sidebar, click "Create".

Then automatically, the service is going to be deployed to Azure.

After release, we can go to apps-reactdemo-prod-neu.azurewebsites.net and see our application running on localhost!


Deploy React to Azure Summary

We have deployed the ASP.NET Core application using Azure DevOps pipelines to Azure. The React application is part of the ASP.NET Core app and is served using a combination of Fallback controller and static files. 

The “part” word has not been used by accident. You can do many more with this approach: for example, the host application (backend) can be a WebApi - then the front-end application can use it to get processed data. This helps reduce the cost of Azure resources. 

Not only is it great to deploy and maintain the application, but it’s super easy to develop such an application. 

Monday, August 9, 2021

Multi-Stage AKS Deployment and Traffic Routing

Summary: Excellent articles on AKS and traffic routing. 

Ref: Multi-Stage AKS Deployment and Traffic Routing | by Amine Kaabachi | Azure Expert | Medium

Step 1: Create an AKS Cluster and push docker to AKS


az group create --name multi-stage-aks --location northeurope
az aks create --resource-group multi-stage-aks --name kube-cluster --node-count 2 --generate-ssh-keys -s Standard_DS4_v2 --disable-rbac

Create docker and execute

docker run -e ENV=dev -p 8080:8080 g0d3l/dummy-app
docker run -e ENV=prod -p 8080:8080 g0d3l/dummy-app

In this configuration it was explained how to manage dev and prod environment with single AKS cluster.

Step2 : AKS with Nginx Ingress Controller


Add help repo: 

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

Install Ingress

helm install single-ingress ingress-nginx/ingress-nginx --set rbac.create=false -n kube-system
NAME: single-ingress
LAST DEPLOYED: Sun Oct 4 04:22:36 2020
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None

Ingress is now functional and is attached to an Azure Load Balancer with an external IP 

Above IP can be attached to DNS . Two ingress installations can be done for one for dev and another for prod. 

kubectl apply -f ingress.yml -n dev
ingress.extensions/cloudapp-ingress created

 Introduction: 

This tutorial will help you deploy your app to an AKS Cluster with multiple environments (Dev, QA, etc) and allow to access each app instance. using a separate FQDN (dev.mycloudapp.org, qa.mycloudapp.org, etc).

In fact, your requirements are the following:

  • Having the two apps in separate environments so that changes in the first do not affect the second.
  • Being able to access each instance of the application separately. i.e. dev.mycloudapp.org redirects to the dev instance and similarly prod.mycloudapp.org redirects to the prod one.
  • Being able to do it with only one AKS cluster.

If you can afford multiple clusters. It may be a better solution as it ensures that the apps will be separated physically. In the other case where you want to optimise your spendings, this article will will help you do it.

To solve this, we will use Kubernetes namespaces to separate and isolate stages.

Note: This article suppose. that you are familiar with the concepts of Kubernetes Service, Ingress and Ingress Controllers. If you want more basic topics, please let me know in the comments.

(Optional) Refresher on Services from KodeKloud.

Lab Setup

This section describes a Lab setup for those who want to go through the use-case without having a real scenario at hand. Feel free to skip it if your AKS is already setup and your apps are deployed in separate namespaces.

Create an AKS Cluster

Let’s start by creating a new resource group for our AKS Cluster deployment:

▶ az group create --name multi-stage-aks --location northeurope

Now, we can create a new AKS Cluster, we will also disable RBAC because we don’t need it in these examples:

▶ az aks create --resource-group multi-stage-aks --name kube-cluster --node-count 2 --generate-ssh-keys -s Standard_DS4_v2 --disable-rbac

This will create another internal resource group with the required Azure components that are needed for the AKS cluster:

The auto-created resource group for `kube-cluster` AKS.

Notice that it contains a VM scale set and a Load balancer. There is also a Public IP that is attached to the Load balancer.

We need to set “kube-cluster” as current context in ~/.kube/config file and configure the credentials. For that we can use the following command from az-cli:

▶ az aks get-credentials -g multi-stage-aks -n kube-cluster

Here is what the AKS Cluster get’s by default:

Default objects created on the AKS cluster.

To note that there is not ingresses setup by default and that would be something we need to do later.

Understanding the Example App

For the Lab we will use the following dummy-app. Lets have a look at the source code:

What you need to notice is mainly that it will append an env line with the value of ENV environment variable and that it listens to port 8080.

I already pushed the image to docker hub. So, you can run the two following commands and compare the result:

▶ docker run -e ENV=dev -p 8080:8080 g0d3l/dummy-app

And the if you change the ENV variable:

▶ docker run -e ENV=prod -p 8080:8080 g0d3l/dummy-app

Here is a preview of the service that will be running:

The dummy-app running with ENV=dev set.

Creating Namespaces and Deploying the App Instances

For this tutorial we will create two namespaces dev and prod. We will deploy an instance of the app in each namespace while changing the ENV from dev to prod.

Note that in a real use-case these steps should be done through a pipeline and preferably using a package manager like Helm.

Let’s start by creating the namespaces:

▶ kubectl create namespace dev
namespace/dev created
▶ kubectl create namespace prod
namespace/prod created

Then lets take a look at our Kubernetes objects definitions:

We have a deployment and a service that will target the created pods. The service should be accessible at port 80 and will target pods internally at port 8080.

Note that there is an ENV variable to be specified. It will allow us to easily create multiple instances of these objects per namespace:

▶ export ENV=dev && envsubst < objects.yml | kubectl apply -f -deployment.apps/app created
service/app-svc created

And finally we do the same thing with ENV=prod and you can test If everything is setup correctly by doing a local port forwarding on the two services:

▶ kubectl port-forward service/app-svc -n prod 8080:80

The prod service should be now accessible through http://localhost:8080.

We are now fully set.

AKS with Nginx Ingress Controller

As a requirement, we will need a custom domain name. I registered mycloudapp.org from Google to show you how to configure one.

But first let’s start by installing the Ingress controller. For this we need helm. If you don’t already have it installed, you can use this command:

▶ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

Installing the Ingress Controller

You need to add the Nginx ingress helm repo to the client database:

▶ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx▶ helm repo update

Now let’s install the Ingress controller:

▶ helm install single-ingress ingress-nginx/ingress-nginx --set rbac.create=false -n kube-systemNAME: single-ingress
LAST DEPLOYED: Sun Oct 4 04:22:36 2020
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace kube-system get services -o wide -w single-ingress-ingress-nginx-controller'

Running the suggested command gives us the following:

NAME                                      TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)                      AGE    SELECTORsingle-ingress-ingress-nginx-controller   LoadBalancer   10.0.44.221   20.191.53.60   80:32306/TCP,443:31700/TCP   3m8s   app.kubernetes.io/component=controller,app.kubernetes.io/instance=single-ingress,app.kubernetes.io/name=ingress-nginx

This confirms that our Ingress is now functional and is attached to an Azure Load Balancer with an external IP of 20.191.53.60.

Load balancer configuration in Azure Portal.

As you can see in the screenshot above a new Frontend IP was added to the Load balancer with two rules (corresponding to ports 80 and 443).

Configure DNS Records

Next step is to go to the generated IP and configure a DNS name label on it. For my case, I now have http://dummy- app.northeurope.cloudapp.azure.com/ pointing to the IP that is configured in the Load balancer.

Generated IP Configuration in Azure Portal.

Next, I need to add to CNAME records on my custom domain name. As you can see in the screenshot below both records point to the same FQDN.

Relevant DNS config for the custom domain.

Create Ingresses in Each Namespace

Let’s take a look at the ingress definition. We will only see the dev one but changes for Prod are very clear and minimal:

We can run the following command:

▶ kubectl apply -f ingress.yml -n dev
ingress.extensions/cloudapp-ingress created

Now, our service is accessible via http://dev.mycloudapp.org ! You apply the same command on the namespace prod and by changing the host on the definition, it will be accessible on the corresponding FQDN.

dummy-app accessed from the external FQDN dev.mycloudapp.org.

(Optional) Ingress Controller with Fixed IP

The IP was automatically generated when creating installing the Ingress Controller using Helm. This presents one disadvantage is that the IP is dynamic and If any changes happen to the Ingress-controller or cluster it will likely result in the IP changing.

The solution is to create the IP beforehand. You can refer to this complete Microsoft docs which is very detailed on the Topic. Do not hesitate to ask in the comments, if you got stack or if you had some issues completing this part.

Conclusion

This tutorial shows you how to create multi-stages AKS deployment and traffic routing. I hope this can help you solve your scenarios or better understand the Kubernetes ecosystem. Feel free to comment, provide or ask for help around this topic.