Exposing Non-HTTP Applications
This page covers exposing applications that do not speak HTTP — SSH servers, databases, message brokers, custom TCP protocols. For web-based (HTTP) applications, see Exposing HTTP Applications.
Unlike HTTP applications, non-HTTP applications cannot share the cluster’s web load balancer. Each one needs its own IP address from the cluster’s MetalLB pool — public, or private within the MUNI network (see Private MUNI IP). Public IPv4 addresses are limited, so prefer HTTP whenever possible.
Prerequisite
This guide assumes that your application (Deployment) is already deployed. If you are unsure, refer to the Hello World example.
Throughout this page, YAML fragments are provided. Deploy them using:
kubectl create -f file.yaml -n namespacewhere file.yaml contains the YAML definitions and namespace is the namespace where the application is running.
LoadBalancer Service
Non-HTTP applications are exposed directly with a LoadBalancer Service. Add an external-dns annotation to get a DNS name automatically:
apiVersion: v1
kind: Service
metadata:
name: application-svc
annotations:
external-dns.alpha.kubernetes.io/hostname: application.dyn.cloud.e-infra.cz
spec:
type: LoadBalancer
allocateLoadBalancerNodePorts: false
ports:
- port: 22
targetPort: 2222
selector:
app: application- The
selectormust match the labels of your Pods (spec.template.metadata.labelsin theDeployment). - If the
external-dns.alpha.kubernetes.io/hostnameannotation is withindyn.cloud.e-infra.cz, the DNS record is automatically registered against the load-balancer IP.
Checking the Assigned Public IP
Run:
kubectl get svc -n namespaceThe IP address is displayed in the EXTERNAL-IP column.
Private MUNI IP
To expose the application only within the MUNI network or MUNI VPN, use the following annotation:
metallb.io/address-pool: privmuniPrefer this whenever the application doesn’t need to be reachable from the internet, as it avoids consuming a public IPv4 address.
apiVersion: v1
kind: Service
metadata:
name: application-svc
annotations:
metallb.io/address-pool: privmuni
external-dns.alpha.kubernetes.io/hostname: application.dyn.cloud.e-infra.cz
spec:
type: LoadBalancer
allocateLoadBalancerNodePorts: false
ports:
- port: 22
targetPort: 2222
selector:
app: applicationLoadBalancer Firewall
It is recommended to restrict external access to publicly exposed applications. This can be achieved either at the NetworkPolicy level (note that this works only when externalTrafficPolicy is set to Local) or by using the loadBalancerSourceRanges field in the Service object.
The example below, for a PostgreSQL database, limits access to the MUNI network 147.251.0.0/16 and to a single IP 1.2.3.4/32. loadBalancerSourceRanges expects CIDR notation, so even a single IP must be expressed with a /32 mask. If you serve both IPv4 and IPv6, include source ranges for both families.
apiVersion: v1
kind: Service
metadata:
name: application-svc-rw
annotations:
external-dns.alpha.kubernetes.io/hostname: application.dyn.cloud.e-infra.cz
spec:
type: LoadBalancer
allocateLoadBalancerNodePorts: false
externalTrafficPolicy: Cluster
ports:
- port: 5432
targetPort: 5432
selector:
app: application-rw
loadBalancerSourceRanges:
- 147.251.0.0/16
- 1.2.3.4/32Sharing an IP Address Across Multiple Services
Since IPv4 addresses are a scarce resource, it’s beneficial to share a single IP address across multiple LoadBalancer Services differentiated by port numbers. This can be accomplished by using the metallb.io/allow-shared-ip annotation.
The annotation’s value is a sharing key: all Services with the same key may share the same IP address. Choose a unique key per application to avoid unintended IP sharing between unrelated Services.
apiVersion: v1
kind: Service
metadata:
name: application-svc-rw
annotations:
external-dns.alpha.kubernetes.io/hostname: application.dyn.cloud.e-infra.cz
metallb.io/allow-shared-ip: my-shared-key-for-application
spec:
type: LoadBalancer
allocateLoadBalancerNodePorts: false
externalTrafficPolicy: Cluster
ports:
- port: 5432
targetPort: 5432
selector:
app: application-rwapiVersion: v1
kind: Service
metadata:
name: application-svc-ro
annotations:
external-dns.alpha.kubernetes.io/hostname: application.dyn.cloud.e-infra.cz
metallb.io/allow-shared-ip: my-shared-key-for-application
spec:
type: LoadBalancer
allocateLoadBalancerNodePorts: false
externalTrafficPolicy: Cluster
ports:
- port: 5433
targetPort: 5432
selector:
app: application-roLimitations
MetalLB puts several Services on the same IP address only if all of the following hold:
- They have the same
metallb.io/allow-shared-ipkey. - They use different ports.
- Either all of them use
externalTrafficPolicy: Cluster, or all of them select exactly the same Pods (identical selectors).
The example above uses Cluster because the two Services select different Pods (application-rw and application-ro). With Cluster, the Pods can also run on any nodes, which gives better resilience and load distribution.
