Skip to content

Commit 0ab27c2

Browse files
authored
Merge pull request #2956 from google/containerd-cri
Integrate CRI and containerd file system stats
2 parents 167d846 + c379491 commit 0ab27c2

28 files changed

+73931
-79
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ docker-build:
9191
presubmit: vet
9292
@echo ">> checking go formatting"
9393
@./build/check_gofmt.sh
94+
@echo ">> checking CRI definitions"
95+
@./build/check_cri_defs.sh
9496
@echo ">> checking go mod tidy"
9597
@./build/check_gotidy.sh
9698
@echo ">> checking file boilerplate"

build/boilerplate/boilerplate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def file_passes(filename, refs, regexs):
9595
def file_extension(filename):
9696
return os.path.splitext(filename)[1].split(".")[-1].lower()
9797

98-
skipped_dirs = ['Godeps', 'vendor', 'third_party', '_gopath', '_output', '.git']
98+
skipped_dirs = ['Godeps', 'vendor', 'third_party', '_gopath', '_output', '.git', 'cri-api']
9999
def normalize_files(files):
100100
newfiles = []
101101
for pathname in files:

build/check_cri_defs.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2021 Google Inc. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Presubmit script that ensures that https://github.com/kubernetes/cri-api
18+
# definitions that are copied into cAdvisor codebase exactly match the ones upstream.
19+
20+
set -e
21+
22+
function check_git_dirty() {
23+
if ! [ -z "$(git status --porcelain)" ]; then
24+
echo ">>> working tree is not clean"
25+
echo ">>> git status:"
26+
echo "$(git status)"
27+
exit 1
28+
fi
29+
}
30+
31+
GIT_ROOT=$(dirname "${BASH_SOURCE}")/..
32+
33+
check_git_dirty
34+
35+
echo ">>> updating k8s CRI definitions..."
36+
"${GIT_ROOT}/build/update_cri_defs.sh"
37+
38+
# If k8s CRI definitions were manually modified or don't match the ones
39+
# upstream, git tree will be unclean.
40+
echo ">>> checking git tree clean after updating k8s CRI definitions..."
41+
check_git_dirty

build/update_cri_defs.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright 2021 Google Inc. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
set -e
18+
19+
CRI_VERSION="release-1.22"
20+
GIT_ROOT=$(dirname "${BASH_SOURCE}")/..
21+
22+
TMP_DIR="$(mktemp -d)"
23+
git clone --depth=1 https://github.com/kubernetes/cri-api.git --branch="${CRI_VERSION}" "${TMP_DIR}"
24+
25+
# rewrite import paths
26+
find "${TMP_DIR}" -type f -name "*.go" -exec sed -i 's:k8s.io/cri-api:github.com/google/cadvisor/cri-api:g' {} +
27+
28+
mkdir -p "${GIT_ROOT}/cri-api"
29+
cp -r "${TMP_DIR}/pkg" "${GIT_ROOT}/cri-api"
30+
rm -rf "${TMP_DIR}"

cmd/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ require (
2121
github.com/mesos/mesos-go v0.0.7-0.20180413204204-29de6ff97b48
2222
github.com/pquerna/ffjson v0.0.0-20171002144729-d49c2bc1aa13 // indirect
2323
github.com/prometheus/client_golang v1.8.0
24-
github.com/stretchr/testify v1.6.1
24+
github.com/stretchr/testify v1.7.0
2525
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
2626
google.golang.org/api v0.34.0
2727
gopkg.in/olivere/elastic.v2 v2.0.12

cmd/go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,9 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
454454
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
455455
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
456456
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
457-
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
458457
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
458+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
459+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
459460
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
460461
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
461462
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=

container/common/fsHandler.go

Lines changed: 83 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,21 @@ type FsUsage struct {
3737
InodeUsage uint64
3838
}
3939

40+
type FsUsageProvider interface {
41+
// Usage returns the fs usage
42+
Usage() (*FsUsage, error)
43+
// Targets returns where the fs usage metric is collected,it maybe a directory, a file or some
44+
// information about the snapshotter(for containerd)
45+
Targets() []string
46+
}
47+
4048
type realFsHandler struct {
4149
sync.RWMutex
42-
lastUpdate time.Time
43-
usage FsUsage
44-
period time.Duration
45-
minPeriod time.Duration
46-
rootfs string
47-
extraDir string
48-
fsInfo fs.FsInfo
50+
lastUpdate time.Time
51+
usage FsUsage
52+
period time.Duration
53+
minPeriod time.Duration
54+
usageProvider FsUsageProvider
4955
// Tells the container to stop.
5056
stopChan chan struct{}
5157
}
@@ -58,56 +64,33 @@ const DefaultPeriod = time.Minute
5864

5965
var _ FsHandler = &realFsHandler{}
6066

61-
func NewFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) FsHandler {
67+
func NewFsHandler(period time.Duration, provider FsUsageProvider) FsHandler {
6268
return &realFsHandler{
63-
lastUpdate: time.Time{},
64-
usage: FsUsage{},
65-
period: period,
66-
minPeriod: period,
67-
rootfs: rootfs,
68-
extraDir: extraDir,
69-
fsInfo: fsInfo,
70-
stopChan: make(chan struct{}, 1),
69+
lastUpdate: time.Time{},
70+
usage: FsUsage{},
71+
period: period,
72+
minPeriod: period,
73+
usageProvider: provider,
74+
stopChan: make(chan struct{}, 1),
7175
}
7276
}
7377

7478
func (fh *realFsHandler) update() error {
75-
var (
76-
rootUsage, extraUsage fs.UsageInfo
77-
rootErr, extraErr error
78-
)
79-
// TODO(vishh): Add support for external mounts.
80-
if fh.rootfs != "" {
81-
rootUsage, rootErr = fh.fsInfo.GetDirUsage(fh.rootfs)
82-
}
8379

84-
if fh.extraDir != "" {
85-
extraUsage, extraErr = fh.fsInfo.GetDirUsage(fh.extraDir)
80+
usage, err := fh.usageProvider.Usage()
81+
82+
if err != nil {
83+
return err
8684
}
8785

88-
// Wait to handle errors until after all operartions are run.
89-
// An error in one will not cause an early return, skipping others
9086
fh.Lock()
9187
defer fh.Unlock()
9288
fh.lastUpdate = time.Now()
93-
if fh.rootfs != "" && rootErr == nil {
94-
fh.usage.InodeUsage = rootUsage.Inodes
95-
fh.usage.BaseUsageBytes = rootUsage.Bytes
96-
fh.usage.TotalUsageBytes = rootUsage.Bytes
97-
}
98-
if fh.extraDir != "" && extraErr == nil {
99-
if fh.rootfs != "" {
100-
fh.usage.TotalUsageBytes += extraUsage.Bytes
101-
} else {
102-
// rootfs is empty, totalUsageBytes use extra usage bytes
103-
fh.usage.TotalUsageBytes = extraUsage.Bytes
104-
}
105-
}
10689

107-
// Combine errors into a single error to return
108-
if rootErr != nil || extraErr != nil {
109-
return fmt.Errorf("rootDiskErr: %v, extraDiskErr: %v", rootErr, extraErr)
110-
}
90+
fh.usage.InodeUsage = usage.InodeUsage
91+
fh.usage.BaseUsageBytes = usage.BaseUsageBytes
92+
fh.usage.TotalUsageBytes = usage.TotalUsageBytes
93+
11194
return nil
11295
}
11396

@@ -130,7 +113,8 @@ func (fh *realFsHandler) trackUsage() {
130113
// if the long duration is persistent either because of slow
131114
// disk or lots of containers.
132115
longOp = longOp + time.Second
133-
klog.V(2).Infof("fs: disk usage and inodes count on following dirs took %v: %v; will not log again for this container unless duration exceeds %v", duration, []string{fh.rootfs, fh.extraDir}, longOp)
116+
klog.V(2).Infof(`fs: disk usage and inodes count on targets took %v: %v; `+
117+
`will not log again for this container unless duration exceeds %v`, duration, fh.usageProvider.Targets(), longOp)
134118
}
135119
select {
136120
case <-fh.stopChan:
@@ -153,3 +137,56 @@ func (fh *realFsHandler) Usage() FsUsage {
153137
defer fh.RUnlock()
154138
return fh.usage
155139
}
140+
141+
type fsUsageProvider struct {
142+
fsInfo fs.FsInfo
143+
rootFs string
144+
// The directory consumed by the container but outside rootFs, e.g. directory of saving logs
145+
extraDir string
146+
}
147+
148+
func NewGeneralFsUsageProvider(fsInfo fs.FsInfo, rootFs, extraDir string) FsUsageProvider {
149+
return &fsUsageProvider{
150+
fsInfo: fsInfo,
151+
rootFs: rootFs,
152+
extraDir: extraDir,
153+
}
154+
}
155+
156+
func (f *fsUsageProvider) Targets() []string {
157+
return []string{f.rootFs, f.extraDir}
158+
}
159+
160+
func (f *fsUsageProvider) Usage() (*FsUsage, error) {
161+
var (
162+
rootUsage, extraUsage fs.UsageInfo
163+
rootErr, extraErr error
164+
)
165+
166+
if f.rootFs != "" {
167+
rootUsage, rootErr = f.fsInfo.GetDirUsage(f.rootFs)
168+
}
169+
170+
if f.extraDir != "" {
171+
extraUsage, extraErr = f.fsInfo.GetDirUsage(f.extraDir)
172+
}
173+
174+
usage := &FsUsage{}
175+
176+
if f.rootFs != "" && rootErr == nil {
177+
usage.InodeUsage = rootUsage.Inodes
178+
usage.BaseUsageBytes = rootUsage.Bytes
179+
usage.TotalUsageBytes = rootUsage.Bytes
180+
}
181+
182+
if f.extraDir != "" && extraErr == nil {
183+
usage.TotalUsageBytes += extraUsage.Bytes
184+
}
185+
186+
// Combine errors into a single error to return
187+
if rootErr != nil || extraErr != nil {
188+
return nil, fmt.Errorf("failed to obtain filesystem usage; rootDiskErr: %v, extraDiskErr: %v", rootErr, extraErr)
189+
}
190+
191+
return usage, nil
192+
}

container/containerd/client.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ import (
2222
"time"
2323

2424
containersapi "github.com/containerd/containerd/api/services/containers/v1"
25+
snapshotapi "github.com/containerd/containerd/api/services/snapshots/v1"
2526
tasksapi "github.com/containerd/containerd/api/services/tasks/v1"
2627
versionapi "github.com/containerd/containerd/api/services/version/v1"
28+
types "github.com/containerd/containerd/api/types"
2729
"github.com/containerd/containerd/containers"
2830
"github.com/containerd/containerd/errdefs"
2931
"github.com/containerd/containerd/pkg/dialer"
3032
ptypes "github.com/gogo/protobuf/types"
33+
criapi "github.com/google/cadvisor/cri-api/pkg/apis/runtime/v1alpha2"
3134
"google.golang.org/grpc"
3235
"google.golang.org/grpc/backoff"
3336
)
@@ -36,12 +39,17 @@ type client struct {
3639
containerService containersapi.ContainersClient
3740
taskService tasksapi.TasksClient
3841
versionService versionapi.VersionClient
42+
snapshotService snapshotapi.SnapshotsClient
43+
criService criapi.RuntimeServiceClient
3944
}
4045

4146
type ContainerdClient interface {
4247
LoadContainer(ctx context.Context, id string) (*containers.Container, error)
4348
TaskPid(ctx context.Context, id string) (uint32, error)
4449
Version(ctx context.Context) (string, error)
50+
SnapshotMounts(ctx context.Context, snapshotter, key string) ([]*types.Mount, error)
51+
ContainerStatus(ctx context.Context, id string) (*criapi.ContainerStatus, error)
52+
ContainerStats(ctx context.Context, id string) (*criapi.ContainerStats, error)
4553
}
4654

4755
var once sync.Once
@@ -92,6 +100,8 @@ func Client(address, namespace string) (ContainerdClient, error) {
92100
containerService: containersapi.NewContainersClient(conn),
93101
taskService: tasksapi.NewTasksClient(conn),
94102
versionService: versionapi.NewVersionClient(conn),
103+
snapshotService: snapshotapi.NewSnapshotsClient(conn),
104+
criService: criapi.NewRuntimeServiceClient(conn),
95105
}
96106
})
97107
return ctrdClient, retErr
@@ -125,6 +135,38 @@ func (c *client) Version(ctx context.Context) (string, error) {
125135
return response.Version, nil
126136
}
127137

138+
func (c *client) SnapshotMounts(ctx context.Context, snapshotter, key string) ([]*types.Mount, error) {
139+
response, err := c.snapshotService.Mounts(ctx, &snapshotapi.MountsRequest{
140+
Snapshotter: snapshotter,
141+
Key: key,
142+
})
143+
if err != nil {
144+
return nil, errdefs.FromGRPC(err)
145+
}
146+
return response.Mounts, nil
147+
}
148+
149+
func (c *client) ContainerStatus(ctx context.Context, id string) (*criapi.ContainerStatus, error) {
150+
response, err := c.criService.ContainerStatus(ctx, &criapi.ContainerStatusRequest{
151+
ContainerId: id,
152+
Verbose: false,
153+
})
154+
if err != nil {
155+
return nil, err
156+
}
157+
return response.Status, nil
158+
}
159+
160+
func (c *client) ContainerStats(ctx context.Context, id string) (*criapi.ContainerStats, error) {
161+
response, err := c.criService.ContainerStats(ctx, &criapi.ContainerStatsRequest{
162+
ContainerId: id,
163+
})
164+
if err != nil {
165+
return nil, err
166+
}
167+
return response.Stats, nil
168+
}
169+
128170
func containerFromProto(containerpb containersapi.Container) *containers.Container {
129171
var runtime containers.RuntimeInfo
130172
if containerpb.Runtime != nil {

0 commit comments

Comments
 (0)