Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using NodePort leaves proxy service externalIPs empty #1463

Closed
tommie opened this issue May 29, 2023 · 4 comments
Closed

Using NodePort leaves proxy service externalIPs empty #1463

tommie opened this issue May 29, 2023 · 4 comments
Labels
kind/bug Something isn't working

Comments

@tommie
Copy link
Contributor

tommie commented May 29, 2023

Description:
I'm trying to run Gateway on bare metal with just public-IP nodes.
I.e. no load balancer or floating IPs. Part of #1035.

When creating a Gateway without spec.addresses and using NodePort for proxy services, I would expect the rendered service to have externalIPs set to the external IPs of the nodes where the pods are currently running. Right now, they are empty, likely because [1] and [2] means they are only set from GatewaySpec.addresses (this was recently added, as a start.) This should makeGatewayStatus.addresses empty, but it's actually populated with all node addresses [3]? This leads to ExternalDNS adding nodes to RRs, that have nothing to do with the Gateway.

Edit: If GatewaySpec.addresses are provided, the IP addresses in GatewayStatus.addresses end up with duplicates, because of [3] and then also adding the Service externalIPs. But until I understand [3], I don't know what that function should look like.

In the other direction, for NodePort, it would make sense if setting GatewaySpec.addresses caused the proxy pods to be scheduled on nodes that match those external IPs. Or rejecting that configuration.

Repro steps:

  1. Create an EnvoyProxy with NodePort.
  2. Create a Gateway without spec.addresses.
  3. Look at the generated Service.

  1. serviceSpec.ExternalIPs = r.infra.Addresses
  2. gwInfraIR.Proxy.Addresses = ipAddr
  3. addresses = nodeAddresses
@tommie tommie added the kind/bug Something isn't working label May 29, 2023
@arkodg
Copy link
Contributor

arkodg commented May 29, 2023

What is happening

  • All the Node Addresses (Internal and External) are being appended to Gateway.Status.Addresses
  • The Gateway.Spec.Addresses is also being appended to Gateway.Status.Addresses

What should happen

  • We cannot tie location of where the Envoy pod is running to the IP that should be used to reach it. Kubernetes allows the Pod to be reachable from any node using iptables magic
  • We currently append Internal IP of the node to Gateway.Status.Addresses if External IP is not present. It is debatable whether this is a good idea or not, and whether we should only limit it to External IP of the node ?
  • We currently always append Gateway.Spec.Addresses to Gateway.Status.Addresses. Maybe this should be an explicit set/override ?

@tommie
Copy link
Contributor Author

tommie commented May 30, 2023

We cannot tie location of where the Envoy pod is running to the IP that should be used to reach it. Kubernetes allows the Pod to be reachable from any node using iptables magic

Doing internal routing doesn't feel very good when I actually know which nodes have Proxy running. Why can't we know the location? (This is mostly about using ExternalDNS to point to the relevant nodes directly.)

@tommie
Copy link
Contributor Author

tommie commented May 30, 2023

As I learn more about k8s port management, here are some notes:

  • When proxier adds IPTables rules, it adds rules for a few different sources:
    • The clusterIP is bound on Service.port
    • The externalIPs are bound on Service.port
    • The status.loadBalancer.ingress.ip are bound on Service.port
    • The Service.nodePort is bound for any IP on any node.
  • Since Service.nodePort is auto-generated (in current EG,) it cannot be used for ingress traffic.
    • That leaves us with externalIPs and status.loadBalancer. For NodePort, that leaves externalIPs.
    • I.e. the Service rendered by EG must have externalIPs.
    • The recent change here simply populates externalIPs from GatewaySpec.addresses, but otherwise leaves it empty.
  • Node-to-node forwarding cannot reasonably work well for Service.port, since it's not unique for the cluster, unlike nodePort. Unless I'm missing some routing magic in K8s.
    • For simple bare-metal clusters, each node has an IP address, and that's all. We have the ability to send requests directly to a proxy Node/Pod, without node-to-node forwarding. Using DNS to store IP address of the nodes currently running the proxy Pods.
  • What I think I'm after is EG defaulting Service.externalIPs to the IP addresses of the nodes where actual proxy Pods are running.
    • As before (and questioned earlier) this could use node external IP if available, and fall back to internal IP. This choice does not disappear; the logic just moves.
    • If GatewaySpec.addresses are provided, it probably means the cluster admin has some specific rules controlled elsewhere, e.g. Pod node affinity, and doing the current simple copying is enough.
    • We could know whether individual GatewaySpec.addresses are node addresses, and force the proxy Pods to match, but it would add yet another node affinity knob, which is probably not warranted.
  • It is possible to build a Gateway-Pod controller that updates GatewaySpec.addresses based on where proxy Pods owned by the Gateway are actually running. It feels a bit backwards to have a controller for an individual spec field, so I'd prefer if EG did this, and we could see GatewaySpec as static.
    • However, this right now creates duplicate addresses in GatewayStatus.addresses, since all nodes are already added.
    • Which also means it's meaningless.

arkodg added a commit to arkodg/gateway that referenced this issue May 30, 2023
Dont append, just set/override the gw.Status.Addresses with the values
from gw.Spec.Addresses (which eventually get set in svc.Spec.ExternalIPs)

Relates to envoyproxy#1463

https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway

Signed-off-by: Arko Dasgupta <arko@tetrate.io>
zirain pushed a commit that referenced this issue May 31, 2023
* only set gw.Spec.Addresses in gw.Status.Addresses

Dont append, just set/override the gw.Status.Addresses with the values
from gw.Spec.Addresses (which eventually get set in svc.Spec.ExternalIPs)

Relates to #1463

https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.Gateway

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* lint

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

---------

Signed-off-by: Arko Dasgupta <arko@tetrate.io>
@arkodg
Copy link
Contributor

arkodg commented Jun 2, 2023

closing this issue since #1465 allows a way for the user to explicitly control the value in gateway.Status.Addressesusinggateway.Spec.Addresses`.
@tommie please feel free to reopen this issue if the issue remains unresolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants