...

Source file src/k8s.io/kubernetes/pkg/proxy/serviceport.go

Documentation: k8s.io/kubernetes/pkg/proxy

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package proxy
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	"k8s.io/klog/v2"
    25  	apiservice "k8s.io/kubernetes/pkg/api/v1/service"
    26  	proxyutil "k8s.io/kubernetes/pkg/proxy/util"
    27  	netutils "k8s.io/utils/net"
    28  )
    29  
    30  // ServicePort is an interface which abstracts information about a service.
    31  type ServicePort interface {
    32  	// String returns service string.  An example format can be: `IP:Port/Protocol`.
    33  	String() string
    34  	// ClusterIP returns service cluster IP in net.IP format.
    35  	ClusterIP() net.IP
    36  	// Port returns service port if present. If return 0 means not present.
    37  	Port() int
    38  	// SessionAffinityType returns service session affinity type
    39  	SessionAffinityType() v1.ServiceAffinity
    40  	// StickyMaxAgeSeconds returns service max connection age
    41  	StickyMaxAgeSeconds() int
    42  	// ExternalIPs returns service ExternalIPs
    43  	ExternalIPs() []net.IP
    44  	// LoadBalancerVIPs returns service LoadBalancerIPs which are VIP mode
    45  	LoadBalancerVIPs() []net.IP
    46  	// Protocol returns service protocol.
    47  	Protocol() v1.Protocol
    48  	// LoadBalancerSourceRanges returns service LoadBalancerSourceRanges if present empty array if not
    49  	LoadBalancerSourceRanges() []*net.IPNet
    50  	// HealthCheckNodePort returns service health check node port if present.  If return 0, it means not present.
    51  	HealthCheckNodePort() int
    52  	// NodePort returns a service Node port if present. If return 0, it means not present.
    53  	NodePort() int
    54  	// ExternalPolicyLocal returns if a service has only node local endpoints for external traffic.
    55  	ExternalPolicyLocal() bool
    56  	// InternalPolicyLocal returns if a service has only node local endpoints for internal traffic.
    57  	InternalPolicyLocal() bool
    58  	// HintsAnnotation returns the value of the v1.DeprecatedAnnotationTopologyAwareHints annotation.
    59  	HintsAnnotation() string
    60  	// ExternallyAccessible returns true if the service port is reachable via something
    61  	// other than ClusterIP (NodePort/ExternalIP/LoadBalancer)
    62  	ExternallyAccessible() bool
    63  	// UsesClusterEndpoints returns true if the service port ever sends traffic to
    64  	// endpoints based on "Cluster" traffic policy
    65  	UsesClusterEndpoints() bool
    66  	// UsesLocalEndpoints returns true if the service port ever sends traffic to
    67  	// endpoints based on "Local" traffic policy
    68  	UsesLocalEndpoints() bool
    69  }
    70  
    71  // BaseServicePortInfo contains base information that defines a service.
    72  // This could be used directly by proxier while processing services,
    73  // or can be used for constructing a more specific ServiceInfo struct
    74  // defined by the proxier if needed.
    75  type BaseServicePortInfo struct {
    76  	clusterIP                net.IP
    77  	port                     int
    78  	protocol                 v1.Protocol
    79  	nodePort                 int
    80  	loadBalancerVIPs         []net.IP
    81  	sessionAffinityType      v1.ServiceAffinity
    82  	stickyMaxAgeSeconds      int
    83  	externalIPs              []net.IP
    84  	loadBalancerSourceRanges []*net.IPNet
    85  	healthCheckNodePort      int
    86  	externalPolicyLocal      bool
    87  	internalPolicyLocal      bool
    88  	hintsAnnotation          string
    89  }
    90  
    91  var _ ServicePort = &BaseServicePortInfo{}
    92  
    93  // String is part of ServicePort interface.
    94  func (bsvcPortInfo *BaseServicePortInfo) String() string {
    95  	return fmt.Sprintf("%s:%d/%s", bsvcPortInfo.clusterIP, bsvcPortInfo.port, bsvcPortInfo.protocol)
    96  }
    97  
    98  // ClusterIP is part of ServicePort interface.
    99  func (bsvcPortInfo *BaseServicePortInfo) ClusterIP() net.IP {
   100  	return bsvcPortInfo.clusterIP
   101  }
   102  
   103  // Port is part of ServicePort interface.
   104  func (bsvcPortInfo *BaseServicePortInfo) Port() int {
   105  	return bsvcPortInfo.port
   106  }
   107  
   108  // SessionAffinityType is part of the ServicePort interface.
   109  func (bsvcPortInfo *BaseServicePortInfo) SessionAffinityType() v1.ServiceAffinity {
   110  	return bsvcPortInfo.sessionAffinityType
   111  }
   112  
   113  // StickyMaxAgeSeconds is part of the ServicePort interface
   114  func (bsvcPortInfo *BaseServicePortInfo) StickyMaxAgeSeconds() int {
   115  	return bsvcPortInfo.stickyMaxAgeSeconds
   116  }
   117  
   118  // Protocol is part of ServicePort interface.
   119  func (bsvcPortInfo *BaseServicePortInfo) Protocol() v1.Protocol {
   120  	return bsvcPortInfo.protocol
   121  }
   122  
   123  // LoadBalancerSourceRanges is part of ServicePort interface
   124  func (bsvcPortInfo *BaseServicePortInfo) LoadBalancerSourceRanges() []*net.IPNet {
   125  	return bsvcPortInfo.loadBalancerSourceRanges
   126  }
   127  
   128  // HealthCheckNodePort is part of ServicePort interface.
   129  func (bsvcPortInfo *BaseServicePortInfo) HealthCheckNodePort() int {
   130  	return bsvcPortInfo.healthCheckNodePort
   131  }
   132  
   133  // NodePort is part of the ServicePort interface.
   134  func (bsvcPortInfo *BaseServicePortInfo) NodePort() int {
   135  	return bsvcPortInfo.nodePort
   136  }
   137  
   138  // ExternalIPs is part of ServicePort interface.
   139  func (bsvcPortInfo *BaseServicePortInfo) ExternalIPs() []net.IP {
   140  	return bsvcPortInfo.externalIPs
   141  }
   142  
   143  // LoadBalancerVIPs is part of ServicePort interface.
   144  func (bsvcPortInfo *BaseServicePortInfo) LoadBalancerVIPs() []net.IP {
   145  	return bsvcPortInfo.loadBalancerVIPs
   146  }
   147  
   148  // ExternalPolicyLocal is part of ServicePort interface.
   149  func (bsvcPortInfo *BaseServicePortInfo) ExternalPolicyLocal() bool {
   150  	return bsvcPortInfo.externalPolicyLocal
   151  }
   152  
   153  // InternalPolicyLocal is part of ServicePort interface
   154  func (bsvcPortInfo *BaseServicePortInfo) InternalPolicyLocal() bool {
   155  	return bsvcPortInfo.internalPolicyLocal
   156  }
   157  
   158  // HintsAnnotation is part of ServicePort interface.
   159  func (bsvcPortInfo *BaseServicePortInfo) HintsAnnotation() string {
   160  	return bsvcPortInfo.hintsAnnotation
   161  }
   162  
   163  // ExternallyAccessible is part of ServicePort interface.
   164  func (bsvcPortInfo *BaseServicePortInfo) ExternallyAccessible() bool {
   165  	return bsvcPortInfo.nodePort != 0 || len(bsvcPortInfo.loadBalancerVIPs) != 0 || len(bsvcPortInfo.externalIPs) != 0
   166  }
   167  
   168  // UsesClusterEndpoints is part of ServicePort interface.
   169  func (bsvcPortInfo *BaseServicePortInfo) UsesClusterEndpoints() bool {
   170  	// The service port uses Cluster endpoints if the internal traffic policy is "Cluster",
   171  	// or if it accepts external traffic at all. (Even if the external traffic policy is
   172  	// "Local", we need Cluster endpoints to implement short circuiting.)
   173  	return !bsvcPortInfo.internalPolicyLocal || bsvcPortInfo.ExternallyAccessible()
   174  }
   175  
   176  // UsesLocalEndpoints is part of ServicePort interface.
   177  func (bsvcPortInfo *BaseServicePortInfo) UsesLocalEndpoints() bool {
   178  	return bsvcPortInfo.internalPolicyLocal || (bsvcPortInfo.externalPolicyLocal && bsvcPortInfo.ExternallyAccessible())
   179  }
   180  
   181  func newBaseServiceInfo(service *v1.Service, ipFamily v1.IPFamily, port *v1.ServicePort) *BaseServicePortInfo {
   182  	externalPolicyLocal := apiservice.ExternalPolicyLocal(service)
   183  	internalPolicyLocal := apiservice.InternalPolicyLocal(service)
   184  
   185  	var stickyMaxAgeSeconds int
   186  	if service.Spec.SessionAffinity == v1.ServiceAffinityClientIP {
   187  		// Kube-apiserver side guarantees SessionAffinityConfig won't be nil when session affinity type is ClientIP
   188  		stickyMaxAgeSeconds = int(*service.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds)
   189  	}
   190  
   191  	clusterIP := proxyutil.GetClusterIPByFamily(ipFamily, service)
   192  	info := &BaseServicePortInfo{
   193  		clusterIP:           netutils.ParseIPSloppy(clusterIP),
   194  		port:                int(port.Port),
   195  		protocol:            port.Protocol,
   196  		nodePort:            int(port.NodePort),
   197  		sessionAffinityType: service.Spec.SessionAffinity,
   198  		stickyMaxAgeSeconds: stickyMaxAgeSeconds,
   199  		externalPolicyLocal: externalPolicyLocal,
   200  		internalPolicyLocal: internalPolicyLocal,
   201  	}
   202  
   203  	// v1.DeprecatedAnnotationTopologyAwareHints has precedence over v1.AnnotationTopologyMode.
   204  	var exists bool
   205  	info.hintsAnnotation, exists = service.Annotations[v1.DeprecatedAnnotationTopologyAwareHints]
   206  	if !exists {
   207  		info.hintsAnnotation = service.Annotations[v1.AnnotationTopologyMode]
   208  	}
   209  
   210  	// filter external ips, source ranges and ingress ips
   211  	// prior to dual stack services, this was considered an error, but with dual stack
   212  	// services, this is actually expected. Hence we downgraded from reporting by events
   213  	// to just log lines with high verbosity
   214  	ipFamilyMap := proxyutil.MapIPsByIPFamily(service.Spec.ExternalIPs)
   215  	info.externalIPs = ipFamilyMap[ipFamily]
   216  
   217  	// Log the IPs not matching the ipFamily
   218  	if ips, ok := ipFamilyMap[proxyutil.OtherIPFamily(ipFamily)]; ok && len(ips) > 0 {
   219  		klog.V(4).InfoS("Service change tracker ignored the following external IPs for given service as they don't match IP Family",
   220  			"ipFamily", ipFamily, "externalIPs", ips, "service", klog.KObj(service))
   221  	}
   222  
   223  	cidrFamilyMap := proxyutil.MapCIDRsByIPFamily(service.Spec.LoadBalancerSourceRanges)
   224  	info.loadBalancerSourceRanges = cidrFamilyMap[ipFamily]
   225  	// Log the CIDRs not matching the ipFamily
   226  	if cidrs, ok := cidrFamilyMap[proxyutil.OtherIPFamily(ipFamily)]; ok && len(cidrs) > 0 {
   227  		klog.V(4).InfoS("Service change tracker ignored the following load balancer source ranges for given Service as they don't match IP Family",
   228  			"ipFamily", ipFamily, "loadBalancerSourceRanges", cidrs, "service", klog.KObj(service))
   229  	}
   230  
   231  	// Obtain Load Balancer Ingress
   232  	var invalidIPs []net.IP
   233  	for _, ing := range service.Status.LoadBalancer.Ingress {
   234  		if ing.IP == "" {
   235  			continue
   236  		}
   237  
   238  		// proxy mode load balancers do not need to track the IPs in the service cache
   239  		// and they can also implement IP family translation, so no need to check if
   240  		// the status ingress.IP and the ClusterIP belong to the same family.
   241  		if !proxyutil.IsVIPMode(ing) {
   242  			klog.V(4).InfoS("Service change tracker ignored the following load balancer ingress IP for given Service as it using Proxy mode",
   243  				"ipFamily", ipFamily, "loadBalancerIngressIP", ing.IP, "service", klog.KObj(service))
   244  			continue
   245  		}
   246  
   247  		// kube-proxy does not implement IP family translation, skip addresses with
   248  		// different IP family
   249  		ip := netutils.ParseIPSloppy(ing.IP) // (already verified as an IP-address)
   250  		if ingFamily := proxyutil.GetIPFamilyFromIP(ip); ingFamily == ipFamily {
   251  			info.loadBalancerVIPs = append(info.loadBalancerVIPs, ip)
   252  		} else {
   253  			invalidIPs = append(invalidIPs, ip)
   254  		}
   255  	}
   256  	if len(invalidIPs) > 0 {
   257  		klog.V(4).InfoS("Service change tracker ignored the following load balancer ingress IPs for given Service as they don't match the IP Family",
   258  			"ipFamily", ipFamily, "loadBalancerIngressIPs", invalidIPs, "service", klog.KObj(service))
   259  	}
   260  
   261  	if apiservice.NeedsHealthCheck(service) {
   262  		p := service.Spec.HealthCheckNodePort
   263  		if p == 0 {
   264  			klog.ErrorS(nil, "Service has no healthcheck nodeport", "service", klog.KObj(service))
   265  		} else {
   266  			info.healthCheckNodePort = int(p)
   267  		}
   268  	}
   269  
   270  	return info
   271  }
   272  

View as plain text