Change:
<!-- Thanks for filing an issue! Before hitting the button, please answer these questions.--Is this a BUG REPORT or FEATURE REQUEST? (choose one): BUG REPORT <!-- If this is a BUG REPORT, please: Fill in as much of the template below as you can. If you leave out information, we can't help you as well. If this is a FEATURE REQUEST, please: Describe in detail the feature/behavior/change you'd like to see. In both cases, be ready for followup questions, and please respond in a timely manner. If we can't reproduce a bug or think a feature already exists, we might close your issue. If we're wrong, PLEASE feel free to reopen it and explain why. --Kubernetes version (use ): Environment: Cloud provider or hardware configuration: AWS m4.2xlarge OS (e.g. from /etc/os-release): Ubuntu 16.04 Kernel (e.g. ): 4.4.0-36-generic Install tools: kubeadm Others: 1 master and 1 minion What happened: This states that if using kube-proxy will only proxy requests to local endpoints, never forwarding traffic to other nodes and thereby preserving the original source IP address. However when I annotate my NodePort type service with this annotation I see the following error in the logs of kube-proxy of the node where the only pod of the service is running: After this message the service stops working outside of the cluster on the specified NodePort. What you expected to happen: I expected to see that only the with the pod on it serves traffic. How to reproduce it (as minimally and precisely as possible): Anything else we need to know:
Sorry, I have found the issue, I had a setup where the none of the node names were the same as the hostname of the kube-proxy pod and this caused the issue, where none of the Pods running on a hosts were recognised as local pods. I have fixed this issue by manually setting the hostname with in kube-proxy. However I still insist that this error message is misleading and shouldn't be printed in case of NodePort type services, but only for just LoadBalancers.
I met the same problem in the aws cloud providers, by adding - the hostname-override to solve this problem. good

Discussion:
package service <del> import ( "strconv" "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" ) </del> const ( // AnnotationLoadBalancerSourceRangesKey is the key of the annotation on a service to set allowed ingress ranges on their LoadBalancers //

// BetaAnnotationExternalTraffic is the beta version of AlphaAnnotationExternalTraffic. BetaAnnotationExternalTraffic = "service.beta.kubernetes.io/external-traffic" ) <del> // NeedsHealthCheck Check service for health check annotations func NeedsHealthCheck(service *api.Service) bool { // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationExternalTraffic, BetaAnnotationExternalTraffic} { if l, ok := service.Annotations[annotation]; ok { if l == AnnotationValueExternalTrafficLocal { return true } else if l == AnnotationValueExternalTrafficGlobal { return false } else { glog.Errorf("Invalid value for annotation %v: %v", annotation, l) } } } return false } // GetServiceHealthCheckNodePort Return health check node port annotation for service, if one exists func GetServiceHealthCheckNodePort(service *api.Service) int32 { if !NeedsHealthCheck(service) { return 0 } // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationHealthCheckNodePort, BetaAnnotationHealthCheckNodePort} { if l, ok := service.Annotations[annotation]; ok { p, err := strconv.Atoi(l) if err != nil { glog.Errorf("Failed to parse annotation %v: %v", annotation, err) continue } return int32(p) } } return 0 } </del>

import ( "fmt" <ins> "strconv" </ins> "strings" "k8s.io/kubernetes/pkg/api" netsets "k8s.io/kubernetes/pkg/util/net/sets" <ins> "github.com/golang/glog" </ins> ) const (

} return ipnets, nil } <ins> // RequestsOnlyLocalTraffic checks if service requests OnlyLocal traffic. func RequestsOnlyLocalTraffic(service *api.Service) bool { if service.Spec.Type != api.ServiceTypeLoadBalancer && service.Spec.Type != api.ServiceTypeNodePort { return false } // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationExternalTraffic, BetaAnnotationExternalTraffic} { if l, ok := service.Annotations[annotation]; ok { switch l { case AnnotationValueExternalTrafficLocal: return true case AnnotationValueExternalTrafficGlobal: return false default: glog.Errorf("Invalid value for annotation %v: %v", annotation, l) } } } return false } // NeedsHealthCheck Check if service needs health check. func NeedsHealthCheck(service *api.Service) bool { if service.Spec.Type != api.ServiceTypeLoadBalancer { return false } return RequestsOnlyLocalTraffic(service) } // GetServiceHealthCheckNodePort Return health check node port annotation for service, if one exists func GetServiceHealthCheckNodePort(service *api.Service) int32 { if !NeedsHealthCheck(service) { return 0 } // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationHealthCheckNodePort, BetaAnnotationHealthCheckNodePort} { if l, ok := service.Annotations[annotation]; ok { p, err := strconv.Atoi(l) if err != nil { glog.Errorf("Failed to parse annotation %v: %v", annotation, err) continue } return int32(p) } } return 0 } </ins>

package service <del> import ( "strconv" "github.com/golang/glog" "k8s.io/kubernetes/pkg/api/v1" ) </del> const ( // AnnotationLoadBalancerSourceRangesKey is the key of the annotation on a service to set allowed ingress ranges on their LoadBalancers //

// BetaAnnotationExternalTraffic is the beta version of AlphaAnnotationExternalTraffic. BetaAnnotationExternalTraffic = "service.beta.kubernetes.io/external-traffic" ) <del> // NeedsHealthCheck Check service for health check annotations func NeedsHealthCheck(service *v1.Service) bool { // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationExternalTraffic, BetaAnnotationExternalTraffic} { if l, ok := service.Annotations[annotation]; ok { if l == AnnotationValueExternalTrafficLocal { return true } else if l == AnnotationValueExternalTrafficGlobal { return false } else { glog.Errorf("Invalid value for annotation %v: %v", annotation, l) } } } return false } // GetServiceHealthCheckNodePort Return health check node port annotation for service, if one exists func GetServiceHealthCheckNodePort(service *v1.Service) int32 { if !NeedsHealthCheck(service) { return 0 } // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationHealthCheckNodePort, BetaAnnotationHealthCheckNodePort} { if l, ok := service.Annotations[annotation]; ok { p, err := strconv.Atoi(l) if err != nil { glog.Errorf("Failed to parse annotation %v: %v", annotation, err) continue } return int32(p) } } return 0 } // GetServiceHealthCheckPathPort Return the path and nodePort programmed into the Cloud LB Health Check func GetServiceHealthCheckPathPort(service *v1.Service) (string, int32) { if !NeedsHealthCheck(service) { return "", 0 } port := GetServiceHealthCheckNodePort(service) if port == 0 { return "", 0 } return "/healthz", port } </del>

import ( "fmt" <ins> "strconv" </ins> "strings" "k8s.io/kubernetes/pkg/api/v1" netsets "k8s.io/kubernetes/pkg/util/net/sets" <ins> "github.com/golang/glog" </ins> ) const (

} return ipnets, nil } <ins> // RequestsOnlyLocalTraffic checks if service requests OnlyLocal traffic. func RequestsOnlyLocalTraffic(service *v1.Service) bool { if service.Spec.Type != v1.ServiceTypeLoadBalancer && service.Spec.Type != v1.ServiceTypeNodePort { return false } // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationExternalTraffic, BetaAnnotationExternalTraffic} { if l, ok := service.Annotations[annotation]; ok { switch l { case AnnotationValueExternalTrafficLocal: return true case AnnotationValueExternalTrafficGlobal: return false default: glog.Errorf("Invalid value for annotation %v: %v", annotation, l) } } } return false } // NeedsHealthCheck Check if service needs health check. func NeedsHealthCheck(service *v1.Service) bool { if service.Spec.Type != v1.ServiceTypeLoadBalancer { return false } return RequestsOnlyLocalTraffic(service) } // GetServiceHealthCheckNodePort Return health check node port annotation for service, if one exists func GetServiceHealthCheckNodePort(service *v1.Service) int32 { if !NeedsHealthCheck(service) { return 0 } // First check the alpha annotation and then the beta. This is so existing // Services continue to work till the user decides to transition to beta. // If they transition to beta, there's no way to go back to alpha without // rolling back the cluster. for _, annotation := range []string{AlphaAnnotationHealthCheckNodePort, BetaAnnotationHealthCheckNodePort} { if l, ok := service.Annotations[annotation]; ok { p, err := strconv.Atoi(l) if err != nil { glog.Errorf("Failed to parse annotation %v: %v", annotation, err) continue } return int32(p) } } return 0 } // GetServiceHealthCheckPathPort Return the path and nodePort programmed into the Cloud LB Health Check func GetServiceHealthCheckPathPort(service *v1.Service) (string, int32) { if !NeedsHealthCheck(service) { return "", 0 } port := GetServiceHealthCheckNodePort(service) if port == 0 { return "", 0 } return "/healthz", port } </ins>

func newServiceInfo(serviceName proxy.ServicePortName, port *api.ServicePort, service *api.Service) *serviceInfo { onlyNodeLocalEndpoints := false if utilfeature.DefaultFeatureGate.Enabled(features.ExternalTrafficLocalOnly) && <del> (service.Spec.Type == api.ServiceTypeLoadBalancer || service.Spec.Type == api.ServiceTypeNodePort) && apiservice.NeedsHealthCheck(service) { </del> <ins> apiservice.RequestsOnlyLocalTraffic(service) { </ins> onlyNodeLocalEndpoints = true } info := &serviceInfo{

} func shouldCheckOrAssignHealthCheckNodePort(service *api.Service) bool { <del> if service.Spec.Type == api.ServiceTypeLoadBalancer { // True if Service-type == LoadBalancer AND annotation AnnotationExternalTraffic present return (utilfeature.DefaultFeatureGate.Enabled(features.ExternalTrafficLocalOnly) && apiservice.NeedsHealthCheck(service)) } glog.V(4).Infof("Service type: %v does not need health check node port", service.Spec.Type) return false </del> <ins> return (utilfeature.DefaultFeatureGate.Enabled(features.ExternalTrafficLocalOnly) && apiservice.NeedsHealthCheck(service)) </ins> } // Loop through the service ports list, find one with the same port number and