AZ CLI Script to Create a Starter AKS Demo

The following is a bash script with a set of Azure CLI commands to build an cost efficient Azure Kubernetes Environment to host a public facing web application.

This is suitable for beginners or a simple development environment for your project. And demonstrates my recommended setup with both Azure cloud resources and the Open Source Software deployed into the Kubernetes cluster for a public facing web application.

This script consists of Azure resources and OSS software.

  • Azure Container Registry
  • Azure Virtual Network and subnet
  • Azure Kubernetes Service
  • Kubernetes Dashboard UI for cluster resource administration
  • NGINX Ingress Controller to expose public traffic into the cluster and application
  • Cert-Manager for Https
  • AKS Hello World Demo application

The latest script can be found in my Github Repo:
https://github.com/RoyKimYYZ/linux-scripts/blob/master/setup-aks-vnet-acr.sh

To get a glance of a version at the time of this post, you can see:

#!/bin/bash
# setup-aks-vnet-acr.sh
 
az account show -o table
az account list -o table
az account set -s 'Visual Studio Enterprise'
az subscription -n 'Visual Studio Enterprise'

# AKS
rgName='linux-demos' #'aks-solution'
aksName='rkaks-vsent'
location='canadacentral'

# create AKS cluster
####################
az group create -l $location -n $rgName #--subscription $appSubId

# Azure Container Registry
# set this to the name of your Azure Container Registry.  It must be globally unique
acrName=rkimacrVSEnt #name is global
# Note: acr in enterprise rg in Enterprise subscription
az acr create --name $acrName --resource-group $rgName -l $location --sku Basic
acrResourceId=$(az acr show --name $acrName --resource-group $rgName --query "id" --output tsv)
az acr update -n $acrName --admin-enabled true
acr_userName=$(az acr credential show -n $acrName --query="username" -o tsv)
acr_pwd=$(az acr credential show -n $acrName --query="passwords[0].value" -o tsv)
echo $acr_userName $acr_pwd

# create vnet and subnet
az network vnet create -g $rgName -n aksVnet  -l $location --address-prefix 10.1.0.0/24 \
    --subnet-name akssubnet --subnet-prefix 10.1.0.0/25 
subnetId=$(az network vnet subnet show --resource-group $rgName --vnet-name aksVnet --name akssubnet --query id -o tsv)
echo $subnetId

# existing subnets
az network vnet subnet list --resource-group $rgName --vnet-name aksVnet -o tsv
subnetId=$(az network vnet subnet show --resource-group $rgName --vnet-name aksVnet --name akssubnet --query id -o tsv)
# VM SKUS 
az vm list-skus --location $location -o table
# versions
az aks get-versions --location $location --output table

az aks create --resource-group $rgName --name $aksName \
    --kubernetes-version 1.19.1 \
    --location $location \
    --node-vm-size Standard_D2_v3 \
    --vm-set-type VirtualMachineScaleSets \
    --node-osdisk-size 30 \
    --node-count 1 --max-pods 30 \
    --network-plugin azure \
    --vnet-subnet-id $subnetId \
    --load-balancer-sku Basic \
    --generate-ssh-keys \
    --enable-cluster-autoscaler --min-count 1 --max-count 5 \
    --enable-aad \
    --enable-managed-identity \
    --attach-acr $acrResourceId
    #--enable-addons monitoring --workspace-resource-id $logWorkspaceResourceId 

# Side Notes
# Standard_D2_v3 2vCpu 8GB Ram, not premium storage
# Networking config options: --service-cidr 10.2.0.0/24 --dns-service-ip 10.2.0.10 --docker-bridge-address 172.17.0.1/16 \
 

az aks get-credentials -n $aksName -g $rgName

az aks show  -n $aksName -g $rgName

# Kubernetes Dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml

kubectl delete clusterrolebinding kubernetes-dashboard 
kubectl create clusterrolebinding kubernetes-dashboard --clusterrole=cluster-admin --serviceaccount=kube-system:kubernetes-dashboard --insecure-skip-tls-verify 
kubectl create clusterrolebinding serviceaccounts-cluster-admin \
  --clusterrole=cluster-admin --group=system:serviceaccounts

kubectl get secrets -n kube-system | grep dashboard-token 
kubectl get secrets -n kube-system -o json
TOKEN=$(kubectl get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='default')].data.token}"|base64 --decode)
echo $TOKEN # | xclip --selection clipboard -o
curl http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#/login

kubectl get pods -n kubernetes-dashboard -o jsonpath | grep kubernetes-dashboard.*
kubectl port-forward $(kubectl get pods -n kubernetes-dashboard -o name | grep kubernetes) 8443:8443

# Create a namespace for your ingress resources
kubectl create namespace ingress-basic

# Add the official stable repo
helm repo update
helm repo add stable https://kubernetes-charts.storage.googleapis.com/

# Use Helm to deploy an NGINX ingress controller
helm install nginx stable/nginx-ingress \
    --namespace ingress-basic \
    --set controller.replicaCount=2 \
    --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \
    --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux

kubectl get service -l app=nginx-ingress --namespace ingress-basic
kubectl get service nginx-nginx-ingress-controller --namespace ingress-basic

# Public IP address of your ingress controller
# Look up in resource group in Azure Portal
publicIP='<PUBLIC IP>'

## Add an A record to your DNS zone ##

# Set A record to GoDaddy DNS 
# OR
# Name to associate with public IP address
DNSNAME="demo-aks-ingress"
# Get the resource-id of the public ip
PUBLICIPID=$(az network public-ip list --query "[?ipAddress!=null]|[?contains(ipAddress, '$IP')].[id]" --output tsv)
# Update public ip address with DNS name
az network public-ip update --ids $PUBLICIPID --dns-name $DNSNAME
# Display the FQDN
az network public-ip show --ids $PUBLICIPID --query "[dnsSettings.fqdn]" --output tsv

## Install cert-manager

# Install the CustomResourceDefinition resources separately
kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.13/deploy/manifests/00-crds.yaml

# Label the ingress-basic namespace to disable resource validation
kubectl label namespace ingress-basic cert-manager.io/disable-validation=true

# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io

# Update your local Helm chart repository cache
helm repo update

# Install the cert-manager Helm chart
helm install \
  cert-manager \
  --namespace ingress-basic \
  --version v0.13.0 \
  jetstack/cert-manager


echo "apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: abc@outlook.com
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
        ingress:
          class: nginx" | kubectl apply -f -

# Demo application
helm repo add azure-samples https://azure-samples.github.io/helm-charts/
akshelloworldnamespace=aks-helloworld
kubectl create namespace $akshelloworldnamespace
helm install aks-helloworld azure-samples/aks-helloworld --namespace $akshelloworldnamespace 
helm install aks-helloworld-two azure-samples/aks-helloworld \
    --namespace $akshelloworldnamespace \
    --set title="AKS Ingress Demo" \
    --set serviceName="aks-helloworld-two"


AppDnsName=akshelloworld.rkim.ca #Your DNS url for your app.

echo "apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-world-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
    - $AppDnsName
    secretName: tls-secret
  rules:
  - host: $AppDnsName
    http:
      paths:
      - backend:
          serviceName: aks-helloworld
          servicePort: 80
        path: /
      - backend:
          serviceName: aks-helloworld-two
          servicePort: 80
        path: /hello-world-two(/|$)(.*)
---
# apiVersion: extensions/v1beta1
# kind: Ingress
# metadata:
#   name: hello-world-ingress-static
#   annotations:
#     kubernetes.io/ingress.class: nginx
#     nginx.ingress.kubernetes.io/rewrite-target: /static/$2
#     cert-manager.io/cluster-issuer: letsencrypt
# spec:
#   tls:
#   - hosts:
#     - $AppDnsName
#     secretName: tls-secret
#   rules:
#   - host: $AppDnsName
#     http:
#       paths:
#       - backend:
#           serviceName: aks-helloworld
#           servicePort: 80
#         path: /static(/|$)(.*) " | kubectl apply --namespace $akshelloworldnamespace -f -

---

kubectl apply -f akshelloworld-hpa.yaml -n $akshelloworldnamespace #need hpa in same namespace as deployment or else <unknown> target


# Verify certificate
kubectl get certificate --namespace ingress-basic
kubectl get certificate --namespace $akshelloworldnamespace 
kubectl describe certificate tls-secret --namespace $akshelloworldnamespace 

kubectl get ingress -n ingress-basic
kubectl get ingress -n $akshelloworldnamespace 
kubectl get ingress -n $akshelloworldnamespace 
kubectl describe ingress -n $akshelloworldnamespace 
kubectl describe ingress hello-world-ingress -n $akshelloworldnamespace 
kubectl describe ingress hello-world-ingress-static -n $akshelloworldnamespace 

kubectl get pods -n ingress-basic
# kubectl logs -n ingress-basic nginx-nginx-ingress-controller-74bf9bd9f5-tzs7k
# kubectl logs -n ingress-basic nginx-nginx-ingress-controller-74bf9bd9f5-4vcc9

Hope this script helps on your journey of learning and applying Azure and Kubernetes for your projects.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s