Commit 4ac15003 authored by Dom Sekotill's avatar Dom Sekotill
Browse files

Use headless services for port-proxy

Switches port-proxy from requiring NodePort services to headless
ClusterIP services. (i.e. clusterIP == "None"). Nginx can then use DNS
for round-robin load balancing.
parent d440943c
Loading
Loading
Loading
Loading
+13 −10
Original line number Diff line number Diff line
@@ -23,8 +23,10 @@ import jinja2


NGINX_BASE = '/etc/nginx'
HEADLESS_DOCS = 'https://kubernetes.io/docs/concepts/services-networking/service/#headless-services'

IPAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
IPAddress = ipaddress.IPv4Address, ipaddress.IPv6Address
IPAddressTypes = Union[IPAddress]

logger = logging.getLogger(__name__)

@@ -60,7 +62,7 @@ class StreamConfig:
			self.path = pathlib.Path(nginx_base).joinpath(f'streams/{uid}.conf')
		template = engine.get_template('stream.conf')
		with self.path.open('w') as conf:
			conf.writelines(template.stream(**self.vals))
			conf.writelines(template.stream(name=self.name, uuid=uid, **self.vals))

	def disable(self):
		"""
@@ -171,7 +173,7 @@ def main(argv=None):

	def nginx_addr_format(input):
		addr, port = input
		if addr.version == 6:
		if isinstance(addr, IPAddress) and addr.version == 6:
			return f'[{addr}]:{port}'
		else:
			return f'{addr}:{port}'
@@ -278,7 +280,7 @@ def filter_services(stream: TextIO):
			logger.warning("empty line from an open stream")


def node_ports(service: dict, host_addrs: Set[IPAddress]):
def node_ports(service: dict, host_addrs: Set[IPAddressTypes]):
	"""
	Yield port information from services relevant to the current node's host

@@ -293,15 +295,16 @@ def node_ports(service: dict, host_addrs: Set[IPAddress]):
	metadata = service['metadata']
	spec = service['spec']
	name = f"{metadata['name']}.{metadata['namespace']}"
	fqdn = f"{name}.svc.cluster.local"
	uid = metadata['uid']
	logger.info("got a service: %s (%s)", name, uid)

	# Clear old streams for this service
	yield uid, None

	# TODO: On addressing #20, remove NodePort restriction
	if spec.get('type') != 'NodePort':
		logger.debug("rejecting service: not a NodePort")
	# TODO: On addressing #20, remove headless restriction
	if spec.get('clusterIP', 'None') != 'None':
		logger.debug("rejecting service: not headless: %s", HEADLESS_DOCS)
		return
	try:
		ports = spec['ports']
@@ -330,7 +333,7 @@ def node_ports(service: dict, host_addrs: Set[IPAddress]):
				external_port=(addr, port['port']),
				# TODO: Awaiting #20
				# internal_port=(cluster_ip, port['port']),
				internal_port=(addr, port['nodePort']),
				internal_port=(fqdn, port['targetPort']),
				use_proxy_protocol = (
					opts.proxy_all or
					port['name'] in opts.proxy or
@@ -339,7 +342,7 @@ def node_ports(service: dict, host_addrs: Set[IPAddress]):
			)


def get_host_addr() -> Iterable[IPAddress]:
def get_host_addr() -> Iterable[IPAddressTypes]:
	"""
	Yield all IP addresses configured on the host
	"""
@@ -356,7 +359,7 @@ def get_host_addr() -> Iterable[IPAddress]:
					logger.warning("ignoring scoped IPv6 address")


def addr_from_names(names: Iterable[str]) -> Iterable[IPAddress]:
def addr_from_names(names: Iterable[str]) -> Iterable[IPAddressTypes]:
	"""
	Generate IPAddress objects from a list of addresses & names

+1 −0
Original line number Diff line number Diff line
@@ -40,5 +40,6 @@ http {
}

stream {
	resolver 10.96.0.10 valid=10s;
	include {{ nginx_directory }}/streams/*.conf;
}
+5 −1
Original line number Diff line number Diff line
map $remote_addr $backend_{{ uuid|replace("-", "_") }} {
	default {{ internal_port|nginx_addr }};
}

server {
	listen {{ external_port|nginx_addr }} {{ 'udp' if protocol == 'UDP' else '' }};
	proxy_pass {{ internal_port|nginx_addr }};
	proxy_pass $backend_{{ uuid|replace("-", "_") }};
	proxy_protocol {{ 'on' if use_proxy_protocol else 'off' }};
}