k3s on Raspberry Pi: NodePort Services

17 Dec 2021 20:58 k3s raspberry-pi

Until just now, I didn’t get NodePort services.

The Kubernetes documentation says the following:

NodePort: Exposes the Service on each Node’s IP at a static port (the NodePort). A ClusterIP Service, to which the NodePort Service routes, is automatically created. You’ll be able to contact the NodePort Service, from outside the cluster, by requesting <NodeIP>:<NodePort>.

This page – Kubernetes: ClusterIP, NodePort, or Ingress? When to Use Each – finally made it click:

Creating a NodePort will open that port on every node in your cluster. Kubernetes will automatically route port traffic to the service it’s linked to.

That’s why it insists on reserving a range of port numbers – it needs to find one that’s available on every node in the cluster.

Let’s add a NodePort service to our docker registry:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: docker-registry
  name: docker-registry-np
  namespace: docker-registry
spec:
  selector:
    app: docker-registry
  type: NodePort
  ports:
  - port: 5000
    targetPort: 5000
$ kubectl apply -f service.yml
service/docker-registry unchanged
service/docker-registry-np created
$ kubectl --namespace docker-registry get services
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
docker-registry      ClusterIP   10.43.236.176   <none>        5000/TCP         7d8h
docker-registry-np   NodePort    10.43.191.198   <none>        5000:30721/TCP   39s

What used to confuse me here is that I’ve already got a CLUSTER-IP, now I’ve got another one? I thought this was supposed to be accessible externally, but there’s no EXTERNAL-IP listed. What’s with the weird 5000:30721 thing? Why is it the “wrong” way round?

What’s going on is that you can access port 30721 from outside the cluster to any cluster node, and Kubernetes will transparently route your traffic to the relevant service.

Until now, I’ve been assuming that “Exposes the Service on each Node’s IP” meant each node that the container is running on. I’ve been using describe pod to figure that out first before attempting to talk to my service.

But, no, we can do this:

desktop-pc % curl http://rpi401:30721:/v2/_catalog
{"repositories":[]}

desktop-pc % curl http://rpi405:30721:/v2/_catalog
{"repositories":[]}

desktop-pc % curl http://rpi403:30721:/v2/_catalog
{"repositories":[]}

At this point, I don’t even know which node my docker registry is running on (actually I do, it’s rpi405).

OK, so it’s not a load-balancer, but it’s not bad.