Overview #
The following tutorial is based on the previous post “Azure DevOps Pipeline: Build Container Image and push the Image to Azure Container Registry”.
AKS Prerequisites #
Create AKS Cluster #
I’m using the following AKS cluster, for more details, check out my post about AKS Kubernetes clusters.
# Create Kubernetes cluster
az aks create \
    --resource-group container-playground \
    --name aks-cluster-1 \
    --location northeurope \
    --kubernetes-version 1.30.0 \
    --node-count 1 \
    --node-vm-size Standard_DS2_v2 \
    --node-osdisk-size 30
Configure Kubectl #
# Configure Kubectl to use the AKS cluster
az aks get-credentials \
    --resource-group container-playground \
    --name aks-cluster-1
Create a Namespace #
Create a namespace for the deployment:
# Create namespace
kubectl create ns dev
Azure DevOps #
Adopt the Repository #
Overview #
The repository file and folderstructure should look like this:
├── azure-pipelines.yml
├── Dockerfile
├── HTML
│   └── index.html
├── kube-manifests
│   └── 01-deployment-and-loadbalancer-service.yml
└── README.md
GitHub Repository: https://github.com/jueklu/azure-devops-aks
Deployment and LoadBalancer Manifest #
Create a manifest for the deployment and loadbalancer service:
kube-manifests/01-deployment-and-loadbalancer-service.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: container-deployment
  labels:
    app: apache-container
spec:
  replicas: 1
  selector:
    matchLabels:
      app: apache-container
  template:
    metadata:
      labels:
        app: apache-container
    spec:
      containers:
        - name: apache-container
          image: jkwregistry.azurecr.io/azuredevopscontainerexample
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: container-loadbalancer
  labels:
    app: apache-container
spec:
  type: LoadBalancer
  selector:
    app: apache-container
  ports:
    - port: 80
      targetPort: 80
Adopt Deployment Pipeline #
Add a task that publishes the build artifacts:
azure-pipelines.yml
# Docker
# Build and push an image to Azure Container Registry
# https://docs.microsoft.com/azure/devops/pipelines/languages/docker
trigger:
- main
resources:
- repo: self
variables:
  # Container registry service connection established during pipeline creation
  dockerRegistryServiceConnection: '875a58dd-registry-service-connection'
  imageRepository: 'azuredevopscontainerexample'
  containerRegistry: 'jkwregistry.azurecr.io'
  dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile'
  tag: '$(Build.BuildId)'
  # Agent VM image name
  vmImageName: 'ubuntu-latest'
stages:
- stage: Build
  displayName: Build and push stage
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: Docker@2
      displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: $(imageRepository)
        dockerfile: $(dockerfilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        tags: |
                    $(tag)
# Add Publish Build Artifacts Task
    - task: PublishBuildArtifacts@1
      inputs:
        PathtoPublish: '$(System.DefaultWorkingDirectory)/kube-manifests'
        ArtifactName: 'kube-manifests'
        publishLocation: 'Container'
AKS Service Connection #
Create a service connection for the AKS cluster:
- 
Go to: Project Settings > (Pipelines) “Service connections” 
- 
Click “New service connection” 
- 
Select “Kubernetes” (login again) 
 
- 
Select “Authentication method” x “Azure Subscription” 
- 
Select your Azure Subscription 
- 
Select your AKS cluster 
- 
Select the previously created “dev” namespace 
- 
Define a service connection name, like aks-cluster-1 dev
- 
Click “Save” 
 
Organization Settings #
Open the dashboard of your organization: 
https://dev.azure.com/jueklu/
- Go to: “Organization settings”
 
- 
Select (Pipelines) “Settings” 
- 
Disable “Disable creation of classic release pipelines” 
 
Release Pipeline #
Create Release Pipeline #
- 
Go to: “Pipelines” > “Releases” 
- 
Click “New pipeline” 
 
- Select template type “Empty job”
 
- 
Define a stage name like Dev
- 
Click “Add an artifact” 
 
- 
Select source type “Build” 
- 
Select your project and repository 
- 
Click “Add” 
 
- Add a “Continuous deployment trigger”
 
- Set continuois deployment trigger to “Enabled”
 
- 
Select the “Tasks” tab 
- 
Click “Agent job” 
- 
Set “Agent pool” to “Azure Pipelines” 
- 
Set “Agent Specification” to “ubuntu-latest” 
 
- 
Click “+” to add a task 
- 
Select “Deploy to Kubernetes” > “Add” 
 
- 
Define a display name like image pull secret
- 
Set action to “create secret” 
- 
Select service connection type “Kubernetes Service Connection” 
- 
Select your Kubernetes service connection aks-cluster-1 dev
- 
Set “Type of secret” to “dockerRegistry” 
- 
Define a secret name like dev-secret
- 
Select your “Docker registry service connection” jkwregistry
 
- 
Click “+” to add a task 
- 
Select “Deploy to Kubernetes” > “Add” 
 
- 
Define a display name like deploy dev
- 
Set action to “deploy” 
- 
Select service connection type “Kubernetes Service Connection” 
- 
Select your Kubernetes service connection aks-cluster-1 dev
- 
Define namespace dev
- 
Set “Manifests” to $(System.DefaultWorkingDirectory)/_azure-devops-container-example/kube-manifests/01-deployment-and-loadbalancer-service.yml
- 
Set “Containers” to jkwregistry.azurecr.io/azuredevopscontainerexample:$(Build.BuildId)
- 
Set “ImagePullSecrets” to dev-secret
- 
Click “Save” 
 
- Click “OK”
 
Change & Push Repository #
Adopt the index.html file and push it into the repository.
Verify the Release Pipeline #
 
 
Verify the Deployment with Kubectl #
# List pods in "dev" namespace
get pods -n dev
# Shell output:
NAME                                   READY   STATUS    RESTARTS   AGE
container-deployment-c85569b74-g4lqc   1/1     Running   0          31s
# List services in "dev" namespace
kubectl get svc -n dev
# Shell output:
NAME                     TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
container-loadbalancer   LoadBalancer   10.0.114.205   172.205.94.30   80:31470/TCP   2m29s
Test the deployment with curl:
# Curl the loadbalancer external IP
curl 172.205.94.30:80
# Shell output:
<!DOCTYPE html>
<html>
<head>
        <title>jklug.work</title>
</head>
<body>
        <h1>Azure DevOps Example</h1>
        <p>Build Container 2</p>
</body>
Delete Resources #
Delete AKS Cluster #
# Delete AKS cluster
az aks delete \
    --resource-group container-playground \
    --name aks-cluster-1 \
    --yes \
    --no-wait
Delete Azure Container Registry #
# Delete the Azure container registry
az acr delete --name jkwregistry --resource-group container-playground --yes
Delete Resource Group #
# Delete the Azure resource group
az group delete --name container-playground --yes --no-wait
Delete Azure DevOps Project #
- 
Open the project dashboard: https://dev.azure.com/jueklu/Project1 
- 
Select “Project settings” 
- 
Select (General) “Overview” and scroll down to “Delete project” 
- 
Click “Delete” 
Delete Azure DevOps Organization #
- 
Open the organization dashboard: https://dev.azure.com/jueklu 
- 
Select “Organization settings” 
- 
Select (General) “Overview” and scroll down to “Delete organization” 
- 
Click “Delete”