mirror of https://github.com/k3s-io/k3s
Track image storage usage for docker containers
add image fs info to summary stats API. Adding node e2e test for image stats. Signed-off-by: Vishnu kannan <vishnuk@google.com>pull/6/head
parent
596c96da8a
commit
e566948a75
|
@ -46,6 +46,16 @@ type NodeStats struct {
|
||||||
// Stats pertaining to total usage of filesystem resources on the rootfs used by node k8s components.
|
// 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.
|
// NodeFs.Used is the total bytes used on the filesystem.
|
||||||
Fs *FsStats `json:"fs,omitempty"`
|
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 (
|
const (
|
||||||
|
|
|
@ -46,6 +46,12 @@ type ImageSpec struct {
|
||||||
Image string
|
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
|
// Runtime interface defines the interfaces that should be implemented
|
||||||
// by a container runtime.
|
// by a container runtime.
|
||||||
// Thread safety is required from implementations of this interface.
|
// Thread safety is required from implementations of this interface.
|
||||||
|
@ -86,6 +92,8 @@ type Runtime interface {
|
||||||
ListImages() ([]Image, error)
|
ListImages() ([]Image, error)
|
||||||
// Removes the specified image.
|
// Removes the specified image.
|
||||||
RemoveImage(image ImageSpec) error
|
RemoveImage(image ImageSpec) error
|
||||||
|
// Returns Image statistics.
|
||||||
|
ImageStats() (*ImageStats, error)
|
||||||
// TODO(vmarmol): Unify pod and containerID args.
|
// TODO(vmarmol): Unify pod and containerID args.
|
||||||
// GetContainerLogs returns logs of a specific container. By
|
// GetContainerLogs returns logs of a specific container. By
|
||||||
// default, it returns a snapshot of the container log. Set 'follow' to true to
|
// 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")
|
f.CalledFunctions = append(f.CalledFunctions, "GarbageCollect")
|
||||||
return f.Err
|
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)
|
args := r.Called(gcPolicy)
|
||||||
return args.Error(0)
|
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)
|
ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error)
|
||||||
PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error
|
PullImage(image string, auth dockertypes.AuthConfig, opts dockertypes.ImagePullOptions) error
|
||||||
RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error)
|
RemoveImage(image string, opts dockertypes.ImageRemoveOptions) ([]dockertypes.ImageDelete, error)
|
||||||
|
ImageHistory(id string) ([]dockertypes.ImageHistory, error)
|
||||||
Logs(string, dockertypes.ContainerLogsOptions, StreamOptions) error
|
Logs(string, dockertypes.ContainerLogsOptions, StreamOptions) error
|
||||||
Version() (*dockertypes.Version, error)
|
Version() (*dockertypes.Version, error)
|
||||||
Info() (*dockertypes.Info, error)
|
Info() (*dockertypes.Info, error)
|
||||||
|
|
|
@ -46,15 +46,16 @@ type FakeDockerClient struct {
|
||||||
pulled []string
|
pulled []string
|
||||||
|
|
||||||
// Created, Stopped and Removed all container docker ID
|
// Created, Stopped and Removed all container docker ID
|
||||||
Created []string
|
Created []string
|
||||||
Stopped []string
|
Stopped []string
|
||||||
Removed []string
|
Removed []string
|
||||||
RemovedImages sets.String
|
RemovedImages sets.String
|
||||||
VersionInfo dockertypes.Version
|
VersionInfo dockertypes.Version
|
||||||
Information dockertypes.Info
|
Information dockertypes.Info
|
||||||
ExecInspect *dockertypes.ContainerExecInspect
|
ExecInspect *dockertypes.ContainerExecInspect
|
||||||
execCmd []string
|
execCmd []string
|
||||||
EnableSleep bool
|
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.
|
// 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
|
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) {
|
func (f *FakeDockerClient) updateContainerStatus(id, status string) {
|
||||||
for i := range f.RunningContainerList {
|
for i := range f.RunningContainerList {
|
||||||
if f.RunningContainerList[i].ID == id {
|
if f.RunningContainerList[i].ID == id {
|
||||||
|
@ -528,6 +535,18 @@ func (f *FakeDockerPuller) IsImagePresent(name string) (bool, error) {
|
||||||
}
|
}
|
||||||
return false, nil
|
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
|
// dockerTimestampToString converts the timestamp to string
|
||||||
func dockerTimestampToString(t time.Time) string {
|
func dockerTimestampToString(t time.Time) string {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
recordError(operation, err)
|
||||||
return 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
|
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) {
|
func (d *kubeDockerClient) ListImages(opts dockertypes.ImageListOptions) ([]dockertypes.Image, error) {
|
||||||
images, err := d.client.ImageList(getDefaultContext(), opts)
|
images, err := d.client.ImageList(getDefaultContext(), opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -163,6 +163,9 @@ type DockerManager struct {
|
||||||
|
|
||||||
// The api version cache of docker daemon.
|
// The api version cache of docker daemon.
|
||||||
versionCache *cache.VersionCache
|
versionCache *cache.VersionCache
|
||||||
|
|
||||||
|
// Provides image stats
|
||||||
|
*imageStatsProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// A subset of the pod.Manager interface extracted for testing purposes.
|
// A subset of the pod.Manager interface extracted for testing purposes.
|
||||||
|
@ -240,6 +243,7 @@ func NewDockerManager(
|
||||||
cpuCFSQuota: cpuCFSQuota,
|
cpuCFSQuota: cpuCFSQuota,
|
||||||
enableCustomMetrics: enableCustomMetrics,
|
enableCustomMetrics: enableCustomMetrics,
|
||||||
configureHairpinMode: hairpinMode,
|
configureHairpinMode: hairpinMode,
|
||||||
|
imageStatsProvider: &imageStatsProvider{client},
|
||||||
}
|
}
|
||||||
dm.runner = lifecycle.NewHandlerRunner(httpClient, dm, dm)
|
dm.runner = lifecycle.NewHandlerRunner(httpClient, dm, dm)
|
||||||
if serializeImagePulls {
|
if serializeImagePulls {
|
||||||
|
|
|
@ -342,8 +342,6 @@ func NewMainKubelet(
|
||||||
enableCustomMetrics: enableCustomMetrics,
|
enableCustomMetrics: enableCustomMetrics,
|
||||||
babysitDaemons: babysitDaemons,
|
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 {
|
if klet.flannelExperimentalOverlay {
|
||||||
glog.Infof("Flannel is in charge of podCIDR and overlay networking.")
|
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)
|
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.pleg = pleg.NewGenericPLEG(klet.containerRuntime, plegChannelCapacity, plegRelistPeriod, klet.podCache, util.RealClock{})
|
||||||
klet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, configureCBR0)
|
klet.runtimeState = newRuntimeState(maxWaitForContainerRuntime, configureCBR0)
|
||||||
klet.updatePodCIDR(podCIDR)
|
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) {
|
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) {
|
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
|
// GetRuntime returns the current Runtime implementation in use by the kubelet. This func
|
||||||
|
|
|
@ -1695,3 +1695,8 @@ func (r *Runtime) GetPodStatus(uid types.UID, name, namespace string) (*kubecont
|
||||||
|
|
||||||
return podStatus, nil
|
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
|
host HostInterface
|
||||||
restfulCont containerInterface
|
restfulCont containerInterface
|
||||||
resourceAnalyzer stats.ResourceAnalyzer
|
resourceAnalyzer stats.ResourceAnalyzer
|
||||||
|
runtime kubecontainer.Runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
type TLSOptions struct {
|
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.
|
// 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)
|
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{
|
s := &http.Server{
|
||||||
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
|
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
|
||||||
Handler: &handler,
|
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.
|
// 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)
|
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{
|
server := &http.Server{
|
||||||
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
|
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.
|
// 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{
|
server := Server{
|
||||||
host: host,
|
host: host,
|
||||||
resourceAnalyzer: resourceAnalyzer,
|
resourceAnalyzer: resourceAnalyzer,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
restfulCont: &filteringContainer{Container: restful.NewContainer()},
|
restfulCont: &filteringContainer{Container: restful.NewContainer()},
|
||||||
|
runtime: runtime,
|
||||||
}
|
}
|
||||||
if auth != nil {
|
if auth != nil {
|
||||||
server.InstallAuthFilter()
|
server.InstallAuthFilter()
|
||||||
|
|
|
@ -41,6 +41,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/auth/user"
|
"k8s.io/kubernetes/pkg/auth/user"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
kubecontainertesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
||||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
"k8s.io/kubernetes/pkg/types"
|
"k8s.io/kubernetes/pkg/types"
|
||||||
|
@ -208,9 +209,10 @@ func newServerTest() *serverTestFramework {
|
||||||
}
|
}
|
||||||
server := NewServer(
|
server := NewServer(
|
||||||
fw.fakeKubelet,
|
fw.fakeKubelet,
|
||||||
stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute),
|
stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute, &kubecontainertesting.FakeRuntime{}),
|
||||||
fw.fakeAuth,
|
fw.fakeAuth,
|
||||||
true)
|
true,
|
||||||
|
&kubecontainertesting.Mock{})
|
||||||
fw.serverUnderTest = &server
|
fw.serverUnderTest = &server
|
||||||
// TODO: Close() this when fix #19254
|
// TODO: Close() this when fix #19254
|
||||||
fw.testHTTPServer = httptest.NewServer(fw.serverUnderTest)
|
fw.testHTTPServer = httptest.NewServer(fw.serverUnderTest)
|
||||||
|
|
|
@ -16,7 +16,11 @@ limitations under the License.
|
||||||
|
|
||||||
package stats
|
package stats
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
)
|
||||||
|
|
||||||
// ResourceAnalyzer provides statistics on node resource consumption
|
// ResourceAnalyzer provides statistics on node resource consumption
|
||||||
type ResourceAnalyzer interface {
|
type ResourceAnalyzer interface {
|
||||||
|
@ -35,9 +39,9 @@ type resourceAnalyzer struct {
|
||||||
var _ ResourceAnalyzer = &resourceAnalyzer{}
|
var _ ResourceAnalyzer = &resourceAnalyzer{}
|
||||||
|
|
||||||
// NewResourceAnalyzer returns a new 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)
|
fsAnalyzer := newFsResourceAnalyzer(statsProvider, calVolumeFrequency)
|
||||||
summaryProvider := NewSummaryProvider(statsProvider, fsAnalyzer)
|
summaryProvider := NewSummaryProvider(statsProvider, fsAnalyzer, runtime)
|
||||||
return &resourceAnalyzer{fsAnalyzer, summaryProvider}
|
return &resourceAnalyzer{fsAnalyzer, summaryProvider}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
|
"k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/leaky"
|
"k8s.io/kubernetes/pkg/kubelet/leaky"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||||
|
@ -45,15 +46,16 @@ type SummaryProvider interface {
|
||||||
type summaryProviderImpl struct {
|
type summaryProviderImpl struct {
|
||||||
provider StatsProvider
|
provider StatsProvider
|
||||||
fsResourceAnalyzer fsResourceAnalyzerInterface
|
fsResourceAnalyzer fsResourceAnalyzerInterface
|
||||||
|
runtime container.Runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ SummaryProvider = &summaryProviderImpl{}
|
var _ SummaryProvider = &summaryProviderImpl{}
|
||||||
|
|
||||||
// NewSummaryProvider returns a new SummaryProvider
|
// NewSummaryProvider returns a new SummaryProvider
|
||||||
func NewSummaryProvider(statsProvider StatsProvider, fsResourceAnalyzer fsResourceAnalyzerInterface) SummaryProvider {
|
func NewSummaryProvider(statsProvider StatsProvider, fsResourceAnalyzer fsResourceAnalyzerInterface, cruntime container.Runtime) SummaryProvider {
|
||||||
stackBuff := []byte{}
|
stackBuff := []byte{}
|
||||||
runtime.Stack(stackBuff, false)
|
runtime.Stack(stackBuff, false)
|
||||||
return &summaryProviderImpl{statsProvider, fsResourceAnalyzer}
|
return &summaryProviderImpl{statsProvider, fsResourceAnalyzer, cruntime}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get implements the SummaryProvider interface
|
// Get implements the SummaryProvider interface
|
||||||
|
@ -83,8 +85,11 @@ func (sp *summaryProviderImpl) Get() (*stats.Summary, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
imageStats, err := sp.runtime.ImageStats()
|
||||||
sb := &summaryBuilder{sp.fsResourceAnalyzer, node, nodeConfig, rootFsInfo, imageFsInfo, infos}
|
if err != nil || imageStats == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sb := &summaryBuilder{sp.fsResourceAnalyzer, node, nodeConfig, rootFsInfo, imageFsInfo, *imageStats, infos}
|
||||||
return sb.build()
|
return sb.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +100,7 @@ type summaryBuilder struct {
|
||||||
nodeConfig cm.NodeConfig
|
nodeConfig cm.NodeConfig
|
||||||
rootFsInfo cadvisorapiv2.FsInfo
|
rootFsInfo cadvisorapiv2.FsInfo
|
||||||
imageFsInfo cadvisorapiv2.FsInfo
|
imageFsInfo cadvisorapiv2.FsInfo
|
||||||
|
imageStats container.ImageStats
|
||||||
infos map[string]cadvisorapiv2.ContainerInfo
|
infos map[string]cadvisorapiv2.ContainerInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +122,13 @@ func (sb *summaryBuilder) build() (*stats.Summary, error) {
|
||||||
CapacityBytes: &sb.rootFsInfo.Capacity,
|
CapacityBytes: &sb.rootFsInfo.Capacity,
|
||||||
UsedBytes: &sb.rootFsInfo.Usage},
|
UsedBytes: &sb.rootFsInfo.Usage},
|
||||||
StartTime: rootStats.StartTime,
|
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{
|
systemContainers := map[string]string{
|
||||||
|
@ -152,7 +165,6 @@ func (sb *summaryBuilder) containerInfoV2FsStats(
|
||||||
AvailableBytes: &sb.imageFsInfo.Available,
|
AvailableBytes: &sb.imageFsInfo.Available,
|
||||||
CapacityBytes: &sb.imageFsInfo.Capacity,
|
CapacityBytes: &sb.imageFsInfo.Capacity,
|
||||||
}
|
}
|
||||||
|
|
||||||
lcs, found := sb.latestContainerStats(info)
|
lcs, found := sb.latestContainerStats(info)
|
||||||
if !found {
|
if !found {
|
||||||
return
|
return
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
kubestats "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
|
kubestats "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/stats"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/leaky"
|
"k8s.io/kubernetes/pkg/kubelet/leaky"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -127,7 +128,7 @@ func TestBuildSummary(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sb := &summaryBuilder{
|
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()
|
summary, err := sb.build()
|
||||||
|
|
||||||
assert.NoError(t, err)
|
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(BeNil())
|
||||||
Expect(*summary.Node.Fs.UsedBytes).NotTo(BeZero())
|
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")
|
By("Having resources for kubelet and runtime system containers")
|
||||||
sysContainers := map[string]stats.ContainerStats{}
|
sysContainers := map[string]stats.ContainerStats{}
|
||||||
sysContainersList := []string{}
|
sysContainersList := []string{}
|
||||||
|
|
Loading…
Reference in New Issue