Conversation
| name="http", port=port, target_port=port, protocol="TCP" | ||
| ) | ||
|
|
||
| service_spec = V1ServiceSpec( |
There was a problem hiding this comment.
super premature but this coudl be a great stepping stone to support service NLBs in paasta. According to https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/guide/service/nlb/ the only additions we would need are:
- in the spec:
typeshould be settable toLoadBalancer- a new directive
LoadBalancerClasswould need to be set toservice.k8s.aws/nlb
there is a third requirement which is to set an annotation of service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip but the current annotation support added in this PR would be enough
an alternative would otherwise to have a top level yelpsoa-config directive enable_nlb: True taht would set all of this in one go
There was a problem hiding this comment.
I really like this idea, and it sounds like a small change to support it. I will wait to see what other reviewers think before making any changes.
Rob-Johnson
left a comment
There was a problem hiding this comment.
this is really nice, thanks @wjhoward!
I think in the long term we might want both, but remember that right now, 99% of clients don't connect directly to a service/instance, but a smartstack namespace. there are cases where a user might want to talk to a specific service/instance, but we'll need to add support for creating services per namespace to serve most interactions.
I've written a few notes about that in the internal design doc I wrote. But this is a great start, thanks!
| port = k8s_service_config.get("port", 8888) # Default to the PaaSTA port | ||
| annotations = k8s_service_config.get("annotations", {}) | ||
| annotations[paasta_prefixed("managed")] = "true" | ||
| headless = k8s_service_config.get("headless", True) |
There was a problem hiding this comment.
is there ever a situation where we wouldn't want this to be true? I don't think we'd ever want a client to be connecting to a paasta service/instance using a k8s cluster ip - we'd only ever want to be using a loadbalancer/cloudmap/something else. I'd probably just remove this config option
There was a problem hiding this comment.
Good point, I agree, I think we can remove this option for now. Thanks!
|
Thanks @Rob-Johnson , agreed - this is catering to a niche use case, specifically external DNS integration:
Where the
Do you mean external DNS support and the NLB use case @itsgc mentioned, or namespace services?
Yeah the intention here is specific to the external DNS use case, hence why I marked service config as opt in, are you thinking about expanding k8s Services to support namespace ingress also? |
even for external DNS, It'd be better if the DNS address was for a smartstack namespace than a specific paasta service/instance. given we're only using this for trivial redirects atm, we can get away with it because there is a 1:1 mapping from service/instance to namespace. but we can't guarantee that'll always be true.
No, I mean having both a service that uses 'paasta_service' and 'paasta_instance' as a label selector (so goes to a specific instance), and one that uses a selector for registrations.
|
| if k8s_service_config is None: | ||
| return None | ||
|
|
||
| port = k8s_service_config.get("port", 8888) # Default to the PaaSTA port |
There was a problem hiding this comment.
nit: could probably default to self.get_container_port() to handle the couple of services that don't set this to DEFAULT_CONTAINER_PORT (otherwise, probably worth replacing the hardcoded 8888 here with
|
|
||
| def get_kubernetes_service_config(self) -> Optional[KubernetesServiceConfigDict]: | ||
| """Returns the k8s_service config block, or None if not configured.""" | ||
| return self.config_dict.get("k8s_service") |
There was a problem hiding this comment.
note: we'll also need to add k8s_service to https://github.com/Yelp/paasta/blob/cb5eca340f12e3d63807f4ce35bcccad0711abe2/paasta_tools/cli/schemas/eks_schema.json
|
|
||
| def test_sync_service_creates_service_when_configured(): | ||
| """Test sync_service creates a Service when k8s_service is configured.""" | ||
| mock_client = mock.MagicMock() |
There was a problem hiding this comment.
i was gonna say that we should be using a mock that's autospec'd on the kube client...but it seems like this is a pre-existing pattern already in this file so nevermind :p
|
|
||
| # Validate against the expected spec structure | ||
| # V1Service validation | ||
| assert isinstance(result, V1Service) |
There was a problem hiding this comment.
nit: mypy should already validate this - so we can probably drop the isinstance asserts from these tests
| assert result.api_version == "v1" | ||
| assert result.kind == "Service" | ||
|
|
||
| # V1ObjectMeta | ||
| assert isinstance(result.metadata, V1ObjectMeta) | ||
| assert result.metadata.name == self.deployment.get_sanitised_deployment_name() | ||
| assert result.metadata.namespace == self.deployment.get_namespace() | ||
| # Annotations | ||
| assert ( | ||
| result.metadata.annotations["external-dns.alpha.kubernetes.io/hostname"] | ||
| == "test.example.com" | ||
| ) | ||
| assert result.metadata.annotations["paasta.yelp.com/managed"] == "true" | ||
| # Labels | ||
| assert result.metadata.labels["paasta.yelp.com/service"] == "kurupt" | ||
| assert result.metadata.labels["paasta.yelp.com/instance"] == "fm" | ||
| assert result.metadata.labels["paasta.yelp.com/managed"] == "true" | ||
|
|
||
| # V1ServiceSpec | ||
| assert isinstance(result.spec, V1ServiceSpec) | ||
| assert ( | ||
| result.spec.cluster_ip == "None" | ||
| ) # K8s API expects string "None" for headless | ||
|
|
||
| # V1ServicePort | ||
| assert len(result.spec.ports) == 1 | ||
| assert isinstance(result.spec.ports[0], V1ServicePort) | ||
| assert result.spec.ports[0].name == "http" | ||
| assert result.spec.ports[0].port == 8888 | ||
| assert result.spec.ports[0].target_port == 8888 | ||
| assert result.spec.ports[0].protocol == "TCP" | ||
|
|
||
| # Spec selector | ||
| assert isinstance(result.spec.selector, dict) | ||
| assert result.spec.selector == { | ||
| "paasta.yelp.com/service": self.deployment.get_service(), | ||
| "paasta.yelp.com/instance": self.deployment.get_instance(), | ||
| } |
There was a problem hiding this comment.
should we construct an expected_service and compare to that rather than comparing attribute-by-attribute manually?
Ticket: HACK-4795
Add minimal Kubernetes Service creation support to PaaSTA.
This adds opt in Service creation via a new
k8s_serviceconfig block, primarily for headless Services.Changes:
Current design decisions
k8s_service: {}is valid and would create a headless Service with default port 8888Example config: