Deploying a web application using Azure Web App, Azure PostgreSQL and Azure Kubernetes Service
Tags: azure, devops, cloud, database
Published: 2023-05-21 22:57:13

We will  build, deploy and manage a three tier application in Azure using Azure Web App, Azure PostgreSQL and Azure Kubernetes Service. A three tier application is basically an application which has a presentation layer (frontend), application layer (backend) and data layer (database). 

## Table of Contents
&nbsp;1. [Prerequisites](#prerequisites)
&nbsp;2. [Sample Application](#sample-application)
&nbsp;3. [Install Azure CLI and log in](#install-azure-cli-and-log-in)
&nbsp;4. [Create resource group in azure](#create-resource-group-in-azure)
&nbsp;5. [Deploy and run a containerized web app with Azure App Service](#deploy-and-run-a-containerized-web-app-with-azure-app-service)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.1. [Run Application Locally](#run-application-locally)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.2. [Create Dockerfile](#create-dockerfile)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.3. [Build Docker image and run Docker container](#build-docker-image-and-run-docker-container)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.4. [Create Azure Container Registry and push Docker image.](#create-azure-container-registry-and-push-docker-image)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.5. [Create and deploy a web app from Azure Container Registry repository](#create-and-deploy-a-web-app-from-azure-container-registry-repository)
&nbsp;6. [Create PostgreSQL Database](#create-postgresql-database)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.1. [Create an Azure Database for PostgreSQL - Flexible Server](#create-an-azure-database-for-postgresql-flexible-server)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.2. [Create Database](#create-database)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.3. [Connect to Database](#connect-to-database)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6.4. [Get Connection String](#get-connection-string)
&nbsp;7. [Deploy Microservices using AKS](#deploy-microservices-using-aks)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.1. [Restructure Monolithic application into a Microservice architecture](#restructure-monolithic-application-into-a-microservice-architecture)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.2. [Get environment variables](#get-environment-variables)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.3. [Run code locally](#run-code-locally)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.4. [Set up Dockerfile](#set-up-dockerfile)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.5. [Configure API Gateway using Reverse Proxy](#configure-api-gateway-using-reverse-proxy)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.6. [Create Images](#create-images)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.7. [Tag Container Images and Push to Registry](#tag-container-images-and-push-to-registry)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.8. [Create a Kubernetes cluster](#create-a-kubernetes-cluster)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.9. [Install the Kubernetes CLI](#install-the-kubernetes-cli)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.10. [Connect to the cluster using kubectl](#connect-to-the-cluster-using-kubectl)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.11. [Create deployment and service manifest files and configure environment variables.](#create-deployment-and-service-manifest-files-and-configure-environment-variables)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.12. [Deploy the application](#deploy-the-application)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7.13. [Expose Proxy endpoint and test application](#expose-proxy-endpoint-and-test-application)
&nbsp;8. [Build Frontend container image](#build-frontend-container-image)
&nbsp;9. [Test Application](#test-application)
&nbsp;10. [Continuous Integration with Azure pipelines](#continuous-integration-with-azure-pipelines)
&nbsp;11. [Clean up resources](#clean-up-resources)


## Prerequisites
- Azure account and subscription. If you do not have an account, you can create one [here](https://azure.microsoft.com/en-us/free/). [Login](https://portal.azure.com/) to your account if you have one.
- Basic understanding of microservice architecture.
- Basic experience using Docker commands to store and retrieve Docker images.
- Basic understanding of Kubernetes concepts.
- Have Docker desktop installed or you can install it [here](https://www.docker.com/products/docker-desktop/).
- Install Azure Data Studio.

## Sample Application
We will deploy an application which is used to verify users bank account number. The frontend was built with VueJS and the backend with ExpressJS. We will split the backend monolith application into a microservice architecture. A reverse proxy would be used to route incoming requests to the right microservice. 

The frontend application will be dockerized, pushed to Azure Container Registry and deployed using Azure Web App service. The backend application will be dockerized, pushed to Azure Container Registry and deployed using Azure Kubernetes Service. Azure PostgresSql database would be used to store user data.

## Install Azure CLI and log in
Azure CLI is a command-line tool to connect to Azure and execute administrative commands on Azure resources. Install for your operating system using this [link](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli). 

I am using MacOS, so i used the command:

```
brew update && brew install azure-cli
```

After installation, confirm the CLI was successfully installed by running `az version` in the terminal. If you get the versions as shown below, then you are now able to run azure commands.


![Install Azure CLI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z7x0nx49bofgm76kcd7q.png)

Log in to authenticate your account by running the command

```
az login
```


## Create resource group in azure
To create any resource in Azure, we need to have a resource group to hold related resources for a solution. You can create a resource group in azure portal or using the CLI.

In Azure portal,

1. Search for **Resource Groups**.
2. Click on **Create** or **Create resource group** button.
3. Fill in the **resource group** name and select a **region** where you would want your resources to be hosted.
![Create Azure Resource Group](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ic7xcxwqnr602rypkc5i.png)
4. Click on **Review + create** button. Then, select **Create**.

Using Azure CLI, we pass the name and location for the resource group as shown below. Run the command in the terminal.

```
az group create --name sca-project --location eastus
```


## Deploy and run a containerized web app with Azure App Service

Let us deploy and run our frontend application first. If you would like to follow along, clone the starter code [here](https://dev.azure.com/moyinadenuga/SCA%20Cloud%20School%20Project/_git/VERIFY-ACCOUNT-FRONTEND-STARTER). 

### Run Application Locally
To run locally, install all dependencies using `npm install`. Create a **.env** file and copy the contents of **.env.sample** into it. Add the application backend base url value  https://verify-account-api.onrender.com after the “=”. Then, run the command npm run serve.

### Create Dockerfile
Dockerfile is a file that contains instructions and commands that would be automatically run in sequence to build a docker image. Create the file named Dockerfile in the root of the project and the contents would be:

```
# Stage 1: Build the Vue app
FROM node:16 as build-stage
WORKDIR /app
ARG VUE_APP_BASE_URL
ENV VUE_APP_BASE_URL $VUE_APP_BASE_URL
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Stage 2: Serve the Vue app using NGINX
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

This Dockerfile is stating to use node version 16. The WORKDIR is used to define the working directory of a container. It creates and changes directory which is a combination of mkdir and cd commands. The ENV provides the environmental variable needed by the application. The package.json file is copied, dependencies are installed, files are copied from the local to the docker container and the build command is run. The application is served using nginx. We are exposing the application at port 80.

### Build Docker image and run Docker container
Open the Docker desktop application which provides the environment to build and run containerized application. On the terminal, build the Docker image using:

```
docker build -t <docker-image-name> .
```
Replace `<docker-image-name>` with the name you want to give the image. 

![Building Docker image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o8vk5o6cyuxfd1thi0ye.png)

Create a container from the image and run container using:

```
docker run -it -p 8080:80 --rm --name <container-name> <docker-image-name>
```
Replace `<container-name>` and `<docker-image-name>` with the name you want to give the container and the docker image name. We are also publishing the container port to 8080 using `-p 8080:80` and automatically removing the container on exit using the `--rm` flag. Read more on docker run commands here.

View application on http://localhost:8080/. Exit running the container using CTRL + C command.

### Create Azure Container Registry and push Docker image.
Azure Container Registry is a service which allows you to build, store, and manage container images in a private registry. Like Docker Hub, Container Registry uses repositories which contain one or more images. 

A major reason to use Container Registry is because it offers better security. Also, as it is hosted on Azure, you can store images close to locations where they would be deployed.

You can create the azure container registry on the Azure portal or using Azure CLI.

In Azure portal,
1. Search for **Container Registries**.
2. Click on **Create** or **Create container registry** button.
3. Select the **resource group** created earlier. Also, fill in the **registry name**. Leave the other fields as default.
![Create Container Registry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3vohocfqs84rbwre422.png)
4. Click on Review + create button. Then, select Create.

Using Azure CLI, we pass the **resource name** and **resource group** as parameters as shown below.

```
az acr create --name <resource-name> --resource-group <resource-group-name> --sku standard --admin-enabled true
```

**Parameters:**

- resource-name: must be alphanumeric and it is the name of your container registry.
- resource-group-name: use the resource group name created earlier.

To host the docker image created earlier, use:

```
az acr build --registry <container_registry_name> --image <docker-image-name> .
```
Replace `<container_registry_name>` with the name of the registry you created earlier and `<docker-image-name>` with the image created earlier.

If you would like to build a Docker image on Azure, run this command. This command copies the file contents to the Container Registry, then uses the Dockerfile to build the image and store it.

```
az acr build --file Dockerfile --registry <container_registry_name> --image <new-docker-image-name> .
```
Replace `<container_registry_name>` with the name of the registry you created earlier and `<new-docker-image-name>` as the name of the docker image that will be created.

It takes a while to build. Once done, view the Docker image in the Container Registry by selecting Repositories.

![View Container Registry](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1dquham58liwjxxd1wf5.png)


### Create and deploy a web app from Azure Container Registry repository

To create and deploy a web app, follow these steps:
1. Search for **Web App** and select.
2. On the **Basics** tab, enter the following values for each setting. Some of the fields are prepopulated. Make sure to confirm they are the ones you would like to use. Enter the web app name which would be the default url. For the Publish, change to Docker Container as we would be deploying from a container. I also updated my Pricing plan to a free one.
![Creating Azure Web App - Basics](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0xc0mhtu8hsm8jz2z5j.png)
3. Select **Next: Docker**.
4. On the **Docker** tab, change the image source to Azure Container Registry. Then, confirm the registry details.
![Creating Azure Web App - Docker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9yasmq6b9q2l4u2frsoo.png)
5. Select **Review and create**, and then select **Create**. It takes a while to deploy.
6. After deployment succeeds, select **Go to resource** to see the web app just created. 
7. In the top menu bar, select **Browse** to open the site in a new browser tab or copy the url. It might take a while the first time for the website to display.
![Open Application from Web App Service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfzirff5ns3zq55igk8m.png)
8. View application.
![View application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25cq962h4jem4r22tp1e.png)


## Create PostgreSQL Database

We will create a PostgreSQL server and database to store users information when they register and also retrieve it during login. 

### Create an Azure Database for PostgreSQL - Flexible Server
This can be created in Azure Portal or using the CLI.

In Azure Portal,
1. Search for **Azure Database for PostgreSQL** and Select it.
2. Click on **Create** on the left card to create the database.
![Creating Azure PostgreSQL Database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2d1lgycckisv8tqvh94p.png)
3. In the **Basics** tab, ensure to fill all required fields as shown below. Enter the **resource group** and database **server name**.Choose a **Region**.
![Creating Azure PostgreSQL Database - Flexible Server Basics 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xeugyvq1bwbt4hnq27r5.png)
For the **workload type**, select development since we are using it for a personal project.
![Creating Azure PostgreSQL Database - Flexible Server Basics 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23yi5nnm2o2ljwq06s6r.png)
Set up **username** and **password** for authentication
![Creating Azure PostgreSQL Database - Flexible Server Basics 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9yghvaa11rj9n524ch94.png)
4. In the **Networking** tab, allow Public access and you can also add your current IP address.
![Creating Azure PostgreSQL Database - Flexible Server Networking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xelin9uu5rrwivz6pkpu.png)
5. Select **Review and create**. Then, **Create**.
6. After Deployment, **Go to resource**.

Using the Azure CLI, we can use this command to create a PostgreSQL flexible server with custom parameters.

```
az postgres flexible-server create --location <location> --resource-group <resource-group> \
  --name <database-server-name> --admin-user <username> --admin-password <password> \
  --sku-name Standard_B1ms --tier Burstable --public-access all --storage-size 128 \
  --version 14
```
Replace the `<location>`, `<resource-group>`, `<database-server-name>`, `<username>` and `<password>` with the right values.

**Note:** Database server name should be globally unique.

### Create Database

Navigate to the **Databases** tab. Select the **Add** button. Fill in database name and **Save**.

![Create Database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7q4fllgyid212bjgjwyt.png)

### Connect to Database

Open **Azure Data Studio** application. Go to the extensions tab and search for **postgresql**.

![Installing postgresql extension](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yv9yodiq5nae2jmqgqa5.png).

If you encounter issue with the extension, check this [link](https://github.com/microsoft/azuredatastudio-postgresql/issues/108#issuecomment-1450720129).

Link azure account and connect to the database by adding the server and database name. Also, add your authentication details.

![Connecting to Azure Database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2k7vdwgmxsu12l40x3v.png)

Create a Users table that would be used by the backend application.

![Create Users table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jmkq52a9xayjevo4swng.png)

### Get Connection String

In Azure, go to the **Connection strings** tab. 
![Azure PostgreSQL Database connection strings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fbbuvp8jgadhsnzziqxc.png)

There are several ways to connect but we will be using the PostgreSQL connection url as it fits into the application we will use. Note, we would replace the postgres close to the end of the url with the database name `verify-account`.
 

## Deploy Microservices using AKS

Here, we will split our backend application into a microservice architecture. Then, we will dockerize the application, set up an application gateway and deploy in a kubernetes cluster. If you would like to follow along, clone the starter code [here](https://dev.azure.com/moyinadenuga/SCA%20Cloud%20School%20Project/_git/VERIFY-ACCOUNT-BACKEND-STARTER).

View the deployed microservice application code [here](https://dev.azure.com/moyinadenuga/SCA%20Cloud%20School%20Project/_git/VERIFY-ACCOUNT-BACKEND).

### Restructure Monolithic application into a Microservice architecture

The current project contains the logic for both /users and /verify-account endpoint. We will decompose the API code into 2 different services that can be run independent of one another.

Create two new directories (as services) named user and verify-account. Delete the `package-lock.json` file. Then, copy the content of the root directory into both folders. The folder directory should look like this. 

![New Microservice folder directory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/by5q473oxut5559oy5ac.png)

Go through the folders and files, and remove code not related to each service. If you get stuck or are unsure of what is left, compare with the code [here](https://dev.azure.com/moyinadenuga/SCA%20Cloud%20School%20Project/_git/VERIFY-ACCOUNT-BACKEND-STARTER?version=GBmicroservice).

### Get environment variables
For the `DATABASE_URL`, use the PostgreSQL connection url and replace the password. To get the `MAIL_USERNAME`, `MAIL_PASSWORD`, `OAUTH_CLIENTID`, `OAUTH_CLIENT_SECRET` and `OAUTH_REFRESH_TOKEN`, check this [guide](https://nodemailer.com/smtp/oauth2/#oauth-3lo) to learn how to create these keys for nodemailer. If you do not want to set these up, you can comment out the part in the code using it. The `TOKEN_KEY` and `RESET_TOKEN_KEY` are random strings used to hash the user password. Create a Paystack account, Add a Business and use the Test API Secret key as the `PAYSTACK_SECRET_KEY`. The `FRONTEND_BASE_URL` is also used in the email to route the user back to the frontend application.

### Run code locally

For each microservice, change directory to the microservice folder and run `npm install` command to install all project dependencies. Create a **.env** file and copy the content of **.env.sample** file. Put the values gotten above. Run `npm start` or `npm run dev` to test the application. 

Let's test the register user endpoint http://localhost:8080/api/users/register.

![Register user endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vu4k4rej1dlj0czfbdiy.png)

If the endpoint does not return a response, then something is wrong. Try to debug and fix the issue.

### Set up Dockerfile
Remember the dockerfile we created earlier for the frontend application. We will create a similar one for the microservices. In the microservices project directory, create a file named `Dockerfile` and paste this code:

```
FROM node:14
# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
COPY package*.json ./
RUN npm install

# Bundle app source
COPY . .
EXPOSE 8080
CMD [ "npm", "start" ]

```
Also, create a `.dockerignore` file and add node_modules.

### Configure API Gateway using Reverse Proxy

[API Gateway](https://learn.microsoft.com/en-us/azure/architecture/microservices/design/gateway) is used to ensure requests are routed to the right service. There are a couple of ways this can be done. These include using Azure API Management, Azure Application Gateway and others, but we will be using reverse proxy. 

Create a folder and name it `reverse-proxy`. Create a `Dockerfile` in it and paste this:

```
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
```

Also, create a nginx.conf file and configure it. Read up on how it works [here](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/).

```
worker_processes 1;  
events { worker_connections 1024; }
error_log /dev/stdout debug;
http {
    sendfile on;
    upstream user {
        server user:8080;
    }
    upstream verify-account {
        server verify-account:8080;
    }
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-NginX-Proxy true;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;    
    server {
        listen 8080;
        location /api/verify-account {
            proxy_pass         http://verify-account;
        }
        location /api/users {
            proxy_pass         http://user;
        }            
    }
}
```

### Create Images
To build multiple images from different Dockerfile, we need to create a `docker-compose-build.yaml` file in the project root directory.

The file contains the path to the different directory and you also specify the image name. This is how the `docker-compose-build.yaml` file looks.

```
version: "3"
services:
  reverseproxy:
    build:
      context: ./reverse-proxy
    image: reverseproxy
  verify_account:
    build:
      context: ./verify-account
    image: verify-account
  user:
    build:
      context: ./user
    image: user

```
Then run this command to build the images.

```
docker compose -f docker-compose-build.yaml build --parallel
```

To run the images in a container, create a `docker-compose.yaml` file.

```
version: "3"
services:
  reverseproxy:
      image: reverseproxy
      ports:
          - 8080:8080
      restart: always
      depends_on:
        - user
        - verify-account
  user:
    image: user
    environment:
      DATABASE_URL: $DATABASE_URL
      MAIL_USERNAME: $MAIL_USERNAME
      MAIL_PASSWORD: $MAIL_PASSWORD
      OAUTH_CLIENTID: $OAUTH_CLIENTID
      OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
      OAUTH_REFRESH_TOKEN: $OAUTH_REFRESH_TOKEN
      TOKEN_KEY: $TOKEN_KEY
      RESET_TOKEN_KEY: $RESET_TOKEN_KEY
      PAYSTACK_SECRET_KEY: $PAYSTACK_SECRET_KEY
      FRONTEND_BASE_URL: $FRONTEND_BASE_URL
  verify-account:
    image: verify-account
    environment:
      TOKEN_KEY: $TOKEN_KEY
      RESET_TOKEN_KEY: $RESET_TOKEN_KEY
      
```

Create a `.env` file in the root directory and paste all the project environment variables. Use the `docker compose config` to confirm all the environment variables are set. Then, use this command to run the container. 

```
docker compose up
``` 

Test the application by using any of the endpoint. It should work as before. When you go to the Database and query for all users, you should see the user added.

![Send Request to Register Endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6mk5v4ykj8238zfta54.png)

![Query Database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zet0qo4pj2nwkskrqo4.png)

### Build and Push to Registry

Log in to the container registry to connect to the Kubernetes cluster from a local computer. The `<acrName>` is the name of the container registry e.g scacloudproject.

```
az acr login --name <acrName>
```
Build the image and push to the Container Registry using the `acr build` command.

```
az acr build --file Dockerfile --registry <container_registry_name> --image <new-docker-image-name> .
```

Confirm the images are successfully pushed by checking the portal.

![Container images](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1gwkyjc1j3k9u2kibcnq.png)

You can also view in the CLI using:

```
az acr repository list --name <acrName> --output table
```

### Create a Kubernetes cluster
You can use the command below to create a kubernetes cluster. If you do not have the **Owner** or **Azure account administrator role** in your Azure subscription , use this [guide](https://learn.microsoft.com/en-us/azure/aks/azure-ad-rbac?tabs=portal) to create Kubernetes cluster with role based access control.

```
az aks create \
    --resource-group <resource-group> \
    --name <aks-cluster-name> \
    --node-count 2 \
    --generate-ssh-keys \
    --attach-acr <acrName>
```
This command takes a while to run. You can also create it in the portal using this [guide](Azure account administrator role in your Azure subscription).

### Install the Kubernetes CLI
Use the Kubernetes CLI, kubectl, to connect to the Kubernetes cluster from a local computer.

```
az aks install-cli
```

### Connect to the cluster using kubectl

Connect to the cluster.

```
az aks get-credentials --resource-group <resource-group> --name <aks-cluster-name>
```
To verify connection to the cluster, run kubectl get nodes to return a list of cluster nodes.

```
kubectl get nodes
```

![List of nodes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ou4v3655je7lb8kplcau.png)

### Create deployment and service manifest files and configure environment variables.

A manifest file is usually a YAML file which is used to specify the configuration for a Kubernetes object that can create and update a set of identical pods. Each pod runs specific containers, which are defined in the `spec.template` field of the YAML configuration. 

Kubernetes also helps in handling scalability of pods, ensuring the correct number of pods are running, and taking care of updates to pods on an ongoing basis. All these activities can be configured through fields in the Deployment YAML.

Create a deployments folder in the project root directory to store all the manifest files. Create manifest file for all the services. Ensure the right image is added for each pod

Here is how the `user-deployment.yaml `file looks.

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user
spec:
  replicas: 2
  selector:
    matchLabels:
      app: user
  template:
    metadata:
      labels:
        app: user
    spec:
      containers:
      - name: user
        image: scacloudproject.azurecr.io/user
        envFrom:
          - secretRef:
            name: verify-account-secret
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 250m
            memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
  name: user
spec:
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
  selector:
    app: user

```

From the file above, we can see the envFrom variable which specifies where to get environment variables. Let's configure the [secret](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/) file. 

```
kubectl create secret generic <secret-name> <data-source>
```
![Create Secret File](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zebiboeuzs9xmx1b3h57.png)

Other ways to get the env variables in the deployment files are by specifying them [directly](https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/) or using [configmap](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#create-a-configmap) for configuration variables. 

### Deploy the application
To deploy all the manifest files, create a bash script named `deploy.sh` in the `deployments` folder and add all the deployment commands for each container.

```
kubectl apply -f <filename>
```

![List of Kubernetes Deployment Commands](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7nwe6wzopqzlg6slrzbn.png)

Change directory to the `deployments` directory and run the script with `./deploy.sh`. 

Run `kubectl get pods` to check the status of the pods.

![List of pods](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6674vm4y852ssk7ufelk.png)

**Debugging**
If you encounter any issue with the deployment, check your indentation as it is very important in a YAML file or try to search online for help. Also you would need to delete all deployment and services using these commands.
```
kubectl delete --all service
kubectl delete --all deployment
```
Run `kubectl get pods` to ensure none is running. Then, run the script `./deploy.sh` again. Then, run `kubectl get pods` and ensure all pods status are running.

### Expose Proxy endpoint and test application

To get the url we will use to access the frontend microservice, we will expose the reverse proxy port. After, get the IP address with port number using `kubectl get services`. 

```
kubectl expose deployment <reverse-proxy-name> --type=LoadBalancer --name=<public-reverse-proxy-name>
```

![Expose proxy endpoint and get url](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pv0f28las1059k54ih98.png)

Try to make a request in Postman application using any of the endpoint to test the url.

![Test AKS Deployed application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jq9001l4pl4kfbblcldr.png)

## Build Frontend container image
Build the container image again to use the url. Run

```
az acr build --file Dockerfile --registry <container_registry_name> --image <docker-image-name> . --build-arg VUE_APP_BASE_URL=<api-base-url>
``` 

## Test Application
In the **configurations**, under the General Settings tab, turn off HTTPs only requests.

![Set up Configurations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5pu4tbjcvf18po4prulw.png)

Add the backend url to CORS as well to prevent CORS origin issues.

![Updated Cors Origin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lypbf6l74twn9g40fqzl.png)

I encountered a mixed content error because https website was trying to access a http url. On Google Chrome, turn it off by going to the site lock, then click on site settings. Scroll down to insecure content, and **allow**. Restart the App Service.

Go back to the application and make requests.

![Web application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q73sund2e32sbzgcjs1x.png)

On reload of routes which were not the base routes, I encountered a 404 error which happens for single page application. To fix this, create a `nginx.conf` file in the frontend project root folder and add this code to route the requests.

```
server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

}
```
Also, add it to the Dockerfile, rebuild image and restart App Service.

![Updated Dockerfile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfsvnur6gyvfiqlomazn.png)

View Deployed application

{% embed https://drive.google.com/file/d/11V09mBG2eGJwbtv3SWFGzx0uPLbU_qzQ/view?usp=share_link %}

## Continuous Integration with Azure pipelines
To avoid manually building the images, it is ideal to set up a continuous integration process to ensure that for every push to the branch or PR merged, an image is built and pushed to the container.  

This can be easily done by going to the Pipelines tab on Azure DevOps and creating a new pipeline. Use this [guide](https://learn.microsoft.com/en-us/azure/devops/pipelines/apps/cd/deploy-docker-webapp?view=azure-devops&tabs=java%2Cyaml) to set up a pipeline as I am not able to due to some permissions.

## Clean up resources

We created several resources using our Azure subscription. We will clean up these resources so that we won't continue to be charged for them. You can delete on the CLI or in the portal.

Delete with CLI using:

```
az group delete --name myResourceGroup --yes --no-wait
```

In the Portal,
1. In Azure, select **Resource groups**.
2. Find the **resource group name** you used, and select it.
3. In the Overview tab of the resource group, select Delete resource group.
4. A dialog box appears prompting you to confirm the deletion. Enter the name of the resource group again, and select **Delete**. 
5. Select **Delete** again to confirm deletion. This will delete all of the resources created.