Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/ws-daemon/pkg/cgroup/plugin_psi.go
2499 views
1
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package cgroup
6
7
import (
8
"context"
9
"os"
10
"path/filepath"
11
"time"
12
13
cgroups "github.com/gitpod-io/gitpod/common-go/cgroups/v2"
14
"github.com/gitpod-io/gitpod/common-go/kubernetes"
15
"github.com/gitpod-io/gitpod/common-go/log"
16
"github.com/prometheus/client_golang/prometheus"
17
)
18
19
type PSIMetrics struct {
20
cpu *prometheus.GaugeVec
21
memory *prometheus.GaugeVec
22
io *prometheus.GaugeVec
23
nodeName string
24
}
25
26
func NewPSIMetrics(prom prometheus.Registerer) *PSIMetrics {
27
p := &PSIMetrics{
28
cpu: prometheus.NewGaugeVec(prometheus.GaugeOpts{
29
Name: "workspace_cpu_psi_total_seconds",
30
Help: "Total time spent under cpu pressure in microseconds",
31
}, []string{"node", "workspace", "kind"}),
32
33
memory: prometheus.NewGaugeVec(prometheus.GaugeOpts{
34
Name: "workspace_memory_psi_total_seconds",
35
Help: "Total time spent under memory pressure in microseconds",
36
}, []string{"node", "workspace", "kind"}),
37
38
io: prometheus.NewGaugeVec(prometheus.GaugeOpts{
39
Name: "workspace_io_psi_total_seconds",
40
Help: "Total time spent under io pressure in microseconds",
41
}, []string{"node", "workspace", "kind"}),
42
43
nodeName: os.Getenv("NODENAME"),
44
}
45
46
prom.MustRegister(
47
p.cpu,
48
p.memory,
49
p.io,
50
)
51
52
return p
53
}
54
55
func (p *PSIMetrics) Name() string { return "psi-metrics" }
56
func (p *PSIMetrics) Type() Version { return Version2 }
57
58
func (p *PSIMetrics) Apply(ctx context.Context, opts *PluginOptions) error {
59
if _, v := opts.Annotations[kubernetes.WorkspacePressureStallInfoAnnotation]; !v {
60
return nil
61
}
62
63
fullPath := filepath.Join(opts.BasePath, opts.CgroupPath)
64
if _, err := os.Stat(fullPath); err != nil {
65
return err
66
}
67
68
cpu := cgroups.NewCpuController(fullPath)
69
memory := cgroups.NewMemoryController(fullPath)
70
io := cgroups.NewIOController(fullPath)
71
72
go func() {
73
ticker := time.NewTicker(10 * time.Second)
74
defer ticker.Stop()
75
76
for {
77
select {
78
case <-ticker.C:
79
p.scrape(cpu, memory, io, opts.InstanceId)
80
case <-ctx.Done():
81
return
82
}
83
}
84
}()
85
86
return nil
87
}
88
89
func (p *PSIMetrics) scrape(cpu *cgroups.Cpu, memory *cgroups.Memory, io *cgroups.IO, instanceID string) {
90
if psi, err := cpu.PSI(); err == nil {
91
p.cpu.WithLabelValues(p.nodeName, instanceID, "some").Set(float64(psi.Some))
92
p.cpu.WithLabelValues(p.nodeName, instanceID, "full").Set(float64(psi.Full))
93
} else if !os.IsNotExist(err) {
94
log.WithError(err).WithFields(log.OWI("", "", instanceID)).Warn("could not retrieve cpu psi")
95
}
96
97
if psi, err := memory.PSI(); err == nil {
98
p.memory.WithLabelValues(p.nodeName, instanceID, "some").Set(float64(psi.Some))
99
p.memory.WithLabelValues(p.nodeName, instanceID, "full").Set(float64(psi.Full))
100
} else if !os.IsNotExist(err) {
101
log.WithError(err).WithFields(log.OWI("", "", instanceID)).Warn("could not retrieve memory psi")
102
}
103
104
if psi, err := io.PSI(); err == nil {
105
p.io.WithLabelValues(p.nodeName, instanceID, "some").Set(float64(psi.Some))
106
p.io.WithLabelValues(p.nodeName, instanceID, "full").Set(float64(psi.Full))
107
} else if !os.IsNotExist(err) {
108
log.WithError(err).WithFields(log.OWI("", "", instanceID)).Warn("could not retrieve io psi")
109
}
110
}
111
112