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

Register Endpoint ports for ClusterIP services #133

Merged
merged 1 commit into from
Sep 18, 2019
Merged

Register Endpoint ports for ClusterIP services #133

merged 1 commit into from
Sep 18, 2019

Conversation

lkysow
Copy link
Member

@lkysow lkysow commented Sep 17, 2019

Previously, we were registering the Service's port instead of the
underlying endpoint port (as specified by targetPort).
If the two were different, this would result
in traffic being directed to the wrong port.

Also handles edge cases around ports being overridden via annotations.

Fixes #132

Some background:

  • ClusterIP services can have a targetPort that specifies which port on the Pod that traffic should be routed to. We weren't taking that into account and always using port.

  • We are registering each Endpoint as a service instance. The endpoints are the Pod IPs so obviously we need to use the Pod's port, i.e. targetPort.

  • We support overriding which port we use via an annotation (https://www.consul.io/docs/platform/k8s/service-sync.html#service-ports). This can be set to a port name or a specific number. If set to a number, then we just use that number. If set to a name, we'd like to use that port name from the service:

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
    spec:
      selector:
        app: nginx
      ports:
        - protocol: TCP
          name: myname
          port: 8080
          targetPort: 80 # <<<<<< want to use this

    Unfortunately, targetPort can also be set to the name of the Pod's port:

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
    spec:
      selector:
        app: nginx
      ports:
        - protocol: TCP
          name: myname
          port: 8080
          targetPort: targetname # <<<<<< ugh

    In this case we need to instead use the named port from the Endpoint which will have an actual number since Kubernetes has resolved targetname for us.

@lkysow lkysow requested a review from ishustava September 17, 2019 00:08
@lkysow lkysow force-pushed the pod-sync branch 2 times, most recently from 266c7fa to 7af8881 Compare September 17, 2019 00:14
Copy link
Contributor

@ishustava ishustava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I added a few comments/questions.

if ok {
if v, err := strconv.ParseInt(target, 0, 0); err == nil {
if v, err := strconv.ParseInt(portAnnotation, 0, 0); err == nil {
port = int(v)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the code you changed, but I think it'd be good to change the type of port to uint16 rather than int. It should probably be the case for all port variables.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the datatype on the consul api struct so we can't change it: https://github.com/hashicorp/consul/blob/master/api/agent.go#L71

Sometimes ports in consul get set to -1 to state that they're disable so that's probably why they use int. In the case of a service this wouldn't make any sense though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for clarifying.

@@ -277,52 +277,58 @@ func (t *ServiceResource) generateRegistrations(key string) {
}

// Determine the default port and set port annotations
var endpointPortName string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would overridePortName be a better name for this to match overridePortNumber?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh good catch. At some point while coding this, I was using this variable if Service.targetPort was also a name so I didn't want to use override but I'm not doing this anymore. Will change.

require.Equal("foo", actual[1].Service.Service)
require.Equal("2.3.4.5", actual[1].Service.Address)
require.Equal(8500, actual[1].Service.Port)
require.Equal(4141, actual[1].Service.Port)
require.NotEqual(actual[0].Service.ID, actual[1].Service.ID)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is overriding the service port to something that isn't a registered endpoint with k8s a valid behavior?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's the best thing we can do in this situation. If you're setting the port to a specific number then we should use it, even if it's currently wrong. Maybe their next step is to add the containerPort.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense.

Previously, we were registering the Service's port instead of the
underlying endpoint port (as specified by targetPort).
If the two were different, this would result
in traffic being directed to the wrong port.

Also handles edge cases around ports being overridden via annotations.
@lkysow
Copy link
Member Author

lkysow commented Sep 18, 2019

@ishustava I've addressed your comments, please take another look

@lkysow lkysow merged commit 08777a2 into master Sep 18, 2019
@lkysow lkysow deleted the pod-sync branch September 18, 2019 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Syncing ClusterIP Service from kube uses service port instead of pod port
2 participants