Sitemap

Azure Kubernetes Service: Deploying and Managing Workloads

12 min readOct 22, 2026

Lesson 24. Introduction

This project demonstrates the end-to-end process of provisioning and managing Azure Kubernetes Service (AKS) clusters, deploying containerized applications, and exposing them publicly. It covers two main scenarios. In the first project, I created an AKS cluster with Azure CLI, deployed my Hopewell application, scaled the deployment to three replicas, and exposed it through a public LoadBalancer IP.

In the second project, I used a Kubernetes YAML manifest to deploy a sample online store with RabbitMQ and application services. I applied the manifest with kubectl and checked the pods and services to confirm that the workload was running. The screenshots document every major step — from SSH key generation and cluster creation through application deployment, service exposure, verification, and final cleanup.

Tools Used

Press enter or click to view image in full size

Labs
Project1: Creating and Scaling Workloads on Azure AKS with Azure CLI
Project 2: Deploying an Application on an AKS Cluster Using a Kubernetes Manifest File
.

Steps
>
Open VS Code and select a folder (k8S)
> Create a folder called Hopewell
> Change directory to the folder created i.e run cd hopewell
> Run the command: mkdir .ssh — This is to create a hidden SSH folder where the SSH key would be generated.
> Run the command: ssh-keygen -f .ssh/aks-ssh — This helps to generate the SSH key and hit the Enter button twice

cd hopewell
mkdir .ssh
ssh-keygen -f .ssh/aks-ssh
Press enter or click to view image in full size

This screenshot shows the navigation to the hopewell lab folder, creates a .ssh directory, and generates an SSH key pair (aks-ssh) using ssh-keygen. This key will later be used for secure node access when creating the AKS cluster.

Creating a Resource Group
> Run the command: az group create — name akss-rg — location eastus — You can replace RG name with the name of your choice.
> Run the command: az group list -o table — This command lists all the available RGs in a tabular form.

az group create --name akss-rg --location eastus
az group list -o table
Press enter or click to view image in full size

An Azure resource group named akss-rg is created in the East US region with the az group create command. The subsequent az group list confirms that the new resource group appears alongside existing ones and is in the Succeeded state.

Creating an AKS Cluster

> Run the command: az aks create — resource-group akss-rg — name CJAKSCluster — node-count 1 — nodepool-name cjsystem — node-resource-group CJNodeRG — ssh-key-value .ssh/aks-ssh.pub — network-plugin azure — enable-cluster-autoscaler — min-count 1 — max-count 2 — zones 1

az aks create --resource-group akss-rg --name CJAKSCluster --node-count 1 --nodepool-name cjsystem --node-resource-group CJNodeRG --ssh-key-value .ssh/aks-ssh.pub --network-plugin azure --enable-cluster-autoscaler --min-count 1 --max-count 2 
Press enter or click to view image in full size

The AKS cluster CJAKSCluster is provisioned inside akss-rg with one system node, SSH authentication, the Azure CNI network plugin, and cluster autoscaler enabled (min 1, max 2 nodes). The command returns a large JSON object describing the newly created cluster configuration.

Azure portal Confirmation
> Open the Azure portal to confirm the resource that has been created.

Press enter or click to view image in full size

In the Azure Portal, the akss-rg resource group is selected, revealing the managed Kubernetes service resource named CJAKSCluster. This confirms that the cluster created via the CLI is visible and managed through the portal.

Press enter or click to view image in full size

The automatically created node resource group CJNodeRG is examined. It contains the underlying infrastructure resources (public IP, network security group, virtual machine scale set, virtual network, managed identity, and load balancer) that support the AKS cluster.

Connect to the Cluster
> Run the command: az aks get-credentials — resource-group akss-rg — name CJAKSCluster

az aks get-credentials --resource-group akss-rg --name CJAKSCluster
Press enter or click to view image in full size

Credentials for the CJAKSCluster are retrieved with az aks get-credentials and merged into the local kubeconfig file. This step enables kubectl commands to target the new cluster.

Verifying the Cluster
> Run the command: kubectl get nodes

kubectl get nodes
Press enter or click to view image in full size

The command kubectl get nodes shows a single ready node (aks-cjsystem-…) running Kubernetes version 1.36.4, confirming that the cluster is healthy and accessible from the local machine.

Create the “frontend” Namespace
> Run the command: kubectl create namespace frontend

kubectl create namespace frontend
Press enter or click to view image in full size

Here, a new Kubernetes namespace called frontend is created with kubectl create namespace frontend. The command returns a success message indicating the namespace is ready for use.

Verify the created Namespace
>
Run the command: kubectl get namespaces # or
> Run the command: kubectl get namespaces -A

kubectl get namespaces
kubectl get namespaces -A
Press enter or click to view image in full size

The command kubectl get namespaces (and the all-namespaces variant) lists the newly created frontend namespace together with the default system namespaces, verifying that the namespace exists and is Active.

Use the Namespace in Deployments

> Run the command: kubectl create deployment my-app — image=emelecj/hopewell:v2 — replicas=3 -n frontend — port=80

kubectl create deployment my-app --image=emelecj/hopewell:v2 --replicas=3 -n frontend -- port=80
Press enter or click to view image in full size

In this screenshot, you will notice that an initial attempt to create a deployment named “my-app” fails because of an invalid label; a corrected command successfully creates the deployment using the nginx image with three replicas in the frontend namespace, exposing port 80.

Verify the Deployment
> Run the command: kubectl get deployments or
> Run the command: kubectl get deployments -A or
> Run the command: kubectl get deployment — namespace default

kubectl get deployments  or
kubectl get deployments -A or
kubectl get deployment --namespace default
Press enter or click to view image in full size

The command kubectl get deployments -A shows the my-app deployment in the frontend namespace (still scaling) along with the system deployments that ship with AKS, confirming the application workload has been scheduled.

Expose the deployment
> Run the command: kubectl expose deployment my-app — name=app-service — type=LoadBalancer — port=80 — protocol=TCP -n frontend

kubectl expose deployment my-app  --name=app-service --type=LoadBalancer --port=80 --protocol=TCP -n frontend
Press enter or click to view image in full size

The my-app deployment is exposed as a LoadBalancer service named app-service on port 80. Kubernetes creates the corresponding service object so the application can be reached from outside the cluster.

Get Emele Chijioke’s stories in your inbox

Join Medium for free to get updates from this writer.

Get the External IP
> Run the command: kubectl get services or
> Run the command: kubectl get services -A or
> Run the command: kubectl get services nginx-service — watch

kubectl get services
kubectl get services -A
kubectl get services nginx-service --watch
Press enter or click to view image in full size

The command kubectl get services -A lists the app-service LoadBalancer together with its external IP address (135.237.71.73). This public IP can now be used to access the nginx application.

Access App Via a Browser

Open your web browser and navigate to http://<EXTERNAL-IP>/. You should see the default home page of the application.

Clean Up
> Run the command: az group delete — name akss-rg — yes — no-wait
> Run the command: az aks delete — resource-group asks-rg — name MyAKSCluster — yes — no-wait

az group delete --name akss-rg --yes --no-wait
az aks delete --resource-group asks-rg --name MyAKSCluster --yes --no-wait
Press enter or click to view image in full size

Here, the original resource group akss-rg (and implicitly its managed node group) is deleted with az group delete. A subsequent list shows the groups in a Deleting state, beginning the cleanup of the first experiment.

Project 2: Deploying an Application on an AKS Cluster Using a Kubernetes Manifest File.

Steps

> Create a folder in the K8s main folder and call is aks-store

Press enter or click to view image in full size

In Visual Studio Code, the new workspace folder named aks-store that will contain the multi-service Contoso application manifests.

> Inside the aks-store folder, create a file called aks-store.yaml

Press enter or click to view image in full size

The file aks-store.yaml is opened in the editor; it is still empty at this moment, ready to receive the Kubernetes manifests for the microservices application.

apiVersion: apps/v1
kind: Deployment
metadata:
name: rabbitmq
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- name: rabbitmq
image: mcr.microsoft.com/mirror/docker/library/rabbitmq:3.10-management-alpine
ports:
- containerPort: 5672
name: rabbitmq-amqp
- containerPort: 15672
name: rabbitmq-http
env:
- name: RABBITMQ_DEFAULT_USER
value: "username"
- name: RABBITMQ_DEFAULT_PASS
value: "password"
resources:
requests:
cpu: 10m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
volumeMounts:
- name: rabbitmq-enabled-plugins
mountPath: /etc/rabbitmq/enabled_plugins
subPath: enabled_plugins
volumes:
- name: rabbitmq-enabled-plugins
configMap:
name: rabbitmq-enabled-plugins
items:
- key: rabbitmq_enabled_plugins
path: enabled_plugins
---
apiVersion: v1
data:
rabbitmq_enabled_plugins: |
[rabbitmq_management,rabbitmq_prometheus,rabbitmq_amqp1_0].
kind: ConfigMap
metadata:
name: rabbitmq-enabled-plugins
---
apiVersion: v1
kind: Service
metadata:
name: rabbitmq
spec:
selector:
app: rabbitmq
ports:
- name: rabbitmq-amqp
port: 5672
targetPort: 5672
- name: rabbitmq-http
port: 15672
targetPort: 15672
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 1
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- name: order-service
image: ghcr.io/azure-samples/aks-store-demo/order-service:latest
ports:
- containerPort: 3000
env:
- name: ORDER_QUEUE_HOSTNAME
value: "rabbitmq"
- name: ORDER_QUEUE_PORT
value: "5672"
- name: ORDER_QUEUE_USERNAME
value: "username"
- name: ORDER_QUEUE_PASSWORD
value: "password"
- name: ORDER_QUEUE_NAME
value: "orders"
- name: FASTIFY_ADDRESS
value: "0.0.0.0"
resources:
requests:
cpu: 1m
memory: 50Mi
limits:
cpu: 75m
memory: 128Mi
initContainers:
- name: wait-for-rabbitmq
image: busybox
command: ['sh', '-c', 'until nc -zv rabbitmq 5672; do echo waiting for rabbitmq; sleep 2; done;']
resources:
requests:
cpu: 1m
memory: 50Mi
limits:
cpu: 75m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
type: ClusterIP
ports:
- name: http
port: 3000
targetPort: 3000
selector:
app: order-service
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
spec:
replicas: 1
selector:
matchLabels:
app: product-service
template:
metadata:
labels:
app: product-service
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- name: product-service
image: ghcr.io/azure-samples/aks-store-demo/product-service:latest
ports:
- containerPort: 3002
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: product-service
spec:
type: ClusterIP
ports:
- name: http
port: 3002
targetPort: 3002
selector:
app: product-service
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: store-front
spec:
replicas: 1
selector:
matchLabels:
app: store-front
template:
metadata:
labels:
app: store-front
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- name: store-front
image: ghcr.io/azure-samples/aks-store-demo/store-front:latest
ports:
- containerPort: 8080
name: store-front
env:
- name: VUE_APP_ORDER_SERVICE_URL
value: "http://order-service:3000/"
- name: VUE_APP_PRODUCT_SERVICE_URL
value: "http://product-service:3002/"
resources:
requests:
cpu: 1m
memory: 200Mi
limits:
cpu: 1000m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: store-front
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: store-front
type: LoadBalancer
Press enter or click to view image in full size

The complete aks-store.yaml is displayed. It defines a RabbitMQ Deployment (with management plugin, environment variables, resource limits, and a ConfigMap volume) that will later be accompanied by the order, product, and storefront services.

Create Resource Group
> Ensure that you are in the Hopewell directory. Else, run the command: cd hopewell
> Run the command: az group create — name newaks-rg — location westus3

cd hopewell
az group create --name newaks-rg --location westus3
Press enter or click to view image in full size

A fresh resource group newaks-rg is created in the West US 3 region. The JSON output confirms successful provisioning of the new group that will host the second AKS cluster.

Create an AKS Cluster
> Run the command: az aks create — resource-group newaks-rg — name my-aks-cluster — node-count 1 — generate-ssh-keys

az aks create --resource-group newaks-rg --name my-aks-cluster --node-count 1 --generate-ssh-keys
Press enter or click to view image in full size

A new AKS cluster named my-aks-cluster is created inside newaks-rg with a single node and automatically generated SSH keys. The returned JSON describes the cluster’s configuration and node pool.

Connect to AKS Cluster
> Run the command: az aks get-credentials — resource-group newaks-rg — name my-aks-cluster
> Run the command: kubectl get nodes

az aks get-credentials --resource-group newaks-rg --name my-aks-cluster 
kubectl get nodes
Press enter or click to view image in full size

In this screenshot, Credentials for my-aks-cluster are retrieved, and kubectl get nodes verifies that a single node is Ready and running Kubernetes 1.35.8, confirming the second cluster is operational.

Verify Creation in Azure Portal

Press enter or click to view image in full size

The Azure Portal resource-groups view shows the newaks-rg group together with its associated managed cluster resource group, confirming both the control-plane and node resources exist.

Note: Change directory to the aks-store before running the commands below.
Then run the Following Commands, one at a time.
> Run the command: kubectl apply -f aks-store.yaml

kubectl apply -f aks-store.yaml
Press enter or click to view image in full size

From the aks-store directory, the full aks-store.yaml manifest is applied with kubectl apply -f. The output lists the successful creation of the ConfigMap, RabbitMQ service/deployment, and the order-service, product-service, and store-front deployments/services.

> Run the command: kubectl get pods

kubectl get pods
Press enter or click to view image in full size

The command kubectl get pods is used to show all four application pods (order-service, product-service, rabbitmq, and store-front) in the Running state with 1/1 ready containers, indicating a healthy microservices deployment.

> Run the command: kubectl get service

kubectl get service
Press enter or click to view image in full size

The command kubectl get service reveals that the store-front service has been assigned a public LoadBalancer IP (4.236.69.41) while the internal services remain ClusterIP-only. This external IP is the entry point to the Contoso storefront.

Access App Via a Browser

> Open your web browser and navigate to http://<EXTERNAL-IP>/. You should see the default home page of the application. E.g http://4.236.69.41

Press enter or click to view image in full size

Browsing to the LoadBalancer IP displays the fully functional Contoso pet-store website, complete with product catalog, prices, and “Add to Cart” buttons — proving that the entire microservices application is reachable from the internet.

Clean Up
> Run the command: az group list -o table
> Run the command: az group delete — resource-group newaks-rg — yes — no-wait

az group delete - resource-group newaks-rg - yes - no-wait
Press enter or click to view image in full size

All remaining resource groups (newaks-rg, its managed node group, and NetworkWatcherRG) are deleted. The final az group list confirms that the resources are in the Deleting state, completing the laboratory cleanup.

Conclusion

The sequence successfully illustrates the complete AKS lifecycle: cluster provisioning with SSH keys, namespace and deployment management, service exposure via LoadBalancer, verification of a working multi-tier application, and thorough resource cleanup. The project demonstrates both a simple single-container workload and a realistic microservices architecture (RabbitMQ-backed Contoso store), giving practical experience with Azure CLI, kubectl, and the Azure Portal.