Hope this might be useful to someone:
# Expanding MicroK8s Service CIDR Without Downtime
## Check Current Usage
```bash
# Get service CIDR from API
CIDR=$(echo ‘{“apiVersion”:“v1”,“kind”:“Service”,“metadata”:{“name”:“tst”},“spec”:{“clusterIP”:“1.1.1.1”,“ports”:[{“port”:443}]}}’ | kubectl apply -f - 2>&1 | sed ‘s/.*valid IPs is //’)
# Count used IPs
USED=$(kubectl get svc --all-namespaces -o jsonpath=‘{.items[*].spec.clusterIP}’ | tr ’ ’ ‘\n’ | grep -v “None” | wc -l)
# Calculate capacity
PREFIX=${CIDR#*/}
TOTAL=$(( (1 << (32 - PREFIX)) - 2 ))
echo “CIDR: $CIDR | Used: $USED / $TOTAL”
```
## Expand to /20
Expands from 254 IPs to 4,094 IPs (16x capacity). Works on live clusters with **zero downtime**.
Kubernetes validates existing service IPs against the new CIDR on startup. Since we’re **expanding** the range (not moving it), all existing IPs remain valid. The `kubernetes` service stays at `.183.1` - no certificate updates needed.
### Procedure (Per Node)
**1. Backup configs**
```bash
NODE=devk8s1-ehv # Change for each node
ssh $NODE “sudo cp /var/snap/microk8s/current/args/cni-env{,.bak}”
ssh $NODE “sudo cp /var/snap/microk8s/current/args/kube-apiserver{,.bak}”
ssh $NODE “sudo cp /var/snap/microk8s/current/args/kube-controller-manager{,.bak}”
```
**2. Update CIDR (change /24 to /20)**
```bash
ssh $NODE “sudo sed -i ‘s|IPv4_SERVICE_CIDR=10.152.183.0/24|IPv4_SERVICE_CIDR=10.152.176.0/20|’ /var/snap/microk8s/current/args/cni-env”
ssh $NODE “sudo sed -i ‘s|service-cluster-ip-range=10.152.183.0/24|service-cluster-ip-range=10.152.176.0/20|’ /var/snap/microk8s/current/args/kube-apiserver”
ssh $NODE “sudo sed -i ‘s|service-cluster-ip-range=10.152.183.0/24|service-cluster-ip-range=10.152.176.0/20|’ /var/snap/microk8s/current/args/kube-controller-manager”
```
**3. Verify changes**
```bash
ssh $NODE “grep -E ‘SERVICE_CIDR|service-cluster-ip-range’ /var/snap/microk8s/current/args/{cni-env,kube-apiserver,kube-controller-manager}”
# All should show: 10.152.176.0/20
```
**4. Restart just kubelite, PODs stay where they are**
```bash
ssh $NODE “sudo snap restart microk8s.daemon-kubelite”
sleep 60
kubectl get nodes # Verify node is Ready
```
**5. Repeat for remaining nodes** (rolling restart keeps cluster available)
### Verify Success
```bash
# Check kubernetes service IP unchanged
kubectl get svc kubernetes -n default -o jsonpath=‘{.spec.clusterIP}’
# Should still be: 10.152.183.1
# Test new allocation works
kubectl create service clusterip test-expansion --tcp=80:80
kubectl get svc test-expansion -o jsonpath=‘{.spec.clusterIP}’
# Should get IP in 10.152.176-191.x range
kubectl delete svc test-expansion
```
### Rollback option
```bash
ssh $NODE “sudo cp /var/snap/microk8s/current/args/cni-env{.bak,}”
ssh $NODE “sudo cp /var/snap/microk8s/current/args/kube-apiserver{.bak,}”
ssh $NODE “sudo cp /var/snap/microk8s/current/args/kube-controller-manager{.bak,}”
ssh $NODE “sudo snap restart microk8s.daemon-kubelite”
```
## Impact
- Zero pod restarts
- Zero service disruptions
- All existing service IPs preserved
- Cluster remains available during rolling restarts
- No certificate updates required
## References
- Kubernetes source: `pkg/registry/core/service/ipallocator/controller/repair.go`
- Tested on: MicroK8s v1.30.14, 6-node cluster