mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 23:47:50 +00:00
Adding EndpointSlice support for kube-proxy ipvs and iptables proxiers
This commit is contained in:
@@ -26,7 +26,7 @@ import (
|
||||
"k8s.io/klog"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
discovery "k8s.io/api/discovery/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/tools/record"
|
||||
@@ -92,6 +92,8 @@ type EndpointChangeTracker struct {
|
||||
items map[types.NamespacedName]*endpointsChange
|
||||
// makeEndpointInfo allows proxier to inject customized information when processing endpoint.
|
||||
makeEndpointInfo makeEndpointFunc
|
||||
// endpointSliceCache holds a simplified version of endpoint slices
|
||||
endpointSliceCache *EndpointSliceCache
|
||||
// isIPv6Mode indicates if change tracker is under IPv6/IPv4 mode. Nil means not applicable.
|
||||
isIPv6Mode *bool
|
||||
recorder record.EventRecorder
|
||||
@@ -101,8 +103,8 @@ type EndpointChangeTracker struct {
|
||||
}
|
||||
|
||||
// NewEndpointChangeTracker initializes an EndpointsChangeMap
|
||||
func NewEndpointChangeTracker(hostname string, makeEndpointInfo makeEndpointFunc, isIPv6Mode *bool, recorder record.EventRecorder) *EndpointChangeTracker {
|
||||
return &EndpointChangeTracker{
|
||||
func NewEndpointChangeTracker(hostname string, makeEndpointInfo makeEndpointFunc, isIPv6Mode *bool, recorder record.EventRecorder, endpointSlicesEnabled bool) *EndpointChangeTracker {
|
||||
ect := &EndpointChangeTracker{
|
||||
hostname: hostname,
|
||||
items: make(map[types.NamespacedName]*endpointsChange),
|
||||
makeEndpointInfo: makeEndpointInfo,
|
||||
@@ -110,6 +112,10 @@ func NewEndpointChangeTracker(hostname string, makeEndpointInfo makeEndpointFunc
|
||||
recorder: recorder,
|
||||
lastChangeTriggerTimes: make(map[types.NamespacedName][]time.Time),
|
||||
}
|
||||
if endpointSlicesEnabled {
|
||||
ect.endpointSliceCache = NewEndpointSliceCache(hostname, isIPv6Mode, recorder, makeEndpointInfo)
|
||||
}
|
||||
return ect
|
||||
}
|
||||
|
||||
// Update updates given service's endpoints change map based on the <previous, current> endpoints pair. It returns true
|
||||
@@ -141,9 +147,9 @@ func (ect *EndpointChangeTracker) Update(previous, current *v1.Endpoints) bool {
|
||||
change.previous = ect.endpointsToEndpointsMap(previous)
|
||||
ect.items[namespacedName] = change
|
||||
}
|
||||
if t := getLastChangeTriggerTime(current); !t.IsZero() {
|
||||
ect.lastChangeTriggerTimes[namespacedName] =
|
||||
append(ect.lastChangeTriggerTimes[namespacedName], t)
|
||||
|
||||
if t := getLastChangeTriggerTime(endpoints.Annotations); !t.IsZero() {
|
||||
ect.lastChangeTriggerTimes[namespacedName] = append(ect.lastChangeTriggerTimes[namespacedName], t)
|
||||
}
|
||||
|
||||
change.current = ect.endpointsToEndpointsMap(current)
|
||||
@@ -163,31 +169,83 @@ func (ect *EndpointChangeTracker) Update(previous, current *v1.Endpoints) bool {
|
||||
return len(ect.items) > 0
|
||||
}
|
||||
|
||||
// getLastChangeTriggerTime returns the time.Time value of the EndpointsLastChangeTriggerTime
|
||||
// annotation stored in the given endpoints object or the "zero" time if the annotation wasn't set
|
||||
// or was set incorrectly.
|
||||
func getLastChangeTriggerTime(endpoints *v1.Endpoints) time.Time {
|
||||
// EndpointSliceUpdate updates given service's endpoints change map based on the <previous, current> endpoints pair.
|
||||
// It returns true if items changed, otherwise return false. Will add/update/delete items of EndpointsChangeMap.
|
||||
// If removeSlice is true, slice will be removed, otherwise it will be added or updated.
|
||||
func (ect *EndpointChangeTracker) EndpointSliceUpdate(endpointSlice *discovery.EndpointSlice, removeSlice bool) bool {
|
||||
// This should never happen
|
||||
if endpointSlice == nil {
|
||||
klog.Error("Nil endpointSlice passed to EndpointSliceUpdate")
|
||||
return false
|
||||
}
|
||||
|
||||
namespacedName, _ := endpointSliceCacheKeys(endpointSlice)
|
||||
|
||||
metrics.EndpointChangesTotal.Inc()
|
||||
|
||||
ect.lock.Lock()
|
||||
defer ect.lock.Unlock()
|
||||
|
||||
change, ok := ect.items[namespacedName]
|
||||
if !ok {
|
||||
change = &endpointsChange{}
|
||||
change.previous = ect.endpointSliceCache.EndpointsMap(namespacedName)
|
||||
ect.items[namespacedName] = change
|
||||
}
|
||||
|
||||
if removeSlice {
|
||||
ect.endpointSliceCache.Delete(endpointSlice)
|
||||
} else {
|
||||
ect.endpointSliceCache.Update(endpointSlice)
|
||||
}
|
||||
|
||||
if t := getLastChangeTriggerTime(endpointSlice.Annotations); !t.IsZero() {
|
||||
ect.lastChangeTriggerTimes[namespacedName] =
|
||||
append(ect.lastChangeTriggerTimes[namespacedName], t)
|
||||
}
|
||||
|
||||
change.current = ect.endpointSliceCache.EndpointsMap(namespacedName)
|
||||
// if change.previous equal to change.current, it means no change
|
||||
if reflect.DeepEqual(change.previous, change.current) {
|
||||
delete(ect.items, namespacedName)
|
||||
// Reset the lastChangeTriggerTimes for this service. Given that the network programming
|
||||
// SLI is defined as the duration between a time of an event and a time when the network was
|
||||
// programmed to incorporate that event, if there are events that happened between two
|
||||
// consecutive syncs and that canceled each other out, e.g. pod A added -> pod A deleted,
|
||||
// there will be no network programming for them and thus no network programming latency metric
|
||||
// should be exported.
|
||||
delete(ect.lastChangeTriggerTimes, namespacedName)
|
||||
}
|
||||
|
||||
metrics.EndpointChangesPending.Set(float64(len(ect.items)))
|
||||
return len(ect.items) > 0
|
||||
}
|
||||
|
||||
// getLastChangeTriggerTime returns the time.Time value of the
|
||||
// EndpointsLastChangeTriggerTime annotation stored in the given endpoints
|
||||
// object or the "zero" time if the annotation wasn't set or was set
|
||||
// incorrectly.
|
||||
func getLastChangeTriggerTime(annotations map[string]string) time.Time {
|
||||
// TODO(#81360): ignore case when Endpoint is deleted.
|
||||
if endpoints == nil {
|
||||
if _, ok := annotations[v1.EndpointsLastChangeTriggerTime]; !ok {
|
||||
// It's possible that the Endpoints object won't have the
|
||||
// EndpointsLastChangeTriggerTime annotation set. In that case return
|
||||
// the 'zero value', which is ignored in the upstream code.
|
||||
return time.Time{}
|
||||
}
|
||||
if _, ok := endpoints.Annotations[v1.EndpointsLastChangeTriggerTime]; !ok {
|
||||
// It's possible that the Endpoints object won't have the EndpointsLastChangeTriggerTime
|
||||
// annotation set. In that case return the 'zero value', which is ignored in the upstream code.
|
||||
return time.Time{}
|
||||
}
|
||||
val, err := time.Parse(time.RFC3339Nano, endpoints.Annotations[v1.EndpointsLastChangeTriggerTime])
|
||||
val, err := time.Parse(time.RFC3339Nano, annotations[v1.EndpointsLastChangeTriggerTime])
|
||||
if err != nil {
|
||||
klog.Warningf("Error while parsing EndpointsLastChangeTriggerTimeAnnotation: '%s'. Error is %v",
|
||||
endpoints.Annotations[v1.EndpointsLastChangeTriggerTime], err)
|
||||
annotations[v1.EndpointsLastChangeTriggerTime], err)
|
||||
// In case of error val = time.Zero, which is ignored in the upstream code.
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// endpointsChange contains all changes to endpoints that happened since proxy rules were synced. For a single object,
|
||||
// changes are accumulated, i.e. previous is state from before applying the changes,
|
||||
// current is state after applying the changes.
|
||||
// endpointsChange contains all changes to endpoints that happened since proxy
|
||||
// rules were synced. For a single object, changes are accumulated, i.e.
|
||||
// previous is state from before applying the changes, current is state after
|
||||
// applying the changes.
|
||||
type endpointsChange struct {
|
||||
previous EndpointsMap
|
||||
current EndpointsMap
|
||||
@@ -240,8 +298,8 @@ func (ect *EndpointChangeTracker) endpointsToEndpointsMap(endpoints *v1.Endpoint
|
||||
}
|
||||
|
||||
endpointsMap := make(EndpointsMap)
|
||||
// We need to build a map of portname -> all ip:ports for that
|
||||
// portname. Explode Endpoints.Subsets[*] into this structure.
|
||||
// We need to build a map of portname -> all ip:ports for that portname.
|
||||
// Explode Endpoints.Subsets[*] into this structure.
|
||||
for i := range endpoints.Subsets {
|
||||
ss := &endpoints.Subsets[i]
|
||||
for i := range ss.Ports {
|
||||
@@ -276,13 +334,8 @@ func (ect *EndpointChangeTracker) endpointsToEndpointsMap(endpoints *v1.Endpoint
|
||||
endpointsMap[svcPortName] = append(endpointsMap[svcPortName], baseEndpointInfo)
|
||||
}
|
||||
}
|
||||
if klog.V(3) {
|
||||
newEPList := []string{}
|
||||
for _, ep := range endpointsMap[svcPortName] {
|
||||
newEPList = append(newEPList, ep.String())
|
||||
}
|
||||
klog.Infof("Setting endpoints for %q to %+v", svcPortName, newEPList)
|
||||
}
|
||||
|
||||
klog.V(3).Infof("Setting endpoints for %q to %+v", svcPortName, formatEndpointsList(endpointsMap[svcPortName]))
|
||||
}
|
||||
}
|
||||
return endpointsMap
|
||||
|
||||
Reference in New Issue
Block a user