Skip to content

Karmada API server unexpectedly assigned nodePort for service #7823

Description

@ADoggg9

What happened

A federated Service with type=NodePort (or any Service whose spec contains spec.ports[*].nodePort) created in the Karmada control plane gets spec.ports[*].nodePort allocated by the Karmada kube-apiserver. Karmada then dispatches the entire spec — including the control-plane-allocated nodePort — to member clusters unchanged. When a member cluster already has that NodePort taken by another Service, the Work apply fails:

Failed to apply all manifests (0/1): Service "test-svc-0" is invalid:
spec.ports[0].nodePort: Invalid value: 32410: provided port is already allocated

Reproduce

Federated Service (control plane):

kind: Service
apiVersion: v1
metadata:
  name: test-svc-0
  namespace: svc-test
spec:
  type: NodePort
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80
  selector:
    app: test-nginx

Member cluster ResourceBinding status:

{
  "aggregatedStatus": [
    {
      "appliedMessage": "Failed to apply all manifests (0/1): Service \"test-svc-0\" is invalid: spec.ports[0].nodePort: Invalid value: 32410: provided port is already allocated",
      "clusterName": "test-cluster-0",
      "health": "Unknown"
    },
    {
      "applied": true,
      "clusterName": "test-cluster-1",
      "health": "Healthy",
    }
  ]
}

A different member cluster applied the same Work fine — proving the spec is valid; the failure is purely a cluster-local port collision.

Root cause

spec.ports[*].nodePort should be allocated by the member-cluster kube-apiserver, not propagated from the control plane. The conflict mechanism: when a member-cluster kube-apiserver receives a create/apply whose Service spec already contains spec.ports[*].nodePort, it honors the requested value and tries to allocate that exact port. If that port is already allocated to another Service in that cluster, the apiserver rejects with provided port is already allocated.

Each member cluster allocates NodePorts from its own --service-node-port-range (default 30000-32767). A port free in the control plane is not guaranteed free in any given member cluster.

removeServiceIrrelevantField (pkg/resourceinterpreter/default/native/prune/prune.go) currently strips only clusterIP / clusterIPs. nodePort is missing.

Proposed fix

Strip spec.ports[*].nodePort in removeServiceIrrelevantField, so the member-cluster kube-apiserver receives a spec without nodePort and allocates one from its own --service-node-port-range — no collision.

Same class of bug as #113 (fixed in #206) for clusterIP / clusterIPs — kube-apiserver-allocated fields should not cross-cluster propagate; the prune function already strips clusterIP/clusterIPs, this issue extends that to nodePort.

Metadata

Metadata

Assignees

Labels

kind/bugCategorizes issue or PR as related to a bug.

Type

No type

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions