mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Merge pull request #23595 from vishh/image-accounting
Automatic merge from submit-queue Collect and expose runtime's image storage usage via Kubelet's /stats/summary endpoint This information is useful to users since docker images are typically not stored on the root filesystem. Kubelet will also consume this feature in the future to decide is evicting images will help with disk usage on the nodes. cc @kubernetes/sig-node
This commit is contained in:
commit
cf38d68734
@ -46,6 +46,16 @@ type NodeStats struct {
|
||||
// Stats pertaining to total usage of filesystem resources on the rootfs used by node k8s components.
|
||||
// NodeFs.Used is the total bytes used on the filesystem.
|
||||
Fs *FsStats `json:"fs,omitempty"`
|
||||
// Stats about the underlying container runtime.
|
||||
Runtime *RuntimeStats `json:"runtime,omitempty"`
|
||||
}
|
||||
|
||||
// Stats pertaining to the underlying container runtime.
|
||||
type RuntimeStats struct {
|
||||
// Stats about the underlying filesystem where container images are stored.
|
||||
// This filesystem could be the same as the primary (root) filesystem.
|
||||
// Usage here refers to the total number of bytes occupied by images on the filesystem.
|
||||
ImageFs *FsStats `json:"imageFs,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -46,6 +46,12 @@ type ImageSpec struct {
|
||||
Image string
|
||||
}
|
||||
|
||||
// ImageStats contains statistics about all the images currently available.
|
||||
type ImageStats struct {
|
||||
// Total amount of storage consumed by existing images.
|
||||
TotalStorageBytes uint64
|
||||
}
|
||||
|
||||
// Runtime interface defines the interfaces that should be implemented
|
||||
// by a container runtime.
|
||||
// Thread safety is required from implementations of this interface.
|
||||
@ -86,6 +92,8 @@ type Runtime interface {
|
||||
ListImages() ([]Image, error)
|
||||
// Removes the specified image.
|
||||
RemoveImage(image ImageSpec) error
|
||||
// Returns Image statistics.
|
||||
ImageStats() (*ImageStats, error)
|
||||
// TODO(vmarmol): Unify pod and containerID args.
|
||||
// GetContainerLogs returns logs of a specific container. By
|
||||
// default, it returns a snapshot of the container log. Set 'follow' to true to
|
||||
|
@ -353,3 +353,11 @@ func (f *FakeRuntime) GarbageCollect(gcPolicy ContainerGCPolicy) error {
|
||||
f.CalledFunctions = append(f.CalledFunctions, "GarbageCollect")
|
||||
return f.Err
|
||||
}
|
||||
|
||||
func (f *FakeRuntime) ImageStats() (*ImageStats, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
f.CalledFunctions = append(f.CalledFunctions, "ImageStats")
|
||||
return nil, f.Err
|
||||
}
|
||||
|
@ -137,3 +137,8 @@ func (r *Mock) GarbageCollect(gcPolicy ContainerGCPolicy) error {
|
||||
args := r.Called(gcPolicy)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (r *Mock) ImageStats() (*ImageStats, error) {
|
||||
args := r.Called()
|
||||
return args.Get(0).(*ImageStats), args.Error(1)
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ type DockerInterface interface {
|
||||
ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error)
|
||||
PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error
|
||||
RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error)
|
||||
ImageHistory(id string) ([]dockertypes.ImageHistory, error)
|
||||
Logs(string, dockertypes.ContainerLogsOptions, StreamOptions) error
|
||||
Version() (*dockertypes.Version, error)
|
||||
Info() (*dockertypes.Info, error)
|
||||
|
@ -46,15 +46,16 @@ type FakeDockerClient struct {
|
||||
pulled []string
|
||||
|
||||
// Created, Stopped and Removed all container docker ID
|
||||
Created []string
|
||||
Stopped []string
|
||||
Removed []string
|
||||
RemovedImages sets.String
|
||||
VersionInfo dockertypes.Version
|
||||
Information dockertypes.Info
|
||||
ExecInspect *dockertypes.ContainerExecInspect
|
||||
execCmd []string
|
||||
EnableSleep bool
|
||||
Created []string
|
||||
Stopped []string
|
||||
Removed []string
|
||||
RemovedImages sets.String
|
||||
VersionInfo dockertypes.Version
|
||||
Information dockertypes.Info
|
||||
ExecInspect *dockertypes.ContainerExecInspect
|
||||
execCmd []string
|
||||
EnableSleep bool
|
||||
ImageHistoryMap map[string][]dockertypes.ImageHistory
|
||||
}
|
||||
|
||||
// We don't check docker version now, just set the docker version of fake docker client to 1.8.1.
|
||||
@ -482,6 +483,12 @@ func (f *FakeDockerClient) RemoveImage(image string, opts dockertypes.ImageRemov
|
||||
return []dockertypes.ImageDelete{{Deleted: image}}, err
|
||||
}
|
||||
|
||||
func (f *FakeDockerClient) InjectImages(images []dockertypes.Image) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
f.Images = append(f.Images, images...)
|
||||
}
|
||||
|
||||
func (f *FakeDockerClient) updateContainerStatus(id, status string) {
|
||||
for i := range f.RunningContainerList {
|
||||
if f.RunningContainerList[i].ID == id {
|
||||
@ -528,6 +535,18 @@ func (f *FakeDockerPuller) IsImagePresent(name string) (bool, error) {
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (f *FakeDockerClient) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
history := f.ImageHistoryMap[id]
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (f *FakeDockerClient) InjectImageHistory(data map[string][]dockertypes.ImageHistory) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
f.ImageHistoryMap = data
|
||||
}
|
||||
|
||||
// dockerTimestampToString converts the timestamp to string
|
||||
func dockerTimestampToString(t time.Time) string {
|
||||
|
71
pkg/kubelet/dockertools/images.go
Normal file
71
pkg/kubelet/dockertools/images.go
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 dockertools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
runtime "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
)
|
||||
|
||||
// imageStatsProvider exposes stats about all images currently available.
|
||||
type imageStatsProvider struct {
|
||||
// Docker remote API client
|
||||
c DockerInterface
|
||||
}
|
||||
|
||||
func (isp *imageStatsProvider) ImageStats() (*runtime.ImageStats, error) {
|
||||
images, err := isp.c.ListImages(dockertypes.ImageListOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list docker images - %v", err)
|
||||
}
|
||||
// A map of all the image layers to its corresponding size.
|
||||
imageMap := sets.NewString()
|
||||
ret := &runtime.ImageStats{}
|
||||
for _, image := range images {
|
||||
// Get information about the various layers of each docker image.
|
||||
history, err := isp.c.ImageHistory(image.ID)
|
||||
if err != nil {
|
||||
glog.V(2).Infof("failed to get history of docker image %v - %v", image, err)
|
||||
continue
|
||||
}
|
||||
// Store size information of each layer.
|
||||
for _, layer := range history {
|
||||
// Skip empty layers.
|
||||
if layer.Size == 0 {
|
||||
glog.V(10).Infof("skipping image layer %v with size 0", layer)
|
||||
continue
|
||||
}
|
||||
key := layer.ID
|
||||
// Some of the layers are empty.
|
||||
// We are hoping that these layers are unique to each image.
|
||||
// Still keying with the CreatedBy field to be safe.
|
||||
if key == "" || key == "<missing>" {
|
||||
key = key + layer.CreatedBy
|
||||
}
|
||||
if !imageMap.Has(key) {
|
||||
ret.TotalStorageBytes += uint64(layer.Size)
|
||||
}
|
||||
imageMap.Insert(key)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
103
pkg/kubelet/dockertools/images_test.go
Normal file
103
pkg/kubelet/dockertools/images_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 dockertools
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestImageStatsNoImages(t *testing.T) {
|
||||
fakeDockerClient := NewFakeDockerClientWithVersion("1.2.3", "1.2")
|
||||
isp := &imageStatsProvider{fakeDockerClient}
|
||||
st, err := isp.ImageStats()
|
||||
as := assert.New(t)
|
||||
as.NoError(err)
|
||||
as.Equal(st.TotalStorageBytes, uint64(0))
|
||||
}
|
||||
|
||||
func TestImageStatsWithImages(t *testing.T) {
|
||||
fakeDockerClient := NewFakeDockerClientWithVersion("1.2.3", "1.2")
|
||||
fakeHistoryData := map[string][]dockertypes.ImageHistory{
|
||||
"busybox": {
|
||||
{
|
||||
ID: "0123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
{
|
||||
ID: "0123457",
|
||||
CreatedBy: "duplicate",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
"kubelet": {
|
||||
{
|
||||
ID: "1123456",
|
||||
CreatedBy: "foo",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "1baz",
|
||||
Size: 400,
|
||||
},
|
||||
},
|
||||
"busybox-new": {
|
||||
{
|
||||
ID: "01234567",
|
||||
CreatedBy: "foo",
|
||||
Size: 100,
|
||||
},
|
||||
{
|
||||
ID: "0123457",
|
||||
CreatedBy: "duplicate",
|
||||
Size: 200,
|
||||
},
|
||||
{
|
||||
ID: "<missing>",
|
||||
CreatedBy: "baz",
|
||||
Size: 300,
|
||||
},
|
||||
},
|
||||
}
|
||||
fakeDockerClient.InjectImageHistory(fakeHistoryData)
|
||||
fakeDockerClient.InjectImages([]dockertypes.Image{
|
||||
{
|
||||
ID: "busybox",
|
||||
},
|
||||
{
|
||||
ID: "kubelet",
|
||||
},
|
||||
{
|
||||
ID: "busybox-new",
|
||||
},
|
||||
})
|
||||
isp := &imageStatsProvider{fakeDockerClient}
|
||||
st, err := isp.ImageStats()
|
||||
as := assert.New(t)
|
||||
as.NoError(err)
|
||||
const expectedOutput uint64 = 1300
|
||||
as.Equal(expectedOutput, st.TotalStorageBytes, "expected %d, got %d", expectedOutput, st.TotalStorageBytes)
|
||||
}
|
@ -199,3 +199,12 @@ func (in instrumentedDockerInterface) AttachToContainer(id string, opts dockerty
|
||||
recordError(operation, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in instrumentedDockerInterface) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
|
||||
const operation = "image_history"
|
||||
defer recordOperation(operation, time.Now())
|
||||
|
||||
out, err := in.client.ImageHistory(id)
|
||||
recordError(operation, err)
|
||||
return out, err
|
||||
}
|
||||
|
@ -151,6 +151,10 @@ func (d *kubeDockerClient) InspectImage(image string) (*dockertypes.ImageInspect
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (d *kubeDockerClient) ImageHistory(id string) ([]dockertypes.ImageHistory, error) {
|
||||
return d.client.ImageHistory(getDefaultContext(), id)
|
||||
}
|
||||
|
||||
func (d *kubeDockerClient) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) {
|
||||
images, err := d.client.ImageList(getDefaultContext(), opts)
|
||||
if err != nil {
|
||||
|
@ -163,6 +163,9 @@ type DockerManager struct {
|
||||
|
||||
// The api version cache of docker daemon.
|
||||
versionCache *cache.VersionCache
|
||||
|
||||
// Provides image stats
|
||||
*imageStatsProvider
|
||||
}
|
||||
|
||||
// A subset of the pod.Manager interface extracted for testing purposes.
|
||||
@ -240,6 +243,7 @@ func NewDockerManager(
|
||||
cpuCFSQuota: cpuCFSQuota,
|
||||
enableCustomMetrics: enableCustomMetrics,
|
||||
configureHairpinMode: hairpinMode,
|
||||
imageStatsProvider: &imageStatsProvider{client},
|
||||
}
|
||||
dm.runner = lifecycle.NewHandlerRunner(httpClient, dm, dm)
|
||||
if serializeImagePulls {
|
||||
|
@ -342,8 +342,6 @@ func NewMainKubelet(
|
||||
enableCustomMetrics: enableCustomMetrics,
|
||||
babysitDaemons: babysitDaemons,
|
||||
}
|
||||
// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
|
||||
klet.resourceAnalyzer = stats.NewResourceAnalyzer(klet, volumeStatsAggPeriod)
|
||||
|
||||
if klet.flannelExperimentalOverlay {
|
||||
glog.Infof("Flannel is in charge of podCIDR and overlay networking.")
|
||||
@ -440,6 +438,9 @@ func NewMainKubelet(
|
||||
return nil, fmt.Errorf("unsupported container runtime %q specified", containerRuntime)
|
||||
}
|
||||
|
||||
// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
|
||||
klet.resourceAnalyzer = stats.NewResourceAnalyzer(klet, volumeStatsAggPeriod, klet.containerRuntime)
|
||||
|
||||
klet.pleg = pleg.NewGenericPLEG(klet.containerRuntime, plegChannelCapacity, plegRelistPeriod, klet.podCache, util.RealClock{})
|
||||
klet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, configureCBR0)
|
||||
klet.updatePodCIDR(podCIDR)
|
||||
@ -3579,11 +3580,11 @@ func (kl *Kubelet) GetCachedMachineInfo() (*cadvisorapi.MachineInfo, error) {
|
||||
}
|
||||
|
||||
func (kl *Kubelet) ListenAndServe(address net.IP, port uint, tlsOptions *server.TLSOptions, auth server.AuthInterface, enableDebuggingHandlers bool) {
|
||||
server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, address, port, tlsOptions, auth, enableDebuggingHandlers)
|
||||
server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, address, port, tlsOptions, auth, enableDebuggingHandlers, kl.containerRuntime)
|
||||
}
|
||||
|
||||
func (kl *Kubelet) ListenAndServeReadOnly(address net.IP, port uint) {
|
||||
server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, address, port)
|
||||
server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, address, port, kl.containerRuntime)
|
||||
}
|
||||
|
||||
// GetRuntime returns the current Runtime implementation in use by the kubelet. This func
|
||||
|
@ -1696,3 +1696,8 @@ func (r *Runtime) GetPodStatus(uid types.UID, name, namespace string) (*kubecont
|
||||
|
||||
return podStatus, nil
|
||||
}
|
||||
|
||||
// FIXME: I need to be implemented.
|
||||
func (r *Runtime) ImageStats() (*kubecontainer.ImageStats, error) {
|
||||
return &kubecontainer.ImageStats{}, nil
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ type Server struct {
|
||||
host HostInterface
|
||||
restfulCont containerInterface
|
||||
resourceAnalyzer stats.ResourceAnalyzer
|
||||
runtime kubecontainer.Runtime
|
||||
}
|
||||
|
||||
type TLSOptions struct {
|
||||
@ -104,9 +105,17 @@ func (a *filteringContainer) RegisteredHandlePaths() []string {
|
||||
}
|
||||
|
||||
// ListenAndServeKubeletServer initializes a server to respond to HTTP network requests on the Kubelet.
|
||||
func ListenAndServeKubeletServer(host HostInterface, resourceAnalyzer stats.ResourceAnalyzer, address net.IP, port uint, tlsOptions *TLSOptions, auth AuthInterface, enableDebuggingHandlers bool) {
|
||||
func ListenAndServeKubeletServer(
|
||||
host HostInterface,
|
||||
resourceAnalyzer stats.ResourceAnalyzer,
|
||||
address net.IP,
|
||||
port uint,
|
||||
tlsOptions *TLSOptions,
|
||||
auth AuthInterface,
|
||||
enableDebuggingHandlers bool,
|
||||
runtime kubecontainer.Runtime) {
|
||||
glog.Infof("Starting to listen on %s:%d", address, port)
|
||||
handler := NewServer(host, resourceAnalyzer, auth, enableDebuggingHandlers)
|
||||
handler := NewServer(host, resourceAnalyzer, auth, enableDebuggingHandlers, runtime)
|
||||
s := &http.Server{
|
||||
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
|
||||
Handler: &handler,
|
||||
@ -121,9 +130,9 @@ func ListenAndServeKubeletServer(host HostInterface, resourceAnalyzer stats.Reso
|
||||
}
|
||||
|
||||
// ListenAndServeKubeletReadOnlyServer initializes a server to respond to HTTP network requests on the Kubelet.
|
||||
func ListenAndServeKubeletReadOnlyServer(host HostInterface, resourceAnalyzer stats.ResourceAnalyzer, address net.IP, port uint) {
|
||||
func ListenAndServeKubeletReadOnlyServer(host HostInterface, resourceAnalyzer stats.ResourceAnalyzer, address net.IP, port uint, runtime kubecontainer.Runtime) {
|
||||
glog.V(1).Infof("Starting to listen read-only on %s:%d", address, port)
|
||||
s := NewServer(host, resourceAnalyzer, nil, false)
|
||||
s := NewServer(host, resourceAnalyzer, nil, false, runtime)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
|
||||
@ -169,12 +178,18 @@ type HostInterface interface {
|
||||
}
|
||||
|
||||
// NewServer initializes and configures a kubelet.Server object to handle HTTP requests.
|
||||
func NewServer(host HostInterface, resourceAnalyzer stats.ResourceAnalyzer, auth AuthInterface, enableDebuggingHandlers bool) Server {
|
||||
func NewServer(
|
||||
host HostInterface,
|
||||
resourceAnalyzer stats.ResourceAnalyzer,
|
||||
auth AuthInterface,
|
||||
enableDebuggingHandlers bool,
|
||||
runtime kubecontainer.Runtime) Server {
|
||||
server := Server{
|
||||
host: host,
|
||||
resourceAnalyzer: resourceAnalyzer,
|
||||
auth: auth,
|
||||
restfulCont: &filteringContainer{Container: restful.NewContainer()},
|
||||
runtime: runtime,
|
||||
}
|
||||
if auth != nil {
|
||||
server.InstallAuthFilter()
|
||||
|
@ -41,6 +41,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
kubecontainertesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/types"
|
||||
@ -208,9 +209,10 @@ func newServerTest() *serverTestFramework {
|
||||
}
|
||||
server := NewServer(
|
||||
fw.fakeKubelet,
|
||||
stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute),
|
||||
stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute, &kubecontainertesting.FakeRuntime{}),
|
||||
fw.fakeAuth,
|
||||
true)
|
||||
true,
|
||||
&kubecontainertesting.Mock{})
|
||||
fw.serverUnderTest = &server
|
||||
// TODO: Close() this when fix #19254
|
||||
fw.testHTTPServer = httptest.NewServer(fw.serverUnderTest)
|
||||
|
@ -16,7 +16,11 @@ limitations under the License.
|
||||
|
||||
package stats
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||
)
|
||||
|
||||
// ResourceAnalyzer provides statistics on node resource consumption
|
||||
type ResourceAnalyzer interface {
|
||||
@ -35,9 +39,9 @@ type resourceAnalyzer struct {
|
||||
var _ ResourceAnalyzer = &resourceAnalyzer{}
|
||||
|
||||
// NewResourceAnalyzer returns a new ResourceAnalyzer
|
||||
func NewResourceAnalyzer(statsProvider StatsProvider, calVolumeFrequency time.Duration) ResourceAnalyzer {
|
||||
func NewResourceAnalyzer(statsProvider StatsProvider, calVolumeFrequency time.Duration, runtime container.Runtime) ResourceAnalyzer {
|
||||
fsAnalyzer := newFsResourceAnalyzer(statsProvider, calVolumeFrequency)
|
||||
summaryProvider := NewSummaryProvider(statsProvider, fsAnalyzer)
|
||||
summaryProvider := NewSummaryProvider(statsProvider, fsAnalyzer, runtime)
|
||||
return &resourceAnalyzer{fsAnalyzer, summaryProvider}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/leaky"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||
@ -45,15 +46,16 @@ type SummaryProvider interface {
|
||||
type summaryProviderImpl struct {
|
||||
provider StatsProvider
|
||||
fsResourceAnalyzer fsResourceAnalyzerInterface
|
||||
runtime container.Runtime
|
||||
}
|
||||
|
||||
var _ SummaryProvider = &summaryProviderImpl{}
|
||||
|
||||
// NewSummaryProvider returns a new SummaryProvider
|
||||
func NewSummaryProvider(statsProvider StatsProvider, fsResourceAnalyzer fsResourceAnalyzerInterface) SummaryProvider {
|
||||
func NewSummaryProvider(statsProvider StatsProvider, fsResourceAnalyzer fsResourceAnalyzerInterface, cruntime container.Runtime) SummaryProvider {
|
||||
stackBuff := []byte{}
|
||||
runtime.Stack(stackBuff, false)
|
||||
return &summaryProviderImpl{statsProvider, fsResourceAnalyzer}
|
||||
return &summaryProviderImpl{statsProvider, fsResourceAnalyzer, cruntime}
|
||||
}
|
||||
|
||||
// Get implements the SummaryProvider interface
|
||||
@ -83,8 +85,11 @@ func (sp *summaryProviderImpl) Get() (*stats.Summary, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sb := &summaryBuilder{sp.fsResourceAnalyzer, node, nodeConfig, rootFsInfo, imageFsInfo, infos}
|
||||
imageStats, err := sp.runtime.ImageStats()
|
||||
if err != nil || imageStats == nil {
|
||||
return nil, err
|
||||
}
|
||||
sb := &summaryBuilder{sp.fsResourceAnalyzer, node, nodeConfig, rootFsInfo, imageFsInfo, *imageStats, infos}
|
||||
return sb.build()
|
||||
}
|
||||
|
||||
@ -95,6 +100,7 @@ type summaryBuilder struct {
|
||||
nodeConfig cm.NodeConfig
|
||||
rootFsInfo cadvisorapiv2.FsInfo
|
||||
imageFsInfo cadvisorapiv2.FsInfo
|
||||
imageStats container.ImageStats
|
||||
infos map[string]cadvisorapiv2.ContainerInfo
|
||||
}
|
||||
|
||||
@ -116,6 +122,13 @@ func (sb *summaryBuilder) build() (*stats.Summary, error) {
|
||||
CapacityBytes: &sb.rootFsInfo.Capacity,
|
||||
UsedBytes: &sb.rootFsInfo.Usage},
|
||||
StartTime: rootStats.StartTime,
|
||||
Runtime: &stats.RuntimeStats{
|
||||
ImageFs: &stats.FsStats{
|
||||
AvailableBytes: &sb.imageFsInfo.Available,
|
||||
CapacityBytes: &sb.imageFsInfo.Capacity,
|
||||
UsedBytes: &sb.imageStats.TotalStorageBytes,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
systemContainers := map[string]string{
|
||||
@ -152,7 +165,6 @@ func (sb *summaryBuilder) containerInfoV2FsStats(
|
||||
AvailableBytes: &sb.imageFsInfo.Available,
|
||||
CapacityBytes: &sb.imageFsInfo.Capacity,
|
||||
}
|
||||
|
||||
lcs, found := sb.latestContainerStats(info)
|
||||
if !found {
|
||||
return
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
kubestats "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/kubelet/leaky"
|
||||
)
|
||||
|
||||
@ -127,7 +128,7 @@ func TestBuildSummary(t *testing.T) {
|
||||
}
|
||||
|
||||
sb := &summaryBuilder{
|
||||
newFsResourceAnalyzer(&MockStatsProvider{}, time.Minute*5), &node, nodeConfig, rootfs, imagefs, infos}
|
||||
newFsResourceAnalyzer(&MockStatsProvider{}, time.Minute*5), &node, nodeConfig, rootfs, imagefs, container.ImageStats{}, infos}
|
||||
summary, err := sb.build()
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
@ -203,6 +203,16 @@ var _ = Describe("Kubelet", func() {
|
||||
Expect(summary.Node.Fs.UsedBytes).NotTo(BeNil())
|
||||
Expect(*summary.Node.Fs.UsedBytes).NotTo(BeZero())
|
||||
|
||||
By("Having container runtime's image storage information")
|
||||
Expect(summary.Node.Runtime).NotTo(BeNil())
|
||||
Expect(summary.Node.Runtime.ImageFs).NotTo(BeNil())
|
||||
Expect(summary.Node.Runtime.ImageFs.AvailableBytes).NotTo(BeNil())
|
||||
Expect(*summary.Node.Runtime.ImageFs.AvailableBytes).NotTo(BeZero())
|
||||
Expect(summary.Node.Runtime.ImageFs.CapacityBytes).NotTo(BeNil())
|
||||
Expect(*summary.Node.Runtime.ImageFs.CapacityBytes).NotTo(BeZero())
|
||||
Expect(summary.Node.Runtime.ImageFs.UsedBytes).NotTo(BeNil())
|
||||
Expect(*summary.Node.Runtime.ImageFs.UsedBytes).NotTo(BeZero())
|
||||
|
||||
By("Having resources for kubelet and runtime system containers")
|
||||
sysContainers := map[string]stats.ContainerStats{}
|
||||
sysContainersList := []string{}
|
||||
|
Loading…
Reference in New Issue
Block a user