Learn how to use NodePort with multiple replicas in k3s on Raspberry Pi, including deployment and scaling. This guide will help you understand how NodePort services work with multiple replicas.

Using NodePort with Multiple Replicas in k3s on Raspberry Pi

If you’re using a NodePort service with multiple replicas, how does it know which replica to use? Let’s find out by running a simple Node.js server with multiple replicas.

Setting Up the Server

We can find out which node we’re running on by following the instructions at Kubernetes documentation.

server.js

const PORT = process.env.PORT || 8111;

const MY_NODE_NAME = process.env.MY_NODE_NAME;
const MY_POD_NAMESPACE = process.env.MY_POD_NAMESPACE;
const MY_POD_NAME = process.env.MY_POD_NAME;
const MY_POD_IP = process.env.MY_POD_IP;

const http = require('http');
const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');

        const info = `pod \${MY_POD_NAME} on node \${MY_NODE_NAME}\n`;
        res.end(info);
});

server.listen(PORT);
console.log(`Server listening on port \${PORT}`);

Dockerfile

FROM node:17
EXPOSE 8111
COPY server.js .
CMD ["node", "server.js"]

Build and Push the Image

DOCKER_REGISTRY=docker.k3s.differentpla.net
IMAGE_TAG="$(date +%s)"
docker build -t node-server . \
&& docker tag node-server "\${DOCKER_REGISTRY}/node-server:\${IMAGE_TAG}" \
&& docker push "\${DOCKER_REGISTRY}/node-server:\${IMAGE_TAG}"

Deploying the Application

Deployment YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-server
  labels:
    app: node-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: node-server
  template:
    metadata:
      labels:
        app: node-server
        name: node-server
    spec:
      containers:
      - name: node-server
        image: docker.k3s.differentpla.net/node-server:1640867226
        env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP

Service YAML

apiVersion: v1
kind: Service
metadata:
  labels:
    app: node-server
  name: node-server
spec:
  type: NodePort
  ports:
  - port: 8111
    protocol: TCP
    targetPort: 8111
  selector:
    app: node-server

Apply the configurations:

kubectl apply -f deployment.yaml -f svc.yaml

Verifying the Deployment

Checking the Running Node

curl http://rpi401:30184

Example output:

pod node-server-fcb84c684-xngdm on node rpi402

Confirm with:

kubectl get pods --selector=app=node-server -o wide
kubectl get pods --selector=app=node-server -o jsonpath='{.items[*].spec.nodeName}'

Scaling the Deployment

Scale up to 3 replicas:

Updated Deployment YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-server
  labels:
    app: node-server
spec:
  replicas: 3
...

Apply the updated configuration:

kubectl apply -f deployment.yaml
kubectl get pods --selector=app=node-server -o jsonpath='{range .items[*]}{.spec.nodeName}{"\n"}{end}'

Example output:

rpi402
rpi402
rpi401

Accessing the Service

The service is available on any node:

curl http://rpi401:30184

Example output:

pod node-server-fcb84c684-psf64 on node rpi401

To emphasize the service is available on any node:

curl http://rpi404:30184

Example output:

pod node-server-fcb84c684-psf64 on node rpi401

References

By following these steps, you can effectively use NodePort with multiple replicas in k3s on Raspberry Pi, ensuring that your services are accessible and balanced across the nodes.