Kubernetes workshop for developers.
Windows
winget install -e --id Microsoft.AzureCLIMacOS
brew update && brew install azure-cliLinux
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bashThere are step-by-step installation instructions if you prefer not to run the script from Microsoft
We will be using a tenant with ID: fdf88c80-36e9-45ee-a0b2-d7eb687e39eb
az login --tenant <Tenant_id>az aks install-cliaz aks get-credentials --name workshop-cluster --resource-group k8s-workshopWindows
winget install k9sMacOS
brew install derailed/k9s/k9sLinux
Use package manager of choice or build from source: install k9s
In Kubernetes, namespaces provide a mechanism for isolating groups of resources within a single cluster. Names of resources need to be unique within a namespace, but not across namespaces.
- Update the file
manifest/namespace.yamlwith a unique name for your namespace, for instance your slack username - Deploy the namespace by running:
kubectl apply -f namespace.yaml
You will now build an image and deploy it to the Azure Container Registry (ACR). All containers must have a unique name within the ACR.
During this workshop you should choose a unique prefix so all your images gets a uniqe name, for instance your full name. Only use lowercase letters. Use this prefix instead of <uri-prefix> in the commands below.
- Open
aspnetapp\aspnetapp\Program.csand update line 24 with a unique path - Navigate to
aspnetappand build the Dockerfile:
az acr build --image <uri-prefix>/aspnet:v1 --registry workshopacrsqr2klsnuxgxa --file Dockerfile .- Update namespace to your own
- In
Poddefinition atspec.containers[0].imageupdate to the correct uri for your image - In
Ingressdefinition atspec.rules[0].http.paths[0].pathset the base path chosen inProgram.cs
- Run:
kubectl apply -f task-1.yaml- Find the IP address using k9s:
:ingress - Navigate to
ip-address/subdirectoryin the browser- The subdirectory is the path from
Program.cs
- The subdirectory is the path from
You will now deploy a Svelte front end, which has nothing to do with the app you deployed in task 1.
- Navigate to
frontend\svelte.config.jsand update line 16 to a path where you will host the frontend.- This path cannot be the same path as the one you set in
aspnetapp.
- This path cannot be the same path as the one you set in
- Build the frontend image
az acr build --image <uri-prefix>/<name>:<tag> --registry workshopacrsqr2klsnuxgxa --file Dockerfile .- namespace
- subdirectory (base path)
- image prefix and name
kubectl apply -f frontend.yaml- Navigate to
databaseand build and push the Dockerfile - After updating all blanked out fields with appropriate values you can deploy
database.yaml - Check the pod with k9s. You should find the pod in a crash loop back off
- Check the
templatesfolder to see an example of how one adds environment variables to a deployment - Add these variables:
POSTGRES_PASSWORD:ඞඞඞPOSTGRES_DB: todoPG_HOST: postgres - Re-deploy and observe that the pod now starts
In this step we will improve the deployment and get closer to best practices. The two big problems with this deployment are that data is being stored in the pod's local storage, which means that if the pod is deleted all the data is lost. Obviously not ideal for a database.
The other large issue is that the 'secret' password is in clear text in the yaml file which is committed to version control.
Finally the environment variables are defined directly on the deployment meaning we have to redeploy each time we want to update the config, and if multiple service share some config it has to be duplicated in all services.
We will fix the last problem first.
- Create a file where you can define your config map. Theres a template to get you started in the template folder
- Remove the env section from the database deployment and add the same keys to the config map
- Deploy the config map
- Add an envFrom section to your database deployment, see templates for help
- Redeploy the database
- Add the same envFrom section to your frontend and it should now be able to connect to the database
To externalize the storage we need to ask kubernetes for a storage volume using a Persistent Volume Claim. Then we need to configure our database to mount the volume and store data there.
- Add a
PersistentVolumeClaimtodatabase.yamlSee the template file for help - Add the volumes section
- Add the volumeMounts section
- Update the config map with this entry:
PGDATA: /var/lib/postgresql/data/pgdata - Redploy the resources you have changed
Now you should be able to add todos from the frontend, delete the database with Ctrl + k in k9s, and see the same todos once the database restarts.
Default Kubernetes policy is to allow all traffic. This is not in line with the principle of least privilege. Therefore a good practice is to deploy a network policy which disallows all traffic, and explicitly allows only desired connections.
- Create and deploy a deny all network policy. See the template file
deny-all-policy.yamlfor help - Check the frontend to see that it has lost connection to the database
- Deploy a network policy which only allows the required communication. See the template file
- Check the frontend again to see that communication is re-established
To use secrets from azure key vault the cluster uses RBAC to connected to a managed identity to authenticate with azure. In kubernetes this identity is interacted with through a service account. First create your secrets in the azure key vault. The password and username for postgres are good candidates. As you share the key vault with all the workshop participants make sure your secret names are unique in the vault.
- Create your secrets through the azure portal
- Deploy a service account based on the template
- Open the k8s-identity in the Azure Portal
- Under Settings -> Federated credentials add a new credential for the service account you just deployed
- Copy the one you see there. The Issuer is the same for your new credential. The scope should point to your service account
- Deploy a secret store based on the template. This establishes a connection to the key vault
- Deploy an external secret based on the template. This creates kubernetes secrets based on the key vault secret and keeps these in sync
- Remove the secrets from the config map and re deploy it.
- Get the secret values from the kubernetes secrets. See
deployment-with-secrets.yamlfor help - If you changed the secret values you will need to delete the old database. To do that you need to delete the Persistent Volume. In k9s: Navigate to persistent volumes
:pvdelete the volumeCtrl+dNavigate to persistent volume claims:pvcdelete the claimCtrl+dNavigate to pods:podelete the databaseCtrl+k
Once the database restarts it should now be using the new secrets safely stored in Azure Key Vault. Note that by default anyone with access to the cluster can read the secret. Try yourself! In k9s navigate to secrets :secret decode the secret by hitting x. Remember that secrets in the cluster are simply base64 encoded by default