Merge pull request #91706 from sbangari/remoteendpointrefcount

Fixing refcounting of remote endpoints used across services
This commit is contained in:
Kubernetes Prow Robot 2020-06-08 21:43:34 -07:00 committed by GitHub
commit 86e14157d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 11 deletions

View File

@ -158,7 +158,7 @@ type endpointsInfo struct {
isLocal bool isLocal bool
macAddress string macAddress string
hnsID string hnsID string
refCount uint16 refCount *uint16
providerAddress string providerAddress string
hns HostNetworkService hns HostNetworkService
} }
@ -180,7 +180,7 @@ func newEndpointInfo(ip string, port uint16, isLocal bool, hns HostNetworkServic
port: port, port: port,
isLocal: isLocal, isLocal: isLocal,
macAddress: conjureMac("02-11", net.ParseIP(ip)), macAddress: conjureMac("02-11", net.ParseIP(ip)),
refCount: 0, refCount: new(uint16),
hnsID: "", hnsID: "",
hns: hns, hns: hns,
} }
@ -201,11 +201,14 @@ func newSourceVIP(hns HostNetworkService, network string, ip string, mac string,
func (ep *endpointsInfo) Cleanup() { func (ep *endpointsInfo) Cleanup() {
Log(ep, "Endpoint Cleanup", 3) Log(ep, "Endpoint Cleanup", 3)
ep.refCount-- if ep.refCount != nil {
*ep.refCount--
}
// Remove the remote hns endpoint, if no service is referring it // Remove the remote hns endpoint, if no service is referring it
// Never delete a Local Endpoint. Local Endpoints are already created by other entities. // Never delete a Local Endpoint. Local Endpoints are already created by other entities.
// Remove only remote endpoints created by this service // Remove only remote endpoints created by this service
if ep.refCount <= 0 && !ep.isLocal { if (ep.refCount == nil || *ep.refCount <= 0) && !ep.isLocal {
klog.V(4).Infof("Removing endpoints for %v, since no one is referencing it", ep) klog.V(4).Infof("Removing endpoints for %v, since no one is referencing it", ep)
err := ep.hns.deleteEndpoint(ep.hnsID) err := ep.hns.deleteEndpoint(ep.hnsID)
if err == nil { if err == nil {
@ -216,6 +219,15 @@ func (ep *endpointsInfo) Cleanup() {
} }
} }
func (refCountMap endPointsReferenceCountMap) getRefCount(hnsID string) *uint16 {
refCount, exists := refCountMap[hnsID]
if !exists {
refCountMap[hnsID] = new(uint16)
refCount = refCountMap[hnsID]
}
return refCount
}
// returns a new serviceInfo struct // returns a new serviceInfo struct
func newServiceInfo(svcPortName proxy.ServicePortName, port *v1.ServicePort, service *v1.Service, hns HostNetworkService) *serviceInfo { func newServiceInfo(svcPortName proxy.ServicePortName, port *v1.ServicePort, service *v1.Service, hns HostNetworkService) *serviceInfo {
onlyNodeLocalEndpoints := false onlyNodeLocalEndpoints := false
@ -311,6 +323,7 @@ type updateServiceMapResult struct {
} }
type proxyServiceMap map[proxy.ServicePortName]*serviceInfo type proxyServiceMap map[proxy.ServicePortName]*serviceInfo
type proxyEndpointsMap map[proxy.ServicePortName][]*endpointsInfo type proxyEndpointsMap map[proxy.ServicePortName][]*endpointsInfo
type endPointsReferenceCountMap map[string]*uint16
func newEndpointsChangeMap(hostname string) endpointsChangeMap { func newEndpointsChangeMap(hostname string) endpointsChangeMap {
return endpointsChangeMap{ return endpointsChangeMap{
@ -465,6 +478,7 @@ type Proxier struct {
serviceMap proxyServiceMap serviceMap proxyServiceMap
endpointsMap proxyEndpointsMap endpointsMap proxyEndpointsMap
portsMap map[localPort]closeable portsMap map[localPort]closeable
endPointsRefCount endPointsReferenceCountMap
// endpointsSynced and servicesSynced are set to true when corresponding // endpointsSynced and servicesSynced are set to true when corresponding
// objects are synced after startup. This is used to avoid updating hns policies // objects are synced after startup. This is used to avoid updating hns policies
// with some partial data after kube-proxy restart. // with some partial data after kube-proxy restart.
@ -638,6 +652,7 @@ func NewProxier(
serviceChanges: newServiceChangeMap(), serviceChanges: newServiceChangeMap(),
endpointsMap: make(proxyEndpointsMap), endpointsMap: make(proxyEndpointsMap),
endpointsChanges: newEndpointsChangeMap(hostname), endpointsChanges: newEndpointsChangeMap(hostname),
endPointsRefCount: make(endPointsReferenceCountMap),
masqueradeAll: masqueradeAll, masqueradeAll: masqueradeAll,
masqueradeMark: masqueradeMark, masqueradeMark: masqueradeMark,
clusterCIDR: clusterCIDR, clusterCIDR: clusterCIDR,
@ -1103,7 +1118,8 @@ func (proxier *Proxier) syncProxyRules() {
continue continue
} }
newHnsEndpoint.refCount++ newHnsEndpoint.refCount = proxier.endPointsRefCount.getRefCount(newHnsEndpoint.hnsID)
*newHnsEndpoint.refCount++
svcInfo.remoteEndpoint = newHnsEndpoint svcInfo.remoteEndpoint = newHnsEndpoint
} }
} }
@ -1205,9 +1221,14 @@ func (proxier *Proxier) syncProxyRules() {
hnsEndpoints = append(hnsEndpoints, *newHnsEndpoint) hnsEndpoints = append(hnsEndpoints, *newHnsEndpoint)
if newHnsEndpoint.isLocal { if newHnsEndpoint.isLocal {
hnsLocalEndpoints = append(hnsLocalEndpoints, *newHnsEndpoint) hnsLocalEndpoints = append(hnsLocalEndpoints, *newHnsEndpoint)
} else {
// We only share the refCounts for remote endpoints
ep.refCount = proxier.endPointsRefCount.getRefCount(newHnsEndpoint.hnsID)
} }
ep.hnsID = newHnsEndpoint.hnsID ep.hnsID = newHnsEndpoint.hnsID
ep.refCount++ *ep.refCount++
Log(ep, "Endpoint resource found", 3) Log(ep, "Endpoint resource found", 3)
} }
@ -1346,6 +1367,12 @@ func (proxier *Proxier) syncProxyRules() {
klog.V(5).Infof("Pending delete stale service IP %s connections", svcIP) klog.V(5).Infof("Pending delete stale service IP %s connections", svcIP)
} }
// remove stale endpoint refcount entries
for hnsID, referenceCount := range proxier.endPointsRefCount {
if *referenceCount <= 0 {
delete(proxier.endPointsRefCount, hnsID)
}
}
} }
type endpointServicePair struct { type endpointServicePair struct {

View File

@ -70,7 +70,7 @@ func (hns fakeHNS) getEndpointByIpAddress(ip string, networkName string) (*endpo
if ipNet.Contains(net.ParseIP(ip)) { if ipNet.Contains(net.ParseIP(ip)) {
return &endpointsInfo{ return &endpointsInfo{
ip: ip, ip: ip,
isLocal: true, isLocal: false,
macAddress: macAddress, macAddress: macAddress,
hnsID: guid, hnsID: guid,
hns: hns, hns: hns,
@ -102,6 +102,7 @@ func (hns fakeHNS) deleteLoadBalancer(hnsID string) error {
func NewFakeProxier(syncPeriod time.Duration, minSyncPeriod time.Duration, clusterCIDR string, hostname string, nodeIP net.IP, networkType string) *Proxier { func NewFakeProxier(syncPeriod time.Duration, minSyncPeriod time.Duration, clusterCIDR string, hostname string, nodeIP net.IP, networkType string) *Proxier {
sourceVip := "192.168.1.2" sourceVip := "192.168.1.2"
hnsNetworkInfo := &hnsNetworkInfo{ hnsNetworkInfo := &hnsNetworkInfo{
id: strings.ToUpper(guid),
name: "TestNetwork", name: "TestNetwork",
networkType: networkType, networkType: networkType,
} }
@ -120,6 +121,7 @@ func NewFakeProxier(syncPeriod time.Duration, minSyncPeriod time.Duration, clust
hostMac: macAddress, hostMac: macAddress,
isDSR: false, isDSR: false,
hns: newFakeHNS(), hns: newFakeHNS(),
endPointsRefCount: make(endPointsReferenceCountMap),
} }
return proxier return proxier
} }
@ -217,6 +219,14 @@ func TestCreateRemoteEndpointOverlay(t *testing.T) {
if proxier.endpointsMap[svcPortName][0].hnsID != guid { if proxier.endpointsMap[svcPortName][0].hnsID != guid {
t.Errorf("%v does not match %v", proxier.endpointsMap[svcPortName][0].hnsID, guid) t.Errorf("%v does not match %v", proxier.endpointsMap[svcPortName][0].hnsID, guid)
} }
if *proxier.endPointsRefCount[guid] <= 0 {
t.Errorf("RefCount not incremented. Current value: %v", *proxier.endPointsRefCount[guid])
}
if *proxier.endPointsRefCount[guid] != *proxier.endpointsMap[svcPortName][0].refCount {
t.Errorf("Global refCount: %v does not match endpoint refCount: %v", *proxier.endPointsRefCount[guid], *proxier.endpointsMap[svcPortName][0].refCount)
}
} }
func TestCreateRemoteEndpointL2Bridge(t *testing.T) { func TestCreateRemoteEndpointL2Bridge(t *testing.T) {
syncPeriod := 30 * time.Second syncPeriod := 30 * time.Second
@ -264,7 +274,16 @@ func TestCreateRemoteEndpointL2Bridge(t *testing.T) {
if proxier.endpointsMap[svcPortName][0].hnsID != guid { if proxier.endpointsMap[svcPortName][0].hnsID != guid {
t.Errorf("%v does not match %v", proxier.endpointsMap[svcPortName][0].hnsID, guid) t.Errorf("%v does not match %v", proxier.endpointsMap[svcPortName][0].hnsID, guid)
} }
if *proxier.endPointsRefCount[guid] <= 0 {
t.Errorf("RefCount not incremented. Current value: %v", *proxier.endPointsRefCount[guid])
}
if *proxier.endPointsRefCount[guid] != *proxier.endpointsMap[svcPortName][0].refCount {
t.Errorf("Global refCount: %v does not match endpoint refCount: %v", *proxier.endPointsRefCount[guid], *proxier.endpointsMap[svcPortName][0].refCount)
}
} }
func TestCreateLoadBalancer(t *testing.T) { func TestCreateLoadBalancer(t *testing.T) {
syncPeriod := 30 * time.Second syncPeriod := 30 * time.Second
proxier := NewFakeProxier(syncPeriod, syncPeriod, clusterCIDR, "testhost", net.ParseIP("10.0.0.1"), "Overlay") proxier := NewFakeProxier(syncPeriod, syncPeriod, clusterCIDR, "testhost", net.ParseIP("10.0.0.1"), "Overlay")