I need to frequently test things in an Azure Kubernetes Services (AKS) cluster. For this I've set up a Terraform pipeline that creates different environments:
- DEV: a very short-lived environment that is created with limited resources. This instance will be destroyed every night automatically for cost-saving reasons.
- TST: a near-production system that is also treated as such. For cost-saving aspects resources for this environment were reserved at Azure for a long timeframe.
- PRD: right now this instance is not used. Production grade apps are deployed currently as Azure Container Instance ACI
Setting up a Kubernetes cluster and managing it with Portainer can be fun. (Photo by Kaique Rocha from Pexels)
- A service principal
- A storage account for terraform state file.
If you need to create a new one:
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/$SUB_ID"Update the variables subscription_id, tenant_id, client_id and client_secret in your local azure.conf
Debugging if you have one already and it's not working. One reason can be to have expired credentials.
az ad sp list --show-mine
az role assignment list --assignee $APP_ID
az ad sp credential reset --name $APP_ID
az login --service-principal --username $APP_ID --password $PASSWORD --tenant $TENANT_ID
az login
az account set --subscription $SUB_ID$ az storage account create \
--name $STORAGE_ACCOUNT \
--resource-group $RESOURCE_GROUP \
--kind StorageV2 \
--sku Standard_LRS \
--https-only true \
--allow-blob-public-access falseIf you have already a storage account, you can check it with:
az account list --output table
az account set --subscription $SUB_ID
az storage account list -g $RESOURCE_GROUP
az storage account show -g $RESOURCE_GROUP -n $STORAGE_ACCOUNTIf you need to create a container:
az storage container create -n tfstate-devopserver --account-name stdefaultkstjj001 --account-key <YourAzureStorageAccountKey>All required credentials are stored in a variable library called k8s-setup and are used by the pipeline. There is no need to set anything up locally. The required variables in the library are:
storage_account_name= the name of the storage account, where the terraform state file is locatedresource_group_name= the name of the resource group the storage account is incontainer_name= "tfstate-k8s"key= "terraform.tfstate"subscription_id= your subscription idtenant_id= your tenant idclient_id= your app idclient_secret= your app password
-
Run the pipeline
k8s-infrastructure-pipeline.yamland wait for it to be completed. This might take around 10 minutes. -
After that retrieve the public IP of your traefik instance and update your DNS:
az login az aks get-credentials --resource-group DEV-rg-k8s-kstjj-001 --name k8s --overwrite-existing az aks get-credentials --resource-group TST-rg-k8s-kstjj-001 --name k8s --overwrite-existing kubectl get service traefik -n ns-admin
Important Note: don't publish any external-facing web UIs until the DNS propagation is successful. Otherwise, Let's Encrypt might block you due to authentication errors.
-
Check also the newly created public IP of your Portainer Agent instance and create or update the endpoint configuration in your Portainer Control Node.
kubectl get service portainer-agent -n portainerAfter that, your cluster is fully operational and can be controlled via Portainer. Apps can be deployed including exposure of HTTPS-enabled web UIs.
If you don't want to make use of pipelines, create an azure.conf file with the following content. Important Note: your azure.conf file must be added to your .gitignore file and should never be committed to a repository. It must contain something like this:
# azure.conf, must be in .gitignore
storage_account_name="$STORAGE_ACCOUNT"
resource_group_name="$RESOURCE_GROUP"
container_name="tfstate-k8s"
key="terraform.tfstate"
subscription_id="$SUB_ID"
tenant_id="$TENANT_ID"
client_id="$APP_ID"
client_secret="$PASSWORD"Then run the different steps of the pipeline manually. Ensure to use the right workspace name which equals the stage you are going to deploy.
terraform init -backend-config=azure.conf
./workspacetest.sh DEV
terraform plan -out out.plan
terraform apply out.planFirst of all check which versions Azure supports in your region. You can also review the AKS Kubernetes Release Calendar. Generally, a new Kubernetes version will be available for preview on Azure a month after it has been released.
az aks get-versions --location westeurope --output tableThe output will tell you what is possible:
KubernetesVersion Upgrades
------------------- ----------------------------------------
1.22.2(preview) None available
1.22.1(preview) 1.22.2(preview)
1.21.2 1.22.1(preview), 1.22.2(preview)
1.21.1 1.21.2, 1.22.1(preview), 1.22.2(preview)
1.20.9 1.21.1, 1.21.2When you found the version you are planning to update your cluster to, update the kubernetes_version variable in the variables.tf file accordingly and deploy a new DEV cluster and test this new version before updating any TST or PRD environment.
Traefik specific resources like middlewares must be created with manifests. You see one example for a IP whitelist in the k8s-apps-03-traefik-middleware-example.yaml file.
You can apply these manifests as an administrator in Portainer.
After that, you can make use of these Middlewares by adding them as an annotation on the resource group level.
Configure the endpoint of your AKS cluster and enable Ingress. Set Ingress class to traefik (this must be the name of the IngressClass in k8s-apps-02-traefik.yaml) and select for Type the value traefik.

Configure the namespace and add these annotations:
traefik.ingress.kubernetes.io/router.tls: true
traefik.ingress.kubernetes.io/router.tls.certresolver: myresolver
traefik.ingress.kubernetes.io/router.entrypoints: websecureIf you want to add middlewares, add a further entry like the following. Ensure to replace {{namespace}} and {{middleware-name}} by the actual names. Be aware that these middlewares are added to all applications in the resource group.
traefik.ingress.kubernetes.io/router.middlewares: {{namespace}}-{{middleware-name}}@kubernetescrdCreate or edit an app and publish a port via an ingress.

