How do I get more control over the NodePorts my Service assigns to my Deployment?

8/15/2019

I have a Deployment with 5 replicas. All have ssh and telnet. They should not be load balanced. I would like each to select from a predictable list of 5.

Here is my Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  selector:
    matchLabels:
      app: myapp
  replicas: 5
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:1.0
        ports:
        - name: ssh
          protocol: TCP
          containerPort: 22
        - name: telnet
          protocol: TCP
          containerPort: 23

This is my Service with invalid nodePort values for illustrative purposes.

apiVersion: v1
kind: Service
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  type: NodePort
  ports:
  - name: ssh
    port: 22
    nodePort: [30022, 30122, 30222, 30322, 30422, 30522]
  - name: telnet
    port: 23
    nodePort: [30023, 30123, 30223, 30323, 30423, 30523]

I am hoping to be able to accomplish 2 things:

  1. Each pod replica instance will only get an ssh port from [30022, 30122, 30222, 30322, 30422, 30522] and a telnet port from [30023, 30123, 30223, 30323, 30423, 30523]
  2. A pod replica instance that gets an ssh port of 30022 also gets the telnet port 30023. A pod replica instance that gets an ssh port of 30122 gets a telnet port of 30123 and so on.

Thank you!

-- Geoff
kubernetes
kubernetes-deployment
kubernetes-service

2 Answers

8/16/2019

Consider that the concept of "Replica" means that is going to be the same as the others; a clone.

Based on your question, you need to have unique objects, based on a template, so neither Deployment nor StatefulSet could help as these rely on a ReplicaSet.

As for the StatefulSet labels approach, I'd rather say that is more like a many-to-one relationship, meaning that the whole Service is going to be linked to a particular pod. This means that all the ports mapped in the NodePort service are going to serve an specific pod in the set. If my understanding is correct, you rather want a one-to-one relationship between exposed ports and pods.

Since services act like a load balancer, routing incoming requests to the exact same target (port) on each backend (replica from a deployment), the kind of uniquess that you need might be best approached having each separately, as Deployment or StatefulSet with their corresponding NodePort service and mapping your ports according to your need for each case.

This approach of course has a high operational overhead but can cope with the level of uniqueness that your use case requires.

-- yyyyahir
Source: StackOverflow

8/20/2019

You can use a StatefulSet instead of a Deployment:

Like a Deployment, a StatefulSet manages Pods that are based on an identical container spec. Unlike a Deployment, a StatefulSet maintains a sticky identity for each of their Pods. These pods are created from the same spec, but are not interchangeable: each has a persistent identifier that it maintains across any rescheduling.

The particularly useful feature of StatefulSets is that you will get a unique label predictably generated for each pod:

When the StatefulSet controller creates a Pod, it adds a label, statefulset.kubernetes.io/pod-name, that is set to the name of the Pod. This label allows you to attach a Service to a specific Pod in the StatefulSet. [source]

Then you would create five distinct Services, one for each pod, of the following form:

apiVersion: v1
kind: Service
metadata:
  name: myapp-${n}
  labels: { ... } # whatever you want
spec:
  type: NodePort
  selector: 
    statefulset.kubernetes.io/pod-name: myapp-${n} # assuming you keep the name
                                                   # "myapp" and just switch kind
                                                   # from Deployment to StatefulSet
  ports:
  - name: ssh
    port: 22
    nodePort: 30${n}22
  - name: telnet
    port: 23
    nodePort: 30${n}23

replacing ${n} with 0 through 4.

-- Amit Kumar Gupta
Source: StackOverflow