Listener

A Listener object represents a single exposed (possibly) load-balanced service that clients can connect to. It can be thought of as the Stackable Data Platform equivalent of a Kubernetes Service.[1]

The mechanism for the service is controlled by the ListenerClass. This way, a single Listener definition can be reused in different clusters, expressing the same intent regardless of the Kubernetes distribution or cloud provider’s limitations.

Listeners only direct traffic to Pods that also mount them as a volume. The volume allows the operator to pin the Pod to a specific Node, and provides an API for workloads to retrieve their external address.

Address API

The CRD-based API is intended for external clients that need to retrieve the address. The workload can retrieve its own address(es) by using the downwards API.

A Listener writes back all addresses that it can be reached on to Listener.status.ingressAddresses, which can then be used to connect to the service (generate discovery information). Compared to Kubernetes' Services, this list is provided regardless of the type of the backing Service.

Ports may be remapped from the Service definition. Never assume that the exposed port on an address will match your declared port. Instead, read the port numbers from .ports.{portname}. Otherwise, it will break when using NodePort services.

Per-replica listeners

A Listener volume can also specify a ListenerClass rather than a Listener, in which case a Listener object is created automatically for each volume.

These volumes, in turn, can automatically be created for each replica using either:

  • StatefulSet’s volumeClaimTemplates (for long-lived listeners that will be kept across replica restarts and upgrades), or

  • Pod’s volumes[].ephemeral (for temporary listeners that are deleted when their corresponding Pod is deleted)

Pinning

When mounting a Listener volume, it will be "pinned" to that node if the ListenerClass uses a strategy that depends on the node that the workload is running on.

Keep in mind that this will only work correctly when using long-lived volumes (such as via StatefulSet’s volumeClaimTemplates). Ephemeral volumes will be "reset" for every pod that is created, even if they refer to a long-lived Listener object.

Reference

apiVersion: listeners.stackable.tech/v1alpha1
kind: Listener
metadata:
  name: my-listener
spec:
  className: external-unstable
  ports:
  - name: http
    port: 9864
    protocol: TCP
  extraPodSelectorLabels:
    foo: bar
  publishNotReadyAddresses: true
status:
  ingressAddresses:
  - address: 172.18.0.3
    addressType: IP
    ports:
      http: 32222
  nodePorts:
    http: 32222
  serviceName: my-listener
spec.className

The name of the ListenerClass to use.

spec.ports

The ports exposed from the backing Pods.

spec.ports.name

The name of the port.

spec.ports.port

The number of the port. This must match the port number exposed by the container.

spec.ports.protocol

The IP protocol (TCP/UDP/SCTP). Defaults to TCP.

spec.extraPodSelectorLabels

Traffic will only be forwarded to Pods that apply these labels. This field exists for exceptional cases, where Pods sometimes want to stop receiving traffic based on some dynamic condition. Normal target selection should use Listener volumes instead. (Volumes are still required when using extraPodSelectorLabels.)

spec.publishNotReadyAddresses

If false, traffic will only be directed to Pods that are Ready. If true, traffic will be directed to any running Pod. Defaults to true.

status.ingressAddresses

A list of all addresses that the Listener can be reached on. See Address API.

status.ingressAddresses.address

The hostname or IP address of this Listener.

status.ingressAddresses.addressType

IP if address is an IP address, Hostname if it is a hostname.

status.ingressAddresses.ports.{portName}

The exposed port number for a given port name (as defined in .spec.ports). Note that this may be different than the port specified in .spec.ports.port`.

status.nodePorts.{portName}

For internal use only. You probably want to use .status.ingressAddresses instead. If the ListenerClass is configured to use NodePort then this is the port number that each port is accessible on on its respective Node.

status.serviceName

The name of the Kubernetes Service object backing this Listener.


1. It is actually implemented using them, but don’t rely on that.