mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #107091 from robscott/endpointslice-metrics-perf
Improving performance of EndpointSlice controller metrics cache
This commit is contained in:
commit
13e97453f9
@ -39,11 +39,16 @@ type Cache struct {
|
|||||||
// should be added to an EndpointSlice.
|
// should be added to an EndpointSlice.
|
||||||
maxEndpointsPerSlice int32
|
maxEndpointsPerSlice int32
|
||||||
|
|
||||||
// lock protects changes to numEndpoints and cache.
|
// lock protects changes to numEndpoints, numSlicesActual, numSlicesDesired,
|
||||||
|
// and cache.
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
// numEndpoints represents the total number of endpoints stored in
|
// numEndpoints represents the total number of endpoints stored in
|
||||||
// EndpointSlices.
|
// EndpointSlices.
|
||||||
numEndpoints int
|
numEndpoints int
|
||||||
|
// numSlicesActual represents the total number of EndpointSlices.
|
||||||
|
numSlicesActual int
|
||||||
|
// numSlicesDesired represents the desired number of EndpointSlices.
|
||||||
|
numSlicesDesired int
|
||||||
// cache stores a ServicePortCache grouped by NamespacedNames representing
|
// cache stores a ServicePortCache grouped by NamespacedNames representing
|
||||||
// Services.
|
// Services.
|
||||||
cache map[types.NamespacedName]*ServicePortCache
|
cache map[types.NamespacedName]*ServicePortCache
|
||||||
@ -77,14 +82,16 @@ func (spc *ServicePortCache) Set(pmKey endpointutil.PortMapKey, eInfo Efficiency
|
|||||||
spc.items[pmKey] = eInfo
|
spc.items[pmKey] = eInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// numEndpoints returns the total number of endpoints represented by a
|
// totals returns the total number of endpoints and slices represented by a
|
||||||
// ServicePortCache.
|
// ServicePortCache.
|
||||||
func (spc *ServicePortCache) numEndpoints() int {
|
func (spc *ServicePortCache) totals(maxEndpointsPerSlice int) (int, int, int) {
|
||||||
num := 0
|
var actualSlices, desiredSlices, endpoints int
|
||||||
for _, eInfo := range spc.items {
|
for _, eInfo := range spc.items {
|
||||||
num += eInfo.Endpoints
|
endpoints += eInfo.Endpoints
|
||||||
|
actualSlices += eInfo.Slices
|
||||||
|
desiredSlices += numDesiredSlices(eInfo.Endpoints, maxEndpointsPerSlice)
|
||||||
}
|
}
|
||||||
return num
|
return actualSlices, desiredSlices, endpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateServicePortCache updates a ServicePortCache in the global cache for a
|
// UpdateServicePortCache updates a ServicePortCache in the global cache for a
|
||||||
@ -96,15 +103,18 @@ func (c *Cache) UpdateServicePortCache(serviceNN types.NamespacedName, spCache *
|
|||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
prevNumEndpoints := 0
|
var prevActualSlices, prevDesiredSlices, prevEndpoints int
|
||||||
if existingSPCache, ok := c.cache[serviceNN]; ok {
|
if existingSPCache, ok := c.cache[serviceNN]; ok {
|
||||||
prevNumEndpoints = existingSPCache.numEndpoints()
|
prevActualSlices, prevDesiredSlices, prevEndpoints = existingSPCache.totals(int(c.maxEndpointsPerSlice))
|
||||||
}
|
}
|
||||||
|
|
||||||
currNumEndpoints := spCache.numEndpoints()
|
currActualSlices, currDesiredSlices, currEndpoints := spCache.totals(int(c.maxEndpointsPerSlice))
|
||||||
// To keep numEndpoints up to date, add the difference between the number of
|
// To keep numEndpoints up to date, add the difference between the number of
|
||||||
// endpoints in the provided spCache and any spCache it might be replacing.
|
// endpoints in the provided spCache and any spCache it might be replacing.
|
||||||
c.numEndpoints = c.numEndpoints + currNumEndpoints - prevNumEndpoints
|
c.numEndpoints = c.numEndpoints + currEndpoints - prevEndpoints
|
||||||
|
|
||||||
|
c.numSlicesDesired += currDesiredSlices - prevDesiredSlices
|
||||||
|
c.numSlicesActual += currActualSlices - prevActualSlices
|
||||||
|
|
||||||
c.cache[serviceNN] = spCache
|
c.cache[serviceNN] = spCache
|
||||||
c.updateMetrics()
|
c.updateMetrics()
|
||||||
@ -117,45 +127,29 @@ func (c *Cache) DeleteService(serviceNN types.NamespacedName) {
|
|||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
if spCache, ok := c.cache[serviceNN]; ok {
|
if spCache, ok := c.cache[serviceNN]; ok {
|
||||||
c.numEndpoints = c.numEndpoints - spCache.numEndpoints()
|
actualSlices, desiredSlices, endpoints := spCache.totals(int(c.maxEndpointsPerSlice))
|
||||||
delete(c.cache, serviceNN)
|
c.numEndpoints = c.numEndpoints - endpoints
|
||||||
|
c.numSlicesDesired -= desiredSlices
|
||||||
|
c.numSlicesActual -= actualSlices
|
||||||
c.updateMetrics()
|
c.updateMetrics()
|
||||||
}
|
delete(c.cache, serviceNN)
|
||||||
}
|
|
||||||
|
|
||||||
// metricsUpdate stores a desired and actual number of EndpointSlices.
|
|
||||||
type metricsUpdate struct {
|
|
||||||
desired, actual int
|
|
||||||
}
|
|
||||||
|
|
||||||
// desiredAndActualSlices returns a metricsUpdate with the desired and actual
|
|
||||||
// number of EndpointSlices given the current values in the cache.
|
|
||||||
// Must be called holding lock.
|
|
||||||
func (c *Cache) desiredAndActualSlices() metricsUpdate {
|
|
||||||
mUpdate := metricsUpdate{}
|
|
||||||
for _, spCache := range c.cache {
|
|
||||||
for _, eInfo := range spCache.items {
|
|
||||||
mUpdate.actual += eInfo.Slices
|
|
||||||
mUpdate.desired += numDesiredSlices(eInfo.Endpoints, int(c.maxEndpointsPerSlice))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return mUpdate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateMetrics updates metrics with the values from this Cache.
|
// updateMetrics updates metrics with the values from this Cache.
|
||||||
// Must be called holding lock.
|
// Must be called holding lock.
|
||||||
func (c *Cache) updateMetrics() {
|
func (c *Cache) updateMetrics() {
|
||||||
mUpdate := c.desiredAndActualSlices()
|
NumEndpointSlices.WithLabelValues().Set(float64(c.numSlicesActual))
|
||||||
NumEndpointSlices.WithLabelValues().Set(float64(mUpdate.actual))
|
DesiredEndpointSlices.WithLabelValues().Set(float64(c.numSlicesDesired))
|
||||||
DesiredEndpointSlices.WithLabelValues().Set(float64(mUpdate.desired))
|
|
||||||
EndpointsDesired.WithLabelValues().Set(float64(c.numEndpoints))
|
EndpointsDesired.WithLabelValues().Set(float64(c.numEndpoints))
|
||||||
}
|
}
|
||||||
|
|
||||||
// numDesiredSlices calculates the number of EndpointSlices that would exist
|
// numDesiredSlices calculates the number of EndpointSlices that would exist
|
||||||
// with ideal endpoint distribution.
|
// with ideal endpoint distribution.
|
||||||
func numDesiredSlices(numEndpoints, maxPerSlice int) int {
|
func numDesiredSlices(numEndpoints, maxEndpointsPerSlice int) int {
|
||||||
if numEndpoints <= maxPerSlice {
|
if numEndpoints <= maxEndpointsPerSlice {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return int(math.Ceil(float64(numEndpoints) / float64(maxPerSlice)))
|
return int(math.Ceil(float64(numEndpoints) / float64(maxEndpointsPerSlice)))
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,14 @@ limitations under the License.
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
discovery "k8s.io/api/discovery/v1"
|
discovery "k8s.io/api/discovery/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/kubernetes/pkg/controller/util/endpoint"
|
||||||
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
|
endpointutil "k8s.io/kubernetes/pkg/controller/util/endpoint"
|
||||||
|
utilpointer "k8s.io/utils/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNumEndpointsAndSlices(t *testing.T) {
|
func TestNumEndpointsAndSlices(t *testing.T) {
|
||||||
@ -59,14 +62,57 @@ func TestNumEndpointsAndSlices(t *testing.T) {
|
|||||||
|
|
||||||
func expectNumEndpointsAndSlices(t *testing.T, c *Cache, desired int, actual int, numEndpoints int) {
|
func expectNumEndpointsAndSlices(t *testing.T, c *Cache, desired int, actual int, numEndpoints int) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
mUpdate := c.desiredAndActualSlices()
|
if c.numSlicesDesired != desired {
|
||||||
if mUpdate.desired != desired {
|
t.Errorf("Expected numSlicesDesired to be %d, got %d", desired, c.numSlicesDesired)
|
||||||
t.Errorf("Expected numEndpointSlices to be %d, got %d", desired, mUpdate.desired)
|
|
||||||
}
|
}
|
||||||
if mUpdate.actual != actual {
|
if c.numSlicesActual != actual {
|
||||||
t.Errorf("Expected desiredEndpointSlices to be %d, got %d", actual, mUpdate.actual)
|
t.Errorf("Expected numSlicesActual to be %d, got %d", actual, c.numSlicesActual)
|
||||||
}
|
}
|
||||||
if c.numEndpoints != numEndpoints {
|
if c.numEndpoints != numEndpoints {
|
||||||
t.Errorf("Expected numEndpoints to be %d, got %d", numEndpoints, c.numEndpoints)
|
t.Errorf("Expected numEndpoints to be %d, got %d", numEndpoints, c.numEndpoints)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func benchmarkUpdateServicePortCache(b *testing.B, num int) {
|
||||||
|
c := NewCache(int32(100))
|
||||||
|
ns := "benchmark"
|
||||||
|
httpKey := endpoint.NewPortMapKey([]discovery.EndpointPort{{Port: utilpointer.Int32Ptr(80)}})
|
||||||
|
httpsKey := endpoint.NewPortMapKey([]discovery.EndpointPort{{Port: utilpointer.Int32Ptr(443)}})
|
||||||
|
spCache := &ServicePortCache{items: map[endpointutil.PortMapKey]EfficiencyInfo{
|
||||||
|
httpKey: {
|
||||||
|
Endpoints: 182,
|
||||||
|
Slices: 2,
|
||||||
|
},
|
||||||
|
httpsKey: {
|
||||||
|
Endpoints: 356,
|
||||||
|
Slices: 4,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
nName := types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("service-%d", i)}
|
||||||
|
c.UpdateServicePortCache(nName, spCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
nName := types.NamespacedName{Namespace: ns, Name: fmt.Sprintf("bench-%d", i)}
|
||||||
|
c.UpdateServicePortCache(nName, spCache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUpdateServicePortCache100(b *testing.B) {
|
||||||
|
benchmarkUpdateServicePortCache(b, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUpdateServicePortCache1000(b *testing.B) {
|
||||||
|
benchmarkUpdateServicePortCache(b, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUpdateServicePortCache10000(b *testing.B) {
|
||||||
|
benchmarkUpdateServicePortCache(b, 10000)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUpdateServicePortCache100000(b *testing.B) {
|
||||||
|
benchmarkUpdateServicePortCache(b, 100000)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user