Step-by-Step Guide: Deploying an ASP.NET Core Application with Azure Kubernetes Services, Azure SQL Server, and Azure Container Registry

15 minute read

What is Azure Kubernetes Services (AKS)? Azure Kubernetes Services (AKS) is a managed Kubernetes service provided by Microsoft Azure. It offers a simplified way to deploy, manage, and scale containerized applications using Kubernetes. AKS is a fully managed service that eliminates the complexity of managing Kubernetes clusters, allowing you to focus on building and deploying your applications.

What is Azure SQL Server? Azure SQL Server is a managed relational database service provided by Microsoft Azure. It offers a scalable, secure, and fully managed database service that is compatible with SQL Server. Azure SQL Server provides high availability, automatic backups, and built-in security features to protect your data.

What is Azure Container Registry (ACR)? Azure Container Registry (ACR) is a managed container registry provided by Microsoft Azure. It allows you to store and manage container images securely in the cloud. ACR integrates with Azure services like Azure Kubernetes Services (AKS) to simplify the deployment of containerized applications.

In this article, you’ll learn how to deploy an ASP.NET Core application using Azure Kubernetes Services (AKS), Azure SQL Server, and Azure Container Registry (ACR). The steps below assume you have some basic familiarity with ASP.NET Core, Azure services, and Docker.

Prerequisites Before you begin, ensure you have the following:

  • An Azure subscription (create a free account if you don’t have one)
  • Azure CLI installed on your local machine
  • Docker Desktop installed on your local machine
  • Visual Studio or any other code editor
  • Basic knowledge of ASP.NET Core, Docker, and Kubernetes
  • Kubectl installed on your local machine

Step 1: Create an Azure SQL Database

  • Open the Azure portal and sign in with your Azure account.
  • Click on the “Create a resource” button and search for “SQL Database”.
  • Click on the “Create” button to create a new SQL Database.
  • Fill in the required details like server name, database name, resource group, and pricing tier.
  • Click on the “Review + create” button to create the SQL Database.

or

  • Login to Azure CLI
az login
  • Create a resource group for the SQL Server and Database
  • Create a SQL Server and Database using Azure CLI
# Create a resource group
az group create --name <ResourceGroupName> --location <Location>

# Create a SQL Server
az sql server create --name <SqlServerName> --resource-group <ResourceGroupName> --location <Location> --admin-user <AdminUser> --admin-password <AdminPassword>

# Create a SQL Database with the S0 pricing tier
az sql db create --resource-group <ResourceGroupName> --server <SqlServerName> --name <DatabaseName> --service-objective S0

Example of creating a SQL Server and Database using Azure CLI:

# Create a resource group named myresourcegroup in the westus location
az group create --name myresourcegroup --location westus

# Create a SQL Server named myserver in the myresourcegroup resource group with the admin user mahedee and password mypass@123
az sql server create --name mahedeesqlserver --resource-group myresourcegroup --location westus --admin-user mahedee --admin-password mypass@123

# Create a SQL Database named mydatabase in the myresourcegroup resource group with the S0 pricing tier
az sql db create --resource-group myresourcegroup --server mahedeesqlserver --name HRMDB --service-objective S0
  • Set Up Firewall Rules Allow your local machine or AKS to access the SQL server.
  • Try to connect to the SQL Server using SQL Server Management Studio (SSMS) or Azure Data Studio to verify the connection.
  • You will get a message that the IP address is not allowed to access the server.
  • It will show the IP address range that is need to be added to the firewall rules.
  • Collect start and end IP address range from the error message and add the IP address range to the firewall rules using the Azure CLI.
# Allow your local machine to access the SQL server
az sql server firewall-rule create --resource-group myresourcegroup --server mahedeesqlserver --name AllowLocalMachine --start-ip-address <YourIPAddress> --end-ip-address <YourIPAddress>
# Example: Allow your local machine to access the SQL server
az sql server firewall-rule create --resource-group myresourcegroup --server mahedeesqlserver --name AllowLocalMachine --start-ip-address 162.237.182.0 --end-ip-address 162.237.182.255

Step 2: Create an ASP.NET Core Application

  • Create a new ASP.NET Core application using Visual Studio or the .NET CLI.
  • Install the required NuGet packages for Azure SQL Database and Entity Framework Core.
  • Configure the connection string to your Azure SQL Database.
  • Create the required models and DbContext for your application.
  • Add the required services and middleware to your application.
  • Build and run your ASP.NET Core application locally to test it.

In this Article I have created an asp.net core web api application name HRM.API. Source of the application is given at the end of the article.

  • Now create a Dockerfile in the root of your project to containerize your ASP.NET Core application.

  • I have added lot of comments in the Dockerfile to explain each step.

Dockerfile

# Define the base image
# Use the official ASP.NET Core runtime image the alias base sets here to use it in the next stage

# Switches the execution to a user named app. This is a security best practice to avoid running as root.
# The app user must exist in the aspnet:8.0 image. If not, this command will fail unless the user is created beforehand.

# Sets the working directory to /app. All subsequent commands in this stage will be relative to this directory.

# Informs Docker that the container will listen on ports 8080 and 8081. 
# This is for documentation purposes and does not expose the ports to the host by itself; you need to publish these ports explicitly with docker run -p.

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
EXPOSE 8081


# Define the Build Stage
# Use the official ASP.NET Core SDK image to build the application
# Set the working directory to /src. This directory will be existing only in the build stage.
# Copies all files from the current directory on the host machine to the /src directory in the container.
# Restores project dependencies from the .csproj file. This ensures all necessary NuGet packages are downloaded.
# Builds the application in Release configuration and outputs the compiled files to the /app directory inside the container.
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app

# Final State
# Use the base image defined in the first stage.
# Set the working directory to /app.
# Copies the compiled application from the build stage to the /app directory in the final image.
# Defines the command to run when the container starts. Here, it launches the application by executing the .NET runtime to run the HRM.API.dll file.
FROM base AS final
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "HRM.API.dll"]

# You can see only the app directory in the final image because Multi-stage builds isolate the content of each stage from the other stages.
# Docker multi-stage build process implicitly discards everything in the intermediate stages when the final stage is built.
  • Build and Test the Docker Image Locally
# Build the Docker image using the Dockerfile
docker build -t hrm:latest .

# Run the Docker image locally
docker run -p 8080:8080 hrm:latest

Access your application at http://localhost:8080/swagger/index.html to verify it works.

Step 3: Create an Azure Container Registry (ACR)

  • Open the Azure portal and sign in with your Azure account.
  • Click on “Create a resource” and search for “Container Registry”.
  • Select “Container Registry” and click on “Create”.
  • Fill in the required details like registry name, resource group, location, and SKU.
  • Click on “Review + create” and then “Create” to create the ACR.

or

  • Create an Azure Container Registry using Azure CLI
# Create a resource group for the ACR
# Since we already have a resource group, we can skip this step
az group create --name <ResourceGroupName> --location <Location>

# Create an ACR
az acr create --name <ACRName> --resource-group <ResourceGroupName> --sku Basic --location <Location>

Example of creating an Azure Container Registry using Azure CLI:

# Create a resource group named myresourcegroup in the westus location
# Since we already have a resource group, we can skip this step
az group create --name myresourcegroup --location westus

# Create an ACR named myacr in the myresourcegroup resource group with the Basic SKU
az acr create --name mh17acr --resource-group myresourcegroup --sku Basic --location westus

Step 4: Push the Docker Image to Azure Container Registry (ACR)

  • Tag the Docker image with the ACR URL. Tagging the image with the ACR URL is required to push the image to the ACR.
  • Log in to the ACR using the Azure CLI.
  • Push the Docker image to the ACR.
# Tag the Docker image with the ACR URL
docker tag hrm:latest <ACRName>.azurecr.io/hrm:latest
# Login to the ACR using the Azure CLI
az acr login --name <ACRName>
# Push the Docker image to the ACR
docker push <ACRName>.azurecr.io/hrm:latest

Example of pushing a Docker image to Azure Container Registry using Azure CLI:

# Tag the Docker image with the ACR URL
docker tag hrm:latest mh17acr.azurecr.io/hrm:latest
# Login to the ACR using the Azure CLI
az acr login --name mh17acr
# Push the Docker image to the ACR
docker push mh17acr.azurecr.io/hrm:latest
  • Verify that the Docker image is successfully pushed to the ACR by checking the ACR in the Azure portal. or using Azure CLI
# List the repositories in the ACR
az acr repository list --name <ACRName> --output table

Example of listing the repositories in the ACR using Azure CLI:

  # List the repositories in the ACR
  az acr repository list --name mh17acr --output table

Step 5: Create an Azure Kubernetes Services (AKS) Cluster

  • Open the Azure portal and sign in with your Azure account.
  • Click on the “Create a resource” button and search for “Kubernetes Service”.
  • Click on “Create” to create a new AKS cluster.
  • Fill in the required information, such as the resource group, location, and cluster name.
  • Basics Tab:
    • Subscription: Choose your Azure subscription.
    • Resource Group: Create a new resource group or select an existing one.
    • Kubernetes Cluster Name: Enter a unique name for your AKS cluster.
    • Region: Select the region for your cluster.
    • Click “Next: Node Pools” to proceed.
  • Node Pools Tab:
    • Click “Add a node pool” to add a new node pool.
    • Node Pool Name: Enter a name for the node pool.
    • Virtual Machine Size: Choose the VM size for the node pool.
    • Node Count: Enter the number of nodes in the pool.
    • Click “Next: Authentication” to proceed.
  • Authentication Tab:
    • Leave the default settings for authentication.
    • Click “Next: Networking” to proceed.
  • Networking Tab:
    • Leave the default settings for networking.
    • Click “Next: Monitoring” to proceed.
  • Monitoring Tab:
    • Enable monitoring if required.
    • Click “Next: Tags” to proceed.
  • Tags Tab:
    • Add any required tags to the cluster.
    • Click “Next: Review + create” to proceed.
  • Review + create Tab:
    • Review the cluster configuration.
    • Click “Create” to create the AKS cluster.
  • Click on “Review + create” and then “Create” to create the AKS cluster.
  • Once the cluster is created, click on the “Connect” button to connect to the cluster using kubectl.

or

  • Create an AKS cluster using Azure CLI
# Create a resource group for the AKS cluster
az group create --name <ResourceGroupName> --location <Location>
# Create an AKS cluster
az aks create --resource-group <ResourceGroupName> --name <AKSName> --node-count <NumberOfNodes> --enable-addons monitoring --generate-ssh-keys

Example of creating an AKS cluster using Azure CLI:

# Create a resource group for the AKS cluster
# Since we already have a resource group, we can skip this step
az group create --name myresourcegroup --location westus

# Create an AKS cluster
az aks create --resource-group myresourcegroup --name myakscluster --node-count 1 --enable-addons monitoring --generate-ssh-keys --location westus --kubernetes-version 1.29.9 --node-vm-size Standard_B2s --enable-cluster-autoscaler --min-count 1 --max-count 3

Note: Sometimes, CLI commands may not work due to the version of the CLI. In that case, you can use the Azure portal to create the AKS cluster.

  • Now Connect to the AKS cluster. Use the following command to connect to the AKS cluster:
az aks get-credentials --resource-group <ResourceGroupName> --name <AKSName>

Example of connecting to an AKS cluster using Azure CLI:

# Connect to the AKS cluster
az aks get-credentials --resource-group myresourcegroup --name myakscluster

# Output
Merged "myakscluster" as current context in /Users/username/.kube/config
  • To get the list of contexts, run the following command:
kubectl config get-contexts
  • To switch to a different context, run the following command:
kubectl config use-context <ContextName>

Step 6: Link ACR(Azure Container Registry) to AKS If the Azure Kubernetes Service (AKS) cluster is not linked to your ACR, Kubernetes cannot pull the image.

Run the following command to allow AKS to access your ACR:

az aks update -n <aks-cluster-name> -g <resource-group-name> --attach-acr mh17acr

Example of linking an ACR to an AKS cluster using Azure CLI:

# Link the ACR to the AKS cluster
az aks update -n myakscluster -g myresourcegroup --attach-acr mh17acr 

Step 7: Deploy the ASP.NET Core Application to AKS

  • Connect to the AKS cluster
az aks get-credentials --resource-group <ResourceGroupName> --name <AKSName>

Example of connecting to an AKS cluster using Azure CLI:

# Connect to the AKS cluster
az aks get-credentials --resource-group myresourcegroup --name myakscluster

# Output
Merged "myakscluster" as current context in /Users/username/.kube/config
  • To get the list of contexts, run the following command:
kubectl config get-contexts
  • To switch to a different context, run the following command:
kubectl config use-context <ContextName>
  • Create a Kubernetes Pod for the ASP.NET Core application
  • Here I used lots of comments to explain each section of the yaml file. You can remove the comments before using it.

    pod_aks.yml


apiVersion: v1
kind: Pod # Pod is the smallest deployable unit in Kubernetes and it represents a single instance of a running process in your cluster.
metadata: # Metadata section provides information about the Pod
  name: hrm-pod # Name of the Pod. Name must be unique within the namespace.
  labels:       # Labels are key/value pairs that are attached to objects, such as pods. Labels are intended to be used to specify identifying attributes of objects that are meaningful and relevant to users.
    app: hrm-api

# Specification for the Pod
spec:
  containers: # List of containers within the Pod
    - name: hrm-ctr
      image: mh17acr.azurecr.io/hrm:latest 
      ports:
        - containerPort: 8080 # Port that the container will expose
      env: # List of environment variables to set in the container
        - name: ConnectionStrings__DefaultConnection # Name of the environment variable. Here __ is used to represent the hierarchy of the connection string. It means ConnectionStrings:DefaultConnection
          value: "Server=mahedeesqlserver.database.windows.net;Database=HRMDB;User Id=mahedee;Password=mypass@123;MultipleActiveResultSets=true"
  
  • Create a Kubernetes Service for the ASP.NET Core application
  • Here I used lots of comments to explain each section of the yaml file. You can remove the comments before using it.

    service_aks.yml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: hrm-api
  name: hrm-service
spec:
  type: LoadBalancer
  ports:
    # First port configuration
    - port: 8011 # Port that the service will expose
      protocol: TCP # Protocol that the service will use
      targetPort: 8080 # Port on the container where traffic will be directed
      name: http # Name of the port (useful for service discovery)
      nodePort: 30376 # Specific port on the cluster nodes to expose (used with NodePort/LoadBalancer services)
    # Second port configuration
    - port: 8081 # Port that the service will expose
      protocol: TCP # Protocol that the service will use
      targetPort: 8081 # Port on the container where traffic will be directed
      name: https # Name of the port (useful for service discovery)
      nodePort: 30676 # Specific port on the cluster nodes to expose (used with NodePort/LoadBalancer services)
  # Selector matches Pods with the label "app: hrm-api"
  selector:
    app: hrm-api
status:
  loadBalancer: {} # Placeholder for LoadBalancer details (IP or DNS name) once provisioned

# Use spaces (2 or 4 spaces) for indentation in YAML files; tabs are not allowed.
# The YAML specification prohibits the use of tabs, and using spaces ensures consistency and proper parsing.
  
  • Deploy the ASP.NET Core application to the AKS cluster

    kubectl apply -f pod_aks.yml
    kubectl apply -f service_aks.yml
    
  • Verify that the ASP.NET Core application is deployed to the AKS cluster by checking the pods and services in the Kubernetes dashboard or using the following commands:

  kubectl get pods

  Output:

  NAME      READY   STATUS    RESTARTS   AGE
  hrm-pod   1/1     Running   0          16s
  kubectl get svc

  Output:
  NAME          TYPE           CLUSTER-IP   EXTERNAL-IP      PORT(S)                         AGE
  hrm-service   LoadBalancer   10.0.80.25   52.137.187.193   8011:30376/TCP,8081:30676/TCP   24s
  kubernetes    ClusterIP      10.0.0.1     <none>           443/TCP                         21m
  • Access the ASP.NET Core application using the external IP address of the service.
  • In this access the application using the following URL:

http://52.137.187.193:8011/swagger/index.html

  • To delete the pod and service, use the following commands:
kubectl delete -f pod_aks.yml
kubectl delete -f service_aks.yml
  • Command to get log of a pod
    kubectl logs -f hrm-pod
    

Trouble Shooting: Application cannot connect to the Azure SQL Database

  • Run the following command to check the logs of the pod to identify any connection issues:
    kubectl logs -f hrm-pod
    
  • You will get the error message in the logs if there is any issue connecting to the Azure SQL Database.
  • It says AKS pod cannot connect to the Azure SQL Database. It will show source IP address is not allowed to access the server.
  • To resolve this issue, you need to allow the AKS cluster’s IP address to access the Azure SQL Database.
  • Run the following command to set up the firewall rule to allow the AKS cluster’s IP address to access the Azure SQL Database:
    az sql server firewall-rule create --resource-group <ResourceGroupName> --server <SqlServerName> --name AllowAKSIP --start-ip-address 13.64.73.58 --end-ip-address 13.64.73.58
    

    Example:

    az sql server firewall-rule create --resource-group <ResourceGroupName> --server <SqlServerName> --name AllowAKSIP --start-ip-address 13.64.73.58 --end-ip-address 13.64.73.58
    
  • After setting up the firewall rule, try to access the Azure SQL Database from the AKS pod again. The connection should be successful now.
  • Kubernetes pods ip address can be changed after restart. So, it is better to use static ip address for the AKS cluster.

Common Commands:

  • To update azur CLI run the following command:
az upgrade
  • To know the region of the AKS cluster, run the following command:
    az aks show -n <aks-cluster-name> -g <resource-group-name> --query location -o tsv
    
  • To know the region of the Azure SQL Database, run the following command:
    az sql server show -n <sql-server-name> -g <resource-group-name> --query location -o tsv
    
  • To know the ip address of the AKS cluster, run the following command:
    az aks show -n <aks-cluster-name> -g <resource-group-name> --query outboundIpAddresses -o tsv
    
  • To get list of contexts, run the following command:
    kubectl config get-contexts
    
  • To switch to a different context, run the following command:
    kubectl config use-context <context-name>
    
kubectl get pods --watch  # Watch the pods in the current namespace
  • To get the logs of a pod, run the following command:
    kubectl logs -f <pod-name>
    
  • To get the logs of a pod in a specific container, run the following command:
    kubectl logs -f <pod-name> -c <container-name>
    
  • Describe a pod
    kubectl describe pod <pod-name>
    
  • To get the details of a pod, run the following command:
    kubectl get pod <pod-name> -o yaml
    

Source code