mirror of https://github.com/k3s-io/k3s
Handle image digests in node status and image GC
Start including Docker image digests in the node status and consider image digests during image garbage collection.pull/6/head
parent
660050631e
commit
f091ea5eda
|
@ -108,7 +108,7 @@ func TestPuller(t *testing.T) {
|
||||||
fakeRecorder := &record.FakeRecorder{}
|
fakeRecorder := &record.FakeRecorder{}
|
||||||
puller := NewImagePuller(fakeRecorder, fakeRuntime, backOff)
|
puller := NewImagePuller(fakeRecorder, fakeRuntime, backOff)
|
||||||
|
|
||||||
fakeRuntime.ImageList = []Image{{"present_image", nil, 0}}
|
fakeRuntime.ImageList = []Image{{"present_image", nil, nil, 1}}
|
||||||
fakeRuntime.Err = c.pullerErr
|
fakeRuntime.Err = c.pullerErr
|
||||||
fakeRuntime.InspectErr = c.inspectErr
|
fakeRuntime.InspectErr = c.inspectErr
|
||||||
|
|
||||||
|
|
|
@ -319,6 +319,8 @@ type Image struct {
|
||||||
ID string
|
ID string
|
||||||
// Other names by which this image is known.
|
// Other names by which this image is known.
|
||||||
RepoTags []string
|
RepoTags []string
|
||||||
|
// Digests by which this image is known.
|
||||||
|
RepoDigests []string
|
||||||
// The size of the image in bytes.
|
// The size of the image in bytes.
|
||||||
Size int64
|
Size int64
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ func TestSerializedPuller(t *testing.T) {
|
||||||
fakeRecorder := &record.FakeRecorder{}
|
fakeRecorder := &record.FakeRecorder{}
|
||||||
puller := NewSerializedImagePuller(fakeRecorder, fakeRuntime, backOff)
|
puller := NewSerializedImagePuller(fakeRecorder, fakeRuntime, backOff)
|
||||||
|
|
||||||
fakeRuntime.ImageList = []Image{{"present_image", nil, 0}}
|
fakeRuntime.ImageList = []Image{{"present_image", nil, nil, 0}}
|
||||||
fakeRuntime.Err = c.pullerErr
|
fakeRuntime.Err = c.pullerErr
|
||||||
fakeRuntime.InspectErr = c.inspectErr
|
fakeRuntime.InspectErr = c.inspectErr
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ func toRuntimeImage(image *dockertypes.Image) (*kubecontainer.Image, error) {
|
||||||
return &kubecontainer.Image{
|
return &kubecontainer.Image{
|
||||||
ID: image.ID,
|
ID: image.ID,
|
||||||
RepoTags: image.RepoTags,
|
RepoTags: image.RepoTags,
|
||||||
|
RepoDigests: image.RepoDigests,
|
||||||
Size: image.VirtualSize,
|
Size: image.VirtualSize,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,11 +72,13 @@ func TestToRuntimeImage(t *testing.T) {
|
||||||
original := &dockertypes.Image{
|
original := &dockertypes.Image{
|
||||||
ID: "aeeea",
|
ID: "aeeea",
|
||||||
RepoTags: []string{"abc", "def"},
|
RepoTags: []string{"abc", "def"},
|
||||||
|
RepoDigests: []string{"123", "456"},
|
||||||
VirtualSize: 1234,
|
VirtualSize: 1234,
|
||||||
}
|
}
|
||||||
expected := &kubecontainer.Image{
|
expected := &kubecontainer.Image{
|
||||||
ID: "aeeea",
|
ID: "aeeea",
|
||||||
RepoTags: []string{"abc", "def"},
|
RepoTags: []string{"abc", "def"},
|
||||||
|
RepoDigests: []string{"123", "456"},
|
||||||
Size: 1234,
|
Size: 1234,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,7 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
|
||||||
imagesInUse := sets.NewString()
|
imagesInUse := sets.NewString()
|
||||||
for _, pod := range pods {
|
for _, pod := range pods {
|
||||||
for _, container := range pod.Containers {
|
for _, container := range pod.Containers {
|
||||||
|
glog.V(5).Infof("Pod %s/%s, container %s uses image %s", pod.Namespace, pod.Name, container.Name, container.Image)
|
||||||
imagesInUse.Insert(container.Image)
|
imagesInUse.Insert(container.Image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,10 +175,12 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
|
||||||
im.imageRecordsLock.Lock()
|
im.imageRecordsLock.Lock()
|
||||||
defer im.imageRecordsLock.Unlock()
|
defer im.imageRecordsLock.Unlock()
|
||||||
for _, image := range images {
|
for _, image := range images {
|
||||||
|
glog.V(5).Infof("Adding image ID %s to currentImages", image.ID)
|
||||||
currentImages.Insert(image.ID)
|
currentImages.Insert(image.ID)
|
||||||
|
|
||||||
// New image, set it as detected now.
|
// New image, set it as detected now.
|
||||||
if _, ok := im.imageRecords[image.ID]; !ok {
|
if _, ok := im.imageRecords[image.ID]; !ok {
|
||||||
|
glog.V(5).Infof("Image ID %s is new", image.ID)
|
||||||
im.imageRecords[image.ID] = &imageRecord{
|
im.imageRecords[image.ID] = &imageRecord{
|
||||||
firstDetected: detectTime,
|
firstDetected: detectTime,
|
||||||
}
|
}
|
||||||
|
@ -185,15 +188,18 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
|
||||||
|
|
||||||
// Set last used time to now if the image is being used.
|
// Set last used time to now if the image is being used.
|
||||||
if isImageUsed(image, imagesInUse) {
|
if isImageUsed(image, imagesInUse) {
|
||||||
|
glog.V(5).Infof("Setting Image ID %s lastUsed to %v", image.ID, now)
|
||||||
im.imageRecords[image.ID].lastUsed = now
|
im.imageRecords[image.ID].lastUsed = now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glog.V(5).Infof("Image ID %s has size %d", image.ID, image.Size)
|
||||||
im.imageRecords[image.ID].size = image.Size
|
im.imageRecords[image.ID].size = image.Size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old images from our records.
|
// Remove old images from our records.
|
||||||
for image := range im.imageRecords {
|
for image := range im.imageRecords {
|
||||||
if !currentImages.Has(image) {
|
if !currentImages.Has(image) {
|
||||||
|
glog.V(5).Infof("Image ID %s is no longer present; removing from imageRecords", image)
|
||||||
delete(im.imageRecords, image)
|
delete(im.imageRecords, image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,8 +272,10 @@ func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (in
|
||||||
var lastErr error
|
var lastErr error
|
||||||
spaceFreed := int64(0)
|
spaceFreed := int64(0)
|
||||||
for _, image := range images {
|
for _, image := range images {
|
||||||
|
glog.V(5).Infof("Evaluating image ID %s for possible garbage collection", image.id)
|
||||||
// Images that are currently in used were given a newer lastUsed.
|
// Images that are currently in used were given a newer lastUsed.
|
||||||
if image.lastUsed.Equal(freeTime) || image.lastUsed.After(freeTime) {
|
if image.lastUsed.Equal(freeTime) || image.lastUsed.After(freeTime) {
|
||||||
|
glog.V(5).Infof("Image ID %s has lastUsed=%v which is >= freeTime=%v, not eligible for garbage collection", image.id, image.lastUsed, freeTime)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,6 +283,7 @@ func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (in
|
||||||
// In such a case, the image may have just been pulled down, and will be used by a container right away.
|
// In such a case, the image may have just been pulled down, and will be used by a container right away.
|
||||||
|
|
||||||
if freeTime.Sub(image.firstDetected) < im.policy.MinAge {
|
if freeTime.Sub(image.firstDetected) < im.policy.MinAge {
|
||||||
|
glog.V(5).Infof("Image ID %s has age %v which is less than the policy's minAge of %v, not eligible for garbage collection", image.id, freeTime.Sub(image.firstDetected), im.policy.MinAge)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,11 +324,11 @@ func (ev byLastUsedAndDetected) Less(i, j int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isImageUsed(image container.Image, imagesInUse sets.String) bool {
|
func isImageUsed(image container.Image, imagesInUse sets.String) bool {
|
||||||
// Check the image ID and all the RepoTags.
|
// Check the image ID and all the RepoTags and RepoDigests.
|
||||||
if _, ok := imagesInUse[image.ID]; ok {
|
if _, ok := imagesInUse[image.ID]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for _, tag := range image.RepoTags {
|
for _, tag := range append(image.RepoTags, image.RepoDigests...) {
|
||||||
if _, ok := imagesInUse[tag]; ok {
|
if _, ok := imagesInUse[tag]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,6 +337,34 @@ func TestFreeSpaceImagesAlsoDoesLookupByRepoTags(t *testing.T) {
|
||||||
assert.Len(fakeRuntime.ImageList, 1)
|
assert.Len(fakeRuntime.ImageList, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFreeSpaceImagesAlsoDoesLookupByRepoDigests(t *testing.T) {
|
||||||
|
manager, fakeRuntime, _ := newRealImageManager(ImageGCPolicy{})
|
||||||
|
fakeRuntime.ImageList = []container.Image{
|
||||||
|
makeImage(0, 1024),
|
||||||
|
{
|
||||||
|
ID: "5678",
|
||||||
|
RepoDigests: []string{"potato", "salad"},
|
||||||
|
Size: 2048,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fakeRuntime.AllPodList = []*container.Pod{
|
||||||
|
{
|
||||||
|
Containers: []*container.Container{
|
||||||
|
{
|
||||||
|
ID: container.ContainerID{Type: "test", ID: "c5678"},
|
||||||
|
Image: "salad",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceFreed, err := manager.freeSpace(1024, time.Now())
|
||||||
|
assert := assert.New(t)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.EqualValues(1024, spaceFreed)
|
||||||
|
assert.Len(fakeRuntime.ImageList, 1)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGarbageCollectBelowLowThreshold(t *testing.T) {
|
func TestGarbageCollectBelowLowThreshold(t *testing.T) {
|
||||||
policy := ImageGCPolicy{
|
policy := ImageGCPolicy{
|
||||||
HighThresholdPercent: 90,
|
HighThresholdPercent: 90,
|
||||||
|
|
|
@ -3009,7 +3009,7 @@ func (kl *Kubelet) setNodeStatusImages(node *api.Node) {
|
||||||
} else {
|
} else {
|
||||||
for _, image := range containerImages {
|
for _, image := range containerImages {
|
||||||
imagesOnNode = append(imagesOnNode, api.ContainerImage{
|
imagesOnNode = append(imagesOnNode, api.ContainerImage{
|
||||||
Names: image.RepoTags,
|
Names: append(image.RepoTags, image.RepoDigests...),
|
||||||
SizeBytes: image.Size,
|
SizeBytes: image.Size,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue