π
In this lab, we will:
- Create an Azure Container Registry (ACR)
- Build a custom Docker image in the cloud
- Deploy it to Azure Kubernetes Service (AKS)
- Expose it publicly using a LoadBalancer
This is a production-style workflow used by modern cloud-native teams.
π Architecture Overview
Developer β ACR β AKS β Service (LoadBalancer) β Public IP
We use:
- Azure Kubernetes Service (AKS)
- Azure Container Registry (ACR)
- Managed Identity for secure image pulls
π§ Prerequisites
You already created AKS:
az aks create \
--resource-group aks-east2-rg \
--name aks-prod-east2 \
--location eastus2 \
--node-count 2 \
--network-plugin azure \
--enable-managed-identity \
--enable-oidc-issuer \
--enable-workload-identity \
--enable-addons monitoring
Set variables:
RG=aks-east2-rg
CLUSTER=aks-prod-east2
ACR_NAME=akseast2acr$RANDOM
LOCATION=eastus2
1οΈβ£ Register Required Azure Providers (Important)
New subscriptions often need manual provider registration:
az provider register --namespace Microsoft.ContainerRegistry
az provider register --namespace Microsoft.ContainerService
az provider register --namespace Microsoft.Network
az provider register --namespace Microsoft.Compute
Verify:
az provider list --query "[?registrationState!='Registered']"
2οΈβ£ Create Azure Container Registry
az acr create \
--resource-group $RG \
--name $ACR_NAME \
--sku Standard \
--location $LOCATION
Attach ACR to AKS (important for image pull permissions):
az aks update \
--name $CLUSTER \
--resource-group $RG \
--attach-acr $ACR_NAME
This configures managed identity-based access.
3οΈβ£ Create a Simple Hello App
Create project folder:
mkdir hello-aks && cd hello-aks
app.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end("Hello from AKS + ACR π");
});
server.listen(3000);
package.json
{
"name": "hello-aks",
"version": "1.0.0",
"main": "app.js"
}
Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
4οΈβ£ Build Image in ACR (Cloud Shell Compatible)
Since Azure Cloud Shell doesnβt support Docker daemon, use:
az acr build \
--registry $ACR_NAME \
--image hello-aks:v1 \
.
This:
- Uploads source
- Builds inside ACR
- Stores image securely
Enterprise-friendly approach β
5οΈβ£ Connect kubectl to AKS
az aks get-credentials \
--resource-group $RG \
--name $CLUSTER
Verify:
kubectl get nodes
6οΈβ£ Deploy the Application
Get ACR login server:
ACR_LOGIN_SERVER=$(az acr show \
--name $ACR_NAME \
--query loginServer \
--output tsv)
Create hello-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-aks
spec:
replicas: 2
selector:
matchLabels:
app: hello-aks
template:
metadata:
labels:
app: hello-aks
spec:
containers:
- name: hello-aks
image: <ACR_LOGIN_SERVER>/hello-aks:v1
ports:
- containerPort: 3000
Replace <ACR_LOGIN_SERVER> with actual value.
Deploy:
kubectl apply -f hello-deployment.yaml
Verify:
kubectl get pods
7οΈβ£ Expose the Application
Create hello-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: hello-aks-service
spec:
type: LoadBalancer
selector:
app: hello-aks
ports:
- port: 80
targetPort: 3000
Apply:
kubectl apply -f hello-service.yaml
Wait for public IP:
kubectl get svc hello-aks-service -w
Test:
curl http://<EXTERNAL-IP>
Expected output:
Hello from AKS + ACR π
π― What You Learned
You now understand:
- How AKS pulls images securely from ACR
- How to build containers without local Docker
- How Azure LoadBalancer exposes services
- How managed identity simplifies authentication
- How cloud-native deployments work end-to-end
π§ Real-World Production Enhancements
Next steps for enterprise-grade setup:
- Private ACR with Private Endpoint
- NGINX Ingress Controller
- TLS with cert-manager
- Horizontal Pod Autoscaler
- Azure Key Vault + Workload Identity
- CI/CD with GitHub Actions
- Blue/Green deployments
π Final Architecture
ACR (Private Registry)
β
AKS Deployment (2 replicas)
β
Kubernetes Service (LoadBalancer)
β
Public IP
You just built a real production foundation used by SaaS companies and platform teams.
Top comments (0)