mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
Merge pull request #118568 from qiutongs/node-startup-latency
Create a node startup latency tracker
This commit is contained in:
commit
4fd8bd9975
@ -834,6 +834,10 @@ func run(ctx context.Context, s *options.KubeletServer, kubeDeps *kubelet.Depend
|
||||
kubeDeps.PodStartupLatencyTracker = kubeletutil.NewPodStartupLatencyTracker()
|
||||
}
|
||||
|
||||
if kubeDeps.NodeStartupLatencyTracker == nil {
|
||||
kubeDeps.NodeStartupLatencyTracker = kubeletutil.NewNodeStartupLatencyTracker()
|
||||
}
|
||||
|
||||
// TODO(vmarmol): Do this through container config.
|
||||
oomAdjuster := kubeDeps.OOMAdjuster
|
||||
if err := oomAdjuster.ApplyOOMScoreAdj(0, int(s.OOMScoreAdj)); err != nil {
|
||||
|
@ -271,6 +271,7 @@ type Dependencies struct {
|
||||
RemoteRuntimeService internalapi.RuntimeService
|
||||
RemoteImageService internalapi.ImageManagerService
|
||||
PodStartupLatencyTracker util.PodStartupLatencyTracker
|
||||
NodeStartupLatencyTracker util.NodeStartupLatencyTracker
|
||||
// remove it after cadvisor.UsingLegacyCadvisorStats dropped.
|
||||
useLegacyCadvisorStats bool
|
||||
}
|
||||
@ -552,6 +553,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||
keepTerminatedPodVolumes: keepTerminatedPodVolumes,
|
||||
nodeStatusMaxImages: nodeStatusMaxImages,
|
||||
tracer: tracer,
|
||||
nodeStartupLatencyTracker: kubeDeps.NodeStartupLatencyTracker,
|
||||
}
|
||||
|
||||
if klet.cloud != nil {
|
||||
@ -1293,6 +1295,9 @@ type Kubelet struct {
|
||||
|
||||
// OpenTelemetry Tracer
|
||||
tracer trace.Tracer
|
||||
|
||||
// Track node startup latencies
|
||||
nodeStartupLatencyTracker util.NodeStartupLatencyTracker
|
||||
}
|
||||
|
||||
// ListPodStats is delegated to StatsProvider, which implements stats.Provider interface
|
||||
|
@ -52,6 +52,9 @@ func (kl *Kubelet) registerWithAPIServer() {
|
||||
if kl.registrationCompleted {
|
||||
return
|
||||
}
|
||||
|
||||
kl.nodeStartupLatencyTracker.RecordAttemptRegisterNode()
|
||||
|
||||
step := 100 * time.Millisecond
|
||||
|
||||
for {
|
||||
@ -85,6 +88,7 @@ func (kl *Kubelet) registerWithAPIServer() {
|
||||
func (kl *Kubelet) tryRegisterWithAPIServer(node *v1.Node) bool {
|
||||
_, err := kl.kubeClient.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{})
|
||||
if err == nil {
|
||||
kl.nodeStartupLatencyTracker.RecordRegisteredNewNode()
|
||||
return true
|
||||
}
|
||||
|
||||
@ -633,6 +637,12 @@ func (kl *Kubelet) patchNodeStatus(originalNode, node *v1.Node) (*v1.Node, error
|
||||
}
|
||||
kl.lastStatusReportTime = kl.clock.Now()
|
||||
kl.setLastObservedNodeAddresses(updatedNode.Status.Addresses)
|
||||
|
||||
readyIdx, readyCondition := nodeutil.GetNodeCondition(&updatedNode.Status, v1.NodeReady)
|
||||
if readyIdx >= 0 && readyCondition.Status == v1.ConditionTrue {
|
||||
kl.nodeStartupLatencyTracker.RecordNodeReady()
|
||||
}
|
||||
|
||||
return updatedNode, nil
|
||||
}
|
||||
|
||||
|
@ -268,6 +268,7 @@ func newTestKubeletWithImageList(
|
||||
kubelet.podManager = kubepod.NewBasicPodManager()
|
||||
podStartupLatencyTracker := kubeletutil.NewPodStartupLatencyTracker()
|
||||
kubelet.statusManager = status.NewManager(fakeKubeClient, kubelet.podManager, &statustest.FakePodDeletionSafetyProvider{}, podStartupLatencyTracker, kubelet.getRootDir())
|
||||
kubelet.nodeStartupLatencyTracker = kubeletutil.NewNodeStartupLatencyTracker()
|
||||
|
||||
kubelet.containerRuntime = fakeRuntime
|
||||
kubelet.runtimeCache = containertest.NewFakeRuntimeCache(kubelet.containerRuntime)
|
||||
|
@ -33,6 +33,11 @@ const (
|
||||
KubeletSubsystem = "kubelet"
|
||||
NodeNameKey = "node_name"
|
||||
NodeLabelKey = "node"
|
||||
NodeStartupPreKubeletKey = "node_startup_pre_kubelet_duration_seconds"
|
||||
NodeStartupPreRegistrationKey = "node_startup_pre_registration_duration_seconds"
|
||||
NodeStartupRegistrationKey = "node_startup_registration_duration_seconds"
|
||||
NodeStartupPostRegistrationKey = "node_startup_post_registration_duration_seconds"
|
||||
NodeStartupKey = "node_startup_duration_seconds"
|
||||
PodWorkerDurationKey = "pod_worker_duration_seconds"
|
||||
PodStartDurationKey = "pod_start_duration_seconds"
|
||||
PodStartSLIDurationKey = "pod_start_sli_duration_seconds"
|
||||
@ -736,6 +741,51 @@ var (
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
)
|
||||
|
||||
NodeStartupPreKubeletDuration = metrics.NewGauge(
|
||||
&metrics.GaugeOpts{
|
||||
Subsystem: KubeletSubsystem,
|
||||
Name: NodeStartupPreKubeletKey,
|
||||
Help: "Duration in seconds of node startup before kubelet starts.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
)
|
||||
|
||||
NodeStartupPreRegistrationDuration = metrics.NewGauge(
|
||||
&metrics.GaugeOpts{
|
||||
Subsystem: KubeletSubsystem,
|
||||
Name: NodeStartupPreRegistrationKey,
|
||||
Help: "Duration in seconds of node startup before registration.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
)
|
||||
|
||||
NodeStartupRegistrationDuration = metrics.NewGauge(
|
||||
&metrics.GaugeOpts{
|
||||
Subsystem: KubeletSubsystem,
|
||||
Name: NodeStartupRegistrationKey,
|
||||
Help: "Duration in seconds of node startup during registration.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
)
|
||||
|
||||
NodeStartupPostRegistrationDuration = metrics.NewGauge(
|
||||
&metrics.GaugeOpts{
|
||||
Subsystem: KubeletSubsystem,
|
||||
Name: NodeStartupPostRegistrationKey,
|
||||
Help: "Duration in seconds of node startup after registration.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
)
|
||||
|
||||
NodeStartupDuration = metrics.NewGauge(
|
||||
&metrics.GaugeOpts{
|
||||
Subsystem: KubeletSubsystem,
|
||||
Name: NodeStartupKey,
|
||||
Help: "Duration in seconds of node startup in total.",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
var registerMetrics sync.Once
|
||||
@ -748,6 +798,11 @@ func Register(collectors ...metrics.StableCollector) {
|
||||
legacyregistry.MustRegister(PodWorkerDuration)
|
||||
legacyregistry.MustRegister(PodStartDuration)
|
||||
legacyregistry.MustRegister(PodStartSLIDuration)
|
||||
legacyregistry.MustRegister(NodeStartupPreKubeletDuration)
|
||||
legacyregistry.MustRegister(NodeStartupPreRegistrationDuration)
|
||||
legacyregistry.MustRegister(NodeStartupRegistrationDuration)
|
||||
legacyregistry.MustRegister(NodeStartupPostRegistrationDuration)
|
||||
legacyregistry.MustRegister(NodeStartupDuration)
|
||||
legacyregistry.MustRegister(CgroupManagerDuration)
|
||||
legacyregistry.MustRegister(PodWorkerStartDuration)
|
||||
legacyregistry.MustRegister(PodStatusSyncDuration)
|
||||
|
103
pkg/kubelet/util/node_startup_latency_tracker.go
Normal file
103
pkg/kubelet/util/node_startup_latency_tracker.go
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||
"k8s.io/utils/clock"
|
||||
)
|
||||
|
||||
type NodeStartupLatencyTracker interface {
|
||||
// This function may be called across Kubelet restart.
|
||||
RecordAttemptRegisterNode()
|
||||
// This function should not be called across Kubelet restart.
|
||||
RecordRegisteredNewNode()
|
||||
// This function may be called across Kubelet restart.
|
||||
RecordNodeReady()
|
||||
}
|
||||
|
||||
type basicNodeStartupLatencyTracker struct {
|
||||
lock sync.Mutex
|
||||
|
||||
bootTime time.Time
|
||||
kubeletStartTime time.Time
|
||||
firstRegistrationAttemptTime time.Time
|
||||
firstRegisteredNewNodeTime time.Time
|
||||
firstNodeReadyTime time.Time
|
||||
|
||||
// For testability
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
func NewNodeStartupLatencyTracker() NodeStartupLatencyTracker {
|
||||
bootTime, err := GetBootTime()
|
||||
if err != nil {
|
||||
bootTime = time.Time{}
|
||||
}
|
||||
return &basicNodeStartupLatencyTracker{
|
||||
bootTime: bootTime,
|
||||
kubeletStartTime: time.Now(),
|
||||
clock: clock.RealClock{},
|
||||
}
|
||||
}
|
||||
|
||||
func (n *basicNodeStartupLatencyTracker) RecordAttemptRegisterNode() {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
|
||||
if !n.firstRegistrationAttemptTime.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
n.firstRegistrationAttemptTime = n.clock.Now()
|
||||
}
|
||||
|
||||
func (n *basicNodeStartupLatencyTracker) RecordRegisteredNewNode() {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
|
||||
if n.firstRegistrationAttemptTime.IsZero() || !n.firstRegisteredNewNodeTime.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
n.firstRegisteredNewNodeTime = n.clock.Now()
|
||||
|
||||
if !n.bootTime.IsZero() {
|
||||
metrics.NodeStartupPreKubeletDuration.Set(n.kubeletStartTime.Sub(n.bootTime).Seconds())
|
||||
}
|
||||
metrics.NodeStartupPreRegistrationDuration.Set(n.firstRegistrationAttemptTime.Sub(n.kubeletStartTime).Seconds())
|
||||
metrics.NodeStartupRegistrationDuration.Set(n.firstRegisteredNewNodeTime.Sub(n.firstRegistrationAttemptTime).Seconds())
|
||||
}
|
||||
|
||||
func (n *basicNodeStartupLatencyTracker) RecordNodeReady() {
|
||||
n.lock.Lock()
|
||||
defer n.lock.Unlock()
|
||||
|
||||
if n.firstRegisteredNewNodeTime.IsZero() || !n.firstNodeReadyTime.IsZero() {
|
||||
return
|
||||
}
|
||||
|
||||
n.firstNodeReadyTime = n.clock.Now()
|
||||
|
||||
metrics.NodeStartupPostRegistrationDuration.Set(n.firstNodeReadyTime.Sub(n.firstRegisteredNewNodeTime).Seconds())
|
||||
if !n.bootTime.IsZero() {
|
||||
metrics.NodeStartupDuration.Set(n.firstNodeReadyTime.Sub(n.bootTime).Seconds())
|
||||
}
|
||||
}
|
418
pkg/kubelet/util/node_startup_latency_tracker_test.go
Normal file
418
pkg/kubelet/util/node_startup_latency_tracker_test.go
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/component-base/metrics/testutil"
|
||||
"k8s.io/kubernetes/pkg/kubelet/metrics"
|
||||
"k8s.io/utils/clock"
|
||||
testingclock "k8s.io/utils/clock/testing"
|
||||
)
|
||||
|
||||
const (
|
||||
metricsNameNodeStartupPreKubelet = "kubelet_node_startup_pre_kubelet_duration_seconds"
|
||||
metricsNameNodeStartupPreRegistration = "kubelet_node_startup_pre_registration_duration_seconds"
|
||||
metricsNameNodeStartupRegistration = "kubelet_node_startup_registration_duration_seconds"
|
||||
metricsNameNodeStartupPostRegistration = "kubelet_node_startup_post_registration_duration_seconds"
|
||||
metricsNameNodeStartup = "kubelet_node_startup_duration_seconds"
|
||||
)
|
||||
|
||||
func TestNodeStartupLatencyNoEvents(t *testing.T) {
|
||||
t.Run("metrics registered; no incoming events", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: clock.RealClock{},
|
||||
}
|
||||
|
||||
wants := `
|
||||
# HELP kubelet_node_startup_duration_seconds [ALPHA] Duration in seconds of node startup in total.
|
||||
# TYPE kubelet_node_startup_duration_seconds gauge
|
||||
kubelet_node_startup_duration_seconds 0
|
||||
# HELP kubelet_node_startup_post_registration_duration_seconds [ALPHA] Duration in seconds of node startup after registration.
|
||||
# TYPE kubelet_node_startup_post_registration_duration_seconds gauge
|
||||
kubelet_node_startup_post_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_kubelet_duration_seconds [ALPHA] Duration in seconds of node startup before kubelet starts.
|
||||
# TYPE kubelet_node_startup_pre_kubelet_duration_seconds gauge
|
||||
kubelet_node_startup_pre_kubelet_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_registration_duration_seconds [ALPHA] Duration in seconds of node startup before registration.
|
||||
# TYPE kubelet_node_startup_pre_registration_duration_seconds gauge
|
||||
kubelet_node_startup_pre_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_registration_duration_seconds [ALPHA] Duration in seconds of node startup during registration.
|
||||
# TYPE kubelet_node_startup_registration_duration_seconds gauge
|
||||
kubelet_node_startup_registration_duration_seconds 0
|
||||
`
|
||||
if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(wants),
|
||||
metricsNameNodeStartupPreKubelet,
|
||||
metricsNameNodeStartupPreRegistration,
|
||||
metricsNameNodeStartupRegistration,
|
||||
metricsNameNodeStartupPostRegistration,
|
||||
metricsNameNodeStartup,
|
||||
); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, frozenTime.Add(-100*time.Millisecond), tracker.bootTime)
|
||||
assert.Equal(t, frozenTime, tracker.kubeletStartTime)
|
||||
assert.True(t, tracker.firstRegistrationAttemptTime.IsZero())
|
||||
assert.True(t, tracker.firstRegisteredNewNodeTime.IsZero())
|
||||
assert.True(t, tracker.firstNodeReadyTime.IsZero())
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecordAllTimestamps(t *testing.T) {
|
||||
t.Run("all timestamps are recorded", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
fakeClock := testingclock.NewFakeClock(frozenTime)
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: fakeClock,
|
||||
}
|
||||
|
||||
fakeClock.Step(800 * time.Millisecond)
|
||||
tracker.RecordAttemptRegisterNode()
|
||||
|
||||
assert.Equal(t, frozenTime.Add(800*time.Millisecond), tracker.firstRegistrationAttemptTime)
|
||||
|
||||
fakeClock.Step(400 * time.Millisecond)
|
||||
tracker.RecordRegisteredNewNode()
|
||||
|
||||
assert.Equal(t, frozenTime.Add(1200*time.Millisecond), tracker.firstRegisteredNewNodeTime)
|
||||
|
||||
fakeClock.Step(1100 * time.Millisecond)
|
||||
tracker.RecordNodeReady()
|
||||
|
||||
assert.Equal(t, frozenTime.Add(2300*time.Millisecond), tracker.firstNodeReadyTime)
|
||||
|
||||
wants := `
|
||||
# HELP kubelet_node_startup_duration_seconds [ALPHA] Duration in seconds of node startup in total.
|
||||
# TYPE kubelet_node_startup_duration_seconds gauge
|
||||
kubelet_node_startup_duration_seconds 2.4
|
||||
# HELP kubelet_node_startup_post_registration_duration_seconds [ALPHA] Duration in seconds of node startup after registration.
|
||||
# TYPE kubelet_node_startup_post_registration_duration_seconds gauge
|
||||
kubelet_node_startup_post_registration_duration_seconds 1.1
|
||||
# HELP kubelet_node_startup_pre_kubelet_duration_seconds [ALPHA] Duration in seconds of node startup before kubelet starts.
|
||||
# TYPE kubelet_node_startup_pre_kubelet_duration_seconds gauge
|
||||
kubelet_node_startup_pre_kubelet_duration_seconds 0.1
|
||||
# HELP kubelet_node_startup_pre_registration_duration_seconds [ALPHA] Duration in seconds of node startup before registration.
|
||||
# TYPE kubelet_node_startup_pre_registration_duration_seconds gauge
|
||||
kubelet_node_startup_pre_registration_duration_seconds 0.8
|
||||
# HELP kubelet_node_startup_registration_duration_seconds [ALPHA] Duration in seconds of node startup during registration.
|
||||
# TYPE kubelet_node_startup_registration_duration_seconds gauge
|
||||
kubelet_node_startup_registration_duration_seconds 0.4
|
||||
`
|
||||
if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(wants),
|
||||
metricsNameNodeStartupPreKubelet,
|
||||
metricsNameNodeStartupPreRegistration,
|
||||
metricsNameNodeStartupRegistration,
|
||||
metricsNameNodeStartupPostRegistration,
|
||||
metricsNameNodeStartup,
|
||||
); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecordAttemptRegister(t *testing.T) {
|
||||
t.Run("record attempt register node", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
fakeClock := testingclock.NewFakeClock(frozenTime)
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: fakeClock,
|
||||
}
|
||||
|
||||
fakeClock.Step(600 * time.Millisecond)
|
||||
tracker.RecordAttemptRegisterNode()
|
||||
|
||||
assert.Equal(t, frozenTime.Add(600*time.Millisecond), tracker.firstRegistrationAttemptTime)
|
||||
assert.True(t, tracker.firstRegisteredNewNodeTime.IsZero())
|
||||
assert.True(t, tracker.firstNodeReadyTime.IsZero())
|
||||
|
||||
wants := `
|
||||
# HELP kubelet_node_startup_duration_seconds [ALPHA] Duration in seconds of node startup in total.
|
||||
# TYPE kubelet_node_startup_duration_seconds gauge
|
||||
kubelet_node_startup_duration_seconds 0
|
||||
# HELP kubelet_node_startup_post_registration_duration_seconds [ALPHA] Duration in seconds of node startup after registration.
|
||||
# TYPE kubelet_node_startup_post_registration_duration_seconds gauge
|
||||
kubelet_node_startup_post_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_kubelet_duration_seconds [ALPHA] Duration in seconds of node startup before kubelet starts.
|
||||
# TYPE kubelet_node_startup_pre_kubelet_duration_seconds gauge
|
||||
kubelet_node_startup_pre_kubelet_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_registration_duration_seconds [ALPHA] Duration in seconds of node startup before registration.
|
||||
# TYPE kubelet_node_startup_pre_registration_duration_seconds gauge
|
||||
kubelet_node_startup_pre_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_registration_duration_seconds [ALPHA] Duration in seconds of node startup during registration.
|
||||
# TYPE kubelet_node_startup_registration_duration_seconds gauge
|
||||
kubelet_node_startup_registration_duration_seconds 0
|
||||
`
|
||||
if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(wants),
|
||||
metricsNameNodeStartupPreKubelet,
|
||||
metricsNameNodeStartupPreRegistration,
|
||||
metricsNameNodeStartupRegistration,
|
||||
metricsNameNodeStartupPostRegistration,
|
||||
metricsNameNodeStartup,
|
||||
); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecordAttemptRegisterTwice(t *testing.T) {
|
||||
t.Run("record attempt register node twice", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
fakeClock := testingclock.NewFakeClock(frozenTime)
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: fakeClock,
|
||||
}
|
||||
|
||||
fakeClock.Step(600 * time.Millisecond)
|
||||
tracker.RecordAttemptRegisterNode()
|
||||
|
||||
fakeClock.Step(300 * time.Millisecond)
|
||||
tracker.RecordAttemptRegisterNode()
|
||||
|
||||
assert.Equal(t, frozenTime.Add(600*time.Millisecond), tracker.firstRegistrationAttemptTime)
|
||||
assert.True(t, tracker.firstRegisteredNewNodeTime.IsZero())
|
||||
assert.True(t, tracker.firstNodeReadyTime.IsZero())
|
||||
})
|
||||
}
|
||||
|
||||
func TestSkippingRecordRegisteredNewNode(t *testing.T) {
|
||||
t.Run("record register new node twice", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
fakeClock := testingclock.NewFakeClock(frozenTime)
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: fakeClock,
|
||||
}
|
||||
|
||||
fakeClock.Step(100 * time.Millisecond)
|
||||
tracker.RecordAttemptRegisterNode()
|
||||
|
||||
fakeClock.Step(500 * time.Millisecond)
|
||||
tracker.RecordRegisteredNewNode()
|
||||
|
||||
fakeClock.Step(300 * time.Millisecond)
|
||||
tracker.RecordRegisteredNewNode()
|
||||
|
||||
assert.Equal(t, frozenTime.Add(600*time.Millisecond), tracker.firstRegisteredNewNodeTime)
|
||||
|
||||
wants := `
|
||||
# HELP kubelet_node_startup_duration_seconds [ALPHA] Duration in seconds of node startup in total.
|
||||
# TYPE kubelet_node_startup_duration_seconds gauge
|
||||
kubelet_node_startup_duration_seconds 0
|
||||
# HELP kubelet_node_startup_post_registration_duration_seconds [ALPHA] Duration in seconds of node startup after registration.
|
||||
# TYPE kubelet_node_startup_post_registration_duration_seconds gauge
|
||||
kubelet_node_startup_post_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_kubelet_duration_seconds [ALPHA] Duration in seconds of node startup before kubelet starts.
|
||||
# TYPE kubelet_node_startup_pre_kubelet_duration_seconds gauge
|
||||
kubelet_node_startup_pre_kubelet_duration_seconds 0.1
|
||||
# HELP kubelet_node_startup_pre_registration_duration_seconds [ALPHA] Duration in seconds of node startup before registration.
|
||||
# TYPE kubelet_node_startup_pre_registration_duration_seconds gauge
|
||||
kubelet_node_startup_pre_registration_duration_seconds 0.1
|
||||
# HELP kubelet_node_startup_registration_duration_seconds [ALPHA] Duration in seconds of node startup during registration.
|
||||
# TYPE kubelet_node_startup_registration_duration_seconds gauge
|
||||
kubelet_node_startup_registration_duration_seconds 0.5
|
||||
`
|
||||
if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(wants),
|
||||
metricsNameNodeStartupPreKubelet,
|
||||
metricsNameNodeStartupPreRegistration,
|
||||
metricsNameNodeStartupRegistration,
|
||||
metricsNameNodeStartupPostRegistration,
|
||||
metricsNameNodeStartup,
|
||||
); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("record register new node without previous step", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
fakeClock := testingclock.NewFakeClock(frozenTime)
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: fakeClock,
|
||||
}
|
||||
|
||||
fakeClock.Step(700 * time.Millisecond)
|
||||
tracker.RecordRegisteredNewNode()
|
||||
|
||||
assert.True(t, tracker.firstRegisteredNewNodeTime.IsZero())
|
||||
|
||||
wants := `
|
||||
# HELP kubelet_node_startup_duration_seconds [ALPHA] Duration in seconds of node startup in total.
|
||||
# TYPE kubelet_node_startup_duration_seconds gauge
|
||||
kubelet_node_startup_duration_seconds 0
|
||||
# HELP kubelet_node_startup_post_registration_duration_seconds [ALPHA] Duration in seconds of node startup after registration.
|
||||
# TYPE kubelet_node_startup_post_registration_duration_seconds gauge
|
||||
kubelet_node_startup_post_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_kubelet_duration_seconds [ALPHA] Duration in seconds of node startup before kubelet starts.
|
||||
# TYPE kubelet_node_startup_pre_kubelet_duration_seconds gauge
|
||||
kubelet_node_startup_pre_kubelet_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_registration_duration_seconds [ALPHA] Duration in seconds of node startup before registration.
|
||||
# TYPE kubelet_node_startup_pre_registration_duration_seconds gauge
|
||||
kubelet_node_startup_pre_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_registration_duration_seconds [ALPHA] Duration in seconds of node startup during registration.
|
||||
# TYPE kubelet_node_startup_registration_duration_seconds gauge
|
||||
kubelet_node_startup_registration_duration_seconds 0
|
||||
`
|
||||
if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(wants),
|
||||
metricsNameNodeStartupPreKubelet,
|
||||
metricsNameNodeStartupPreRegistration,
|
||||
metricsNameNodeStartupRegistration,
|
||||
metricsNameNodeStartupPostRegistration,
|
||||
metricsNameNodeStartup,
|
||||
); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSkippingRecordNodeReady(t *testing.T) {
|
||||
t.Run("record node ready twice", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
fakeClock := testingclock.NewFakeClock(frozenTime)
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: fakeClock,
|
||||
}
|
||||
|
||||
fakeClock.Step(100 * time.Millisecond)
|
||||
tracker.RecordAttemptRegisterNode()
|
||||
|
||||
fakeClock.Step(200 * time.Millisecond)
|
||||
tracker.RecordRegisteredNewNode()
|
||||
|
||||
fakeClock.Step(300 * time.Millisecond)
|
||||
tracker.RecordNodeReady()
|
||||
|
||||
fakeClock.Step(700 * time.Millisecond)
|
||||
tracker.RecordNodeReady()
|
||||
|
||||
assert.Equal(t, frozenTime.Add(600*time.Millisecond), tracker.firstNodeReadyTime)
|
||||
|
||||
wants := `
|
||||
# HELP kubelet_node_startup_duration_seconds [ALPHA] Duration in seconds of node startup in total.
|
||||
# TYPE kubelet_node_startup_duration_seconds gauge
|
||||
kubelet_node_startup_duration_seconds 0.7
|
||||
# HELP kubelet_node_startup_post_registration_duration_seconds [ALPHA] Duration in seconds of node startup after registration.
|
||||
# TYPE kubelet_node_startup_post_registration_duration_seconds gauge
|
||||
kubelet_node_startup_post_registration_duration_seconds 0.3
|
||||
# HELP kubelet_node_startup_pre_kubelet_duration_seconds [ALPHA] Duration in seconds of node startup before kubelet starts.
|
||||
# TYPE kubelet_node_startup_pre_kubelet_duration_seconds gauge
|
||||
kubelet_node_startup_pre_kubelet_duration_seconds 0.1
|
||||
# HELP kubelet_node_startup_pre_registration_duration_seconds [ALPHA] Duration in seconds of node startup before registration.
|
||||
# TYPE kubelet_node_startup_pre_registration_duration_seconds gauge
|
||||
kubelet_node_startup_pre_registration_duration_seconds 0.1
|
||||
# HELP kubelet_node_startup_registration_duration_seconds [ALPHA] Duration in seconds of node startup during registration.
|
||||
# TYPE kubelet_node_startup_registration_duration_seconds gauge
|
||||
kubelet_node_startup_registration_duration_seconds 0.2
|
||||
`
|
||||
if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(wants),
|
||||
metricsNameNodeStartupPreKubelet,
|
||||
metricsNameNodeStartupPreRegistration,
|
||||
metricsNameNodeStartupRegistration,
|
||||
metricsNameNodeStartupPostRegistration,
|
||||
metricsNameNodeStartup,
|
||||
); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("record node ready without previous step", func(t *testing.T) {
|
||||
metrics.Register()
|
||||
defer clearMetrics()
|
||||
|
||||
fakeClock := testingclock.NewFakeClock(frozenTime)
|
||||
tracker := &basicNodeStartupLatencyTracker{
|
||||
bootTime: frozenTime.Add(-100 * time.Millisecond),
|
||||
kubeletStartTime: frozenTime,
|
||||
clock: fakeClock,
|
||||
}
|
||||
|
||||
fakeClock.Step(100 * time.Millisecond)
|
||||
tracker.RecordAttemptRegisterNode()
|
||||
|
||||
fakeClock.Step(700 * time.Millisecond)
|
||||
tracker.RecordNodeReady()
|
||||
|
||||
assert.True(t, tracker.firstNodeReadyTime.IsZero())
|
||||
|
||||
wants := `
|
||||
# HELP kubelet_node_startup_duration_seconds [ALPHA] Duration in seconds of node startup in total.
|
||||
# TYPE kubelet_node_startup_duration_seconds gauge
|
||||
kubelet_node_startup_duration_seconds 0
|
||||
# HELP kubelet_node_startup_post_registration_duration_seconds [ALPHA] Duration in seconds of node startup after registration.
|
||||
# TYPE kubelet_node_startup_post_registration_duration_seconds gauge
|
||||
kubelet_node_startup_post_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_kubelet_duration_seconds [ALPHA] Duration in seconds of node startup before kubelet starts.
|
||||
# TYPE kubelet_node_startup_pre_kubelet_duration_seconds gauge
|
||||
kubelet_node_startup_pre_kubelet_duration_seconds 0
|
||||
# HELP kubelet_node_startup_pre_registration_duration_seconds [ALPHA] Duration in seconds of node startup before registration.
|
||||
# TYPE kubelet_node_startup_pre_registration_duration_seconds gauge
|
||||
kubelet_node_startup_pre_registration_duration_seconds 0
|
||||
# HELP kubelet_node_startup_registration_duration_seconds [ALPHA] Duration in seconds of node startup during registration.
|
||||
# TYPE kubelet_node_startup_registration_duration_seconds gauge
|
||||
kubelet_node_startup_registration_duration_seconds 0
|
||||
`
|
||||
if err := testutil.GatherAndCompare(metrics.GetGather(), strings.NewReader(wants),
|
||||
metricsNameNodeStartupPreKubelet,
|
||||
metricsNameNodeStartupPreRegistration,
|
||||
metricsNameNodeStartupRegistration,
|
||||
metricsNameNodeStartupPostRegistration,
|
||||
metricsNameNodeStartup,
|
||||
); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func clearMetrics() {
|
||||
metrics.NodeStartupPreKubeletDuration.Set(0)
|
||||
metrics.NodeStartupPreRegistrationDuration.Set(0)
|
||||
metrics.NodeStartupRegistrationDuration.Set(0)
|
||||
metrics.NodeStartupPostRegistrationDuration.Set(0)
|
||||
metrics.NodeStartupDuration.Set(0)
|
||||
}
|
@ -113,6 +113,7 @@ func NewHollowKubelet(
|
||||
Subpather: &subpath.FakeSubpath{},
|
||||
HostUtil: hostutil.NewFakeHostUtil(nil),
|
||||
PodStartupLatencyTracker: kubeletutil.NewPodStartupLatencyTracker(),
|
||||
NodeStartupLatencyTracker: kubeletutil.NewNodeStartupLatencyTracker(),
|
||||
TracerProvider: trace.NewNoopTracerProvider(),
|
||||
Recorder: &record.FakeRecorder{}, // With real recorder we attempt to read /dev/kmsg.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user