Skip to content

Commit 2a6ed07

Browse files
committed
Add crictl stats
Signed-off-by: Lantao Liu <[email protected]>
1 parent d7a6822 commit 2a6ed07

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed

cmd/crictl/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func main() {
110110
stopPodSandboxCommand,
111111
updateContainerCommand,
112112
configCommand,
113+
statsCommand,
113114
}
114115

115116
app.Flags = []cli.Flag{

cmd/crictl/stats.go

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"sort"
23+
"strings"
24+
"text/tabwriter"
25+
"time"
26+
27+
"github.com/Sirupsen/logrus"
28+
units "github.com/docker/go-units"
29+
"github.com/urfave/cli"
30+
"golang.org/x/net/context"
31+
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
32+
)
33+
34+
type statsOptions struct {
35+
// all containers
36+
all bool
37+
// id of container
38+
id string
39+
// podID of container
40+
podID string
41+
// sample is the duration for sampling cpu usage.
42+
sample time.Duration
43+
// labels are selectors for the sandbox
44+
labels map[string]string
45+
// output format
46+
output string
47+
}
48+
49+
var statsCommand = cli.Command{
50+
Name: "stats",
51+
// TODO(random-liu): Support live monitoring of resource usage.
52+
Usage: "List container(s) resource usage statistics",
53+
Flags: []cli.Flag{
54+
cli.BoolFlag{
55+
Name: "all, a",
56+
Usage: "Show all containers (default shows just running)",
57+
},
58+
cli.StringFlag{
59+
Name: "id",
60+
Value: "",
61+
Usage: "Filter by container id",
62+
},
63+
cli.StringFlag{
64+
Name: "sandbox",
65+
Value: "",
66+
Usage: "Filter by sandbox id",
67+
},
68+
cli.StringSliceFlag{
69+
Name: "label",
70+
Usage: "Filter by key=value label",
71+
},
72+
cli.StringFlag{
73+
Name: "output, o",
74+
Usage: "Output format, One of: json|yaml|table",
75+
},
76+
cli.IntFlag{
77+
Name: "seconds, s",
78+
Value: 1,
79+
Usage: "Sample duration for CPU usage in seconds",
80+
},
81+
},
82+
Action: func(context *cli.Context) error {
83+
var err error
84+
if err := getRuntimeClient(context); err != nil {
85+
return err
86+
}
87+
88+
opts := statsOptions{
89+
all: context.Bool("all"),
90+
id: context.String("id"),
91+
podID: context.String("sandbox"),
92+
sample: time.Duration(context.Int("seconds")) * time.Second,
93+
output: context.String("output"),
94+
}
95+
opts.labels, err = parseLabelStringSlice(context.StringSlice("label"))
96+
if err != nil {
97+
return err
98+
}
99+
100+
if err := ContainerStats(runtimeClient, opts); err != nil {
101+
return fmt.Errorf("get container stats failed: %v", err)
102+
}
103+
return nil
104+
},
105+
}
106+
107+
type containerStatsByID []*pb.ContainerStats
108+
109+
func (c containerStatsByID) Len() int { return len(c) }
110+
func (c containerStatsByID) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
111+
func (c containerStatsByID) Less(i, j int) bool {
112+
return c[i].Attributes.Id < c[j].Attributes.Id
113+
}
114+
115+
// ContainerStats sends a ListContainerStatsRequest to the server, and
116+
// parses the returned ListContainerStatsResponse.
117+
func ContainerStats(client pb.RuntimeServiceClient, opts statsOptions) error {
118+
filter := &pb.ContainerStatsFilter{}
119+
if opts.id != "" {
120+
filter.Id = opts.id
121+
}
122+
if opts.podID != "" {
123+
filter.PodSandboxId = opts.podID
124+
}
125+
if opts.labels != nil {
126+
filter.LabelSelector = opts.labels
127+
}
128+
request := &pb.ListContainerStatsRequest{
129+
Filter: filter,
130+
}
131+
logrus.Debugf("ListContainerStatsRequest: %v", request)
132+
r, err := client.ListContainerStats(context.Background(), request)
133+
logrus.Debugf("ListContainerResponse: %v", r)
134+
if err != nil {
135+
return err
136+
}
137+
sort.Sort(containerStatsByID(r.Stats))
138+
139+
switch opts.output {
140+
case "json":
141+
return outputProtobufObjAsJSON(r)
142+
case "yaml":
143+
return outputProtobufObjAsYAML(r)
144+
}
145+
oldStats := make(map[string]*pb.ContainerStats)
146+
for _, s := range r.GetStats() {
147+
oldStats[s.Attributes.Id] = s
148+
}
149+
150+
time.Sleep(opts.sample)
151+
152+
logrus.Debugf("ListContainerStatsRequest: %v", request)
153+
r, err = client.ListContainerStats(context.Background(), request)
154+
logrus.Debugf("ListContainerResponse: %v", r)
155+
if err != nil {
156+
return err
157+
}
158+
sort.Sort(containerStatsByID(r.Stats))
159+
160+
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
161+
fmt.Fprintln(w, "CONTAINER\tCPU %\tMEM\tDISK\tINODES")
162+
for _, s := range r.GetStats() {
163+
id := strings.TrimPrefix(s.Attributes.Id, "")[:truncatedIDLen]
164+
cpu := s.GetCpu().GetUsageCoreNanoSeconds().GetValue()
165+
mem := s.GetMemory().GetWorkingSetBytes().GetValue()
166+
disk := s.GetWritableLayer().GetUsedBytes().GetValue()
167+
inodes := s.GetWritableLayer().GetInodesUsed().GetValue()
168+
if !opts.all && cpu == 0 && mem == 0 {
169+
// Skip non-running container
170+
continue
171+
}
172+
old, ok := oldStats[s.Attributes.Id]
173+
if !ok {
174+
// Skip new container
175+
continue
176+
}
177+
var cpuPerc float64
178+
if cpu != 0 {
179+
// Only generate cpuPerc for running container
180+
duration := s.GetCpu().GetTimestamp() - old.GetCpu().GetTimestamp()
181+
if duration == 0 {
182+
return fmt.Errorf("cpu stat is not updated during sample")
183+
}
184+
cpuPerc = float64(cpu-old.GetCpu().GetUsageCoreNanoSeconds().GetValue()) / float64(duration) * 100
185+
}
186+
fmt.Fprintf(w, "%s\t%.2f\t%s\t%s\t%d\n", id, cpuPerc, units.HumanSize(float64(mem)), units.HumanSize(float64(disk)), inodes)
187+
}
188+
189+
w.Flush()
190+
return nil
191+
}

0 commit comments

Comments
 (0)