Top 5 Things an Azure Developer Needs to Know: Kubernetes Applications

Posted on

All modern software is cloud software, and it’s more than likely that it runs on Kubernetes. Developers are faced with the challenge of deploy applications composed of many microservices. And each microservice adds to the complexity of the deployment.

This article reviews the different methods for deploying applications on Azure Kubernetes Service (AKS).

A quick review on application deployment

If you’re not familiar with deploying applications on Kubernetes, these articles cover the basic and advance cases:

Setting up the AKS sandbox

We’ll set up an AKS sandbox composed of an AKS cluster and Azure Container Registry (ACR) for this article. Make sure you have the Azure CLI installed, and you’re logged into Azure, e.g., az login.

Create a container registry

We’ll create a resource group first, followed by the registry.

$ az group create --resource-group aks-group --location westus2
$ az acr create --resource-group aks-group   --name pulumipusContainerRegistry --sku Basic

When the registry is created, the Azure CLI returns JSON with metadata about the registry. Under the id key, you’ll find the URL to the login server.

...
  "id": "/subscriptions/fceb55a3-db15-4f3a-bffa-81271e7b7676/resourceGroups/myResourceGroup/providers/Microsoft.ContainerRegistry/registries/azContainerRegistryPulumi",
  "identity": null,
  "location": "eastus",
  "loginServer": "pulumipuscontainerregistry.azurecr.io",
  "name": "azContainerRegistryPulumi",
...

At this point, you can push and pull container images from your registry. If you want to try out the registry and push an image, you’ll need Docker installed. This example pulls an image from Docker Hub and tags it with the URL to the Azure container registry. We then log into the registry and push the image. To verify the image, we use the Azure CLI to list the images in the registry.

$ docker pull hello-world
$ docker tag hello-world pulumipuscontainerregistry.azurecr.io/hello-world:v1
$ az acr login --name pulumipusContainerRegistry
$ docker push  pulumipuscontainerregistry.azurecr.io/hello-world:v1
$ az acr repository list --name pulumipusContainerRegistry
[
  "hello-world"
]

Create an AKS cluster

The previous article this series demonstrated how to create an AKS cluster with the Azure portal and code to illustrate creating an AKS cluster. For this example, we’ll use the CLI. In the example below, the first line creates a basic cluster, and the second line retrieves the cluster credentials that let us connect to it.

$ az aks create --resource-group aks-group --name aks-cluster --node-count 3 --generate-ssh-keys -s Standard_B2ms --disable-rbac
$ az aks get-credentials -g aks-group -n aks-cluster

Deploy an NGINX container on AKS with Helm

Helm is an application package manager for Kubernetes. Follow the directions on helm.sh to install Helm. Helm can install packages from repositories such as ArtifactHUB, Bitnami, and the Azure Marketplace.

First, we’ll add the Azure Marketplace repository to helm and update it. Next, we install the NGINX helm chart to our AKS cluster.

$ helm repo add azure-marketplace https://marketplace.azurecr.io/helm/v1/repo
$ helm repo update
$ helm install my-release azure-marketplace/nginx
NAME: my-release
LAST DEPLOYED: Mon Aug 16 13:58:26 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.

To find the URL for our NGINX deployment, use kubectl to get the external IP address.

$ kubectl get service
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes         ClusterIP      10.0.0.1       <none>        443/TCP        3h21m
my-release-nginx   LoadBalancer   10.0.212.197   20.72.78.59   80:31251/TCP   175m

Deploy a custom container

In the previous section, we used a Helm chart to deploy an application from a repository. But what if we want to deploy our custom application. In this example, we’ll build our NGINX container, push it to our registry, and deploy it on the AKS cluster.

Create our application container

For this example, you will need Docker. We will replace the default NGINX page with our welcome page. Copy the sample below and save it as index.html.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AKS NGINX</title>
</head>
<body>
  <h2>Hello from Azure AKS</h2>
</body>
</html>

Next, we’ll make a Dockerfile to build our custom NGINX container. Copy and save the following to a file called Dockerfile.

FROM nginx:latest
COPY ./index.html /usr/share/nginx/html/index.html

Build and tag the image with this command and push it to the repository we created earlier.

$ docker build -t pulumipuscontainerregistry.azurecr.io/my-nginx:v1
$ az acr login --name pulumipusContainerRegistry
$ docker push pulumipuscontainerregistry.azurecr.io/my-nginx:v1

Create a deployment manifest

In the previous example, we deployed NGINX with Helm. Manifests are another way to deploy applications. A manifest contains a deployment that defines pods that run a containerized application. It also contains services that route traffic to pods.

Copy and save the example below to a file named manifest.yml.

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: my-nginx
  name: my-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-nginx
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: my-nginx
    spec:
      containers:
      - image: pulumipuscontainerregistry.azurecr.io/my-nginx:v1
        name: my-nginx
        imagePullPolicy: Always
        resources: {}
        ports:
          - containerPort: 80
status: {}

---

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    app: my-nginx
  type: LoadBalancer

Deploy the application to AKS

We’ll use kubectl to deploy the application with the manifest. But before we can do this, we need to attach the ACR pulumipuscontainerregistry to the AKS cluster, aks-cluster. Once the registry is added to the cluster configuration, we can deploy our application with kubectl.

$ az aks update --resource-group aks-group --name aks-cluster --attach-acr pulumipuscontainerregistry
$ kubectl create -f manifest.yml

Use kubectl get service to get the load balancer’s external IP as we did with the Helm example.

Deploy with Pulumi

The previous article in this series showed how to deploy AKS with code. Starting with the Python example from Github and add to it to create a complete solution

Create a container registry and custom container

First, we create the image registry to hold our custom NGINX container. Put the index.html and the Dockerfile files in a directory called app. The app directory is the context Docker uses to build an image. Note that the Docker provider builds and pushes the image to our repository.

# Create a private ACR registry.
rg = azure.core.ResourceGroup('azure-native-py-aks')
registry = azure.containerservice.Registry('pulumipuscontainerrepository',
    resource_group_name=rg.name,
    admin_enabled=True,
    sku='Basic'

# Get registry info (creds and endpoint).
image_name = "pulumipuscontainerrepository/my-nginx"
registry_info = docker.ImageRegistry(
    server=registry.login_server,
    username=registry.admin_username,
    password=registry.admin_password
)

import pulumi_docker as docker

# Build and publish the container image.
image = docker.Image('my-image',
    build='app',
    image_name=image_name,
    registry=registry_info,
)

Deploy the container

The code below creates a Deployment and a LoadBalancer Service similar to the manifest we used earlier. The script exports the IP address of the LoadBalancer at the end of the script.


# Create a NGINX Deployment
app_labels = { 'app': 'my-nginx' }
app_dep = k8s.apps.v1.Deployment('my-aks-deployment',
    spec={
        'selector': { 'matchLabels': app_labels },
        'replicas': 3,
        'template': {
            'metadata': { 'labels': app_labels },
            'spec': {
                'containers': [{
                    'name': 'pulumipuscontainerrepository/my-nginx',
                    'image': 'my-nginx;,
                    'ports': [
                        {
                            'name': 'http',
                            'containerPort': 80
                        }
                    ]
                }],
            },
        },
    },
)

# Create a Service
app_svc = k8s.core.v1.Service('my-aks-service',
    metadata={ 'labels': app_labels },
    spec={
        'type': 'LoadBalancer',
        'ports': [
            {
                'port': 80,
                'targetPort': 80,
                'protocol': 'TCP'
            }
        ],
        'selector': app_labels,
    }
)
pulumi.export('application IP', app_svc.status.apply(lambda status: status.loadbalancer.ingress[0].ip))

This example provides a sketch of the process to create an AKS cluster and deploy a custom container. For a fully worked example, check out the Azure Kubernetes Service (AKS) - Hello World! tutorial.

Summary

There are many ways to deploy applications on AKS, including ConfigMaps and Kustomize which were not discussed. We’ve covered deploying with Helm, manifests, and infrastructure as code. This article covers the basics of application deployment using Deployments and Services. To review, Deployments are declarative updates to a Pod such as specifying a container to run, and Services route traffic to Pods either within the cluster or permitting ingress with a LoadBalancer. We should note that templating methods such as Helm or manifests require file management, whereas using cloud engineering practices enables managing both infrastructure and application deployment in a versioned and repeatable way.

The following article will wrap up the series. We’ll examine how to implement DevOps practices with Azure.