Day 56 of DevOps, Day 6 of Azure. Both tasks today were about what a thing is actually attached to.
A Kubernetes Service is not attached to a Deployment at all; it matches labels, and nothing else. And an Azure subnet is not attached to an availability zone, which undoes a habit the AWS track kept reinforcing.
One Kubernetes task, one Azure task. Deploy nginx with three replicas behind a NodePort Service, then create a virtual network with a subnet. The tasks come from the KodeKloud Engineer platform.
Generate, then edit
kubectl create deploy nginx-deployment --image=nginx:latest --replicas=3 --dry-run=client -o yaml > deployment.yaml
Generating the manifest saves typing, and it gets one thing wrong for this task. kubectl names the container after the image: its source takes the last part of the image name and strips the tag, so nginx:latest becomes a container called nginx. The task wanted nginx-container, so the file needed one edit before kubectl apply.
It is the same trap as Day 48 from the other direction. kubectl run names the container after the pod. kubectl create deployment names it after the image. Neither knows what your task calls it.
The generator also sets one label, app: nginx-deployment, and uses it in three places: the Deployment's own labels, its selector, and the pod template. That label is what the Service relies on next.
The Service never mentions the Deployment
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx-deployment
ports:
- port: 80
targetPort: 80
nodePort: 30011
app: nginx-deployment looks like a reference to the Deployment. It is not. It is a label value, and the Service selects any pod in its namespace carrying it, whoever created that pod. A Deployment with a different name, whose pods carry the same label, would be matched just the same. Change the label in the pod template, and the Service quietly has nowhere to send traffic.
The quickest way to see what a Service has actually matched:
kubectl get endpointslices -l kubernetes.io/service-name=nginx-service
The control plane creates EndpointSlices for any Service with a selector, labelled with the Service's name. An empty list means the selector matches nothing.
Three ports, three jobs
| Field | What it is |
|---|---|
port |
The Service's own port |
targetPort |
The port on the pod that traffic goes to. Defaults to the same value as port
|
nodePort |
The port opened on the nodes |
The Kubernetes docs describe nodePort plainly: "Each node proxies that port (the same port number on every Node) into your Service." The range defaults to 30000 to 32767. Ask for a specific port, and the control plane either allocates it or rejects the request.
The detail I did not know: that range is split in two to avoid collisions. By default, 30000 to 30085 is a static band and 30086 to 32767 is dynamic, and Kubernetes assigns ports from the upper band first, only using the lower band once the upper one runs out. The task's 30011 sits in the static band, which is where a hand-picked port is least likely to clash with one Kubernetes picked for somebody else.
An Azure subnet has no zone
The Azure task was a VNet, nautilus-vnet, with a subnet, nautilus-subnet, and one range for both: 10.0.0.0/16.
az network vnet create --resource-group $RG --name nautilus-vnet \
--location eastus --address-prefixes 10.0.0.0/16 \
--subnet-name nautilus-subnet --subnet-prefixes 10.0.0.0/16
The task named one range for two resources, so I gave it to both. Azure allows a subnet that fills the whole VNet, and it satisfies either reading. It would be a poor choice anywhere real, because it leaves no room for a second subnet, and Microsoft's rule for changing a subnet's range is strict: you can change it only if no resources are deployed in it. My notes said Azure had been adding in-place resizing. I could not find that documented, so the working rule is to empty the subnet first.
The bigger lesson was about zones.
On AWS, a subnet lives in exactly one availability zone. The AWS docs: "Each subnet must reside entirely within one Availability Zone and cannot span zones." That is why several tasks in the AWS track needed subnets in two or more zones: the RDS subnet group on Day 31, the load balancer on Day 36, the EKS cluster on Day 43. On AWS, multi-AZ means multi-subnet.
Azure does it the other way round. Microsoft: "Virtual networks and subnets span all availability zones in a region." The zone is chosen per resource instead, with --zone 1 on a VM, and Microsoft spells out the consequence: for a zonal VM, "you don't have to take into consideration the virtual network when selecting the availability zone".
So a single Azure subnet can hold a zone-redundant deployment, which a single AWS subnet never can. Azure subnets end up separating things by function or security boundary, not by physical location, and the AWS habit of one subnet per zone per tier just produces subnets nobody needs.
What it is attached to
A Service attaches to a label, not to the Deployment that happens to set it. An Azure subnet attaches to a region, not a zone, and the zone moves down to the individual resource. Both are small facts, and both change how you design. They are also easy to get wrong when you carry the model over from somewhere else.
So here is the Day 56 question. Which habit did you bring from the first platform you learned that does not quite fit the one you use now?
Day 56 down. Forty-four to go.
Top comments (0)