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.
This commit is contained in:
Andy Goldstein
2016-05-03 13:22:39 -04:00
parent 660050631e
commit f091ea5eda
8 changed files with 53 additions and 11 deletions

View File

@@ -108,7 +108,7 @@ func TestPuller(t *testing.T) {
fakeRecorder := &record.FakeRecorder{}
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.InspectErr = c.inspectErr

View File

@@ -319,6 +319,8 @@ type Image struct {
ID string
// Other names by which this image is known.
RepoTags []string
// Digests by which this image is known.
RepoDigests []string
// The size of the image in bytes.
Size int64
}

View File

@@ -108,7 +108,7 @@ func TestSerializedPuller(t *testing.T) {
fakeRecorder := &record.FakeRecorder{}
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.InspectErr = c.inspectErr

View File

@@ -78,6 +78,7 @@ func toRuntimeImage(image *dockertypes.Image) (*kubecontainer.Image, error) {
return &kubecontainer.Image{
ID: image.ID,
RepoTags: image.RepoTags,
RepoDigests: image.RepoDigests,
Size: image.VirtualSize,
}, nil
}

View File

@@ -72,11 +72,13 @@ func TestToRuntimeImage(t *testing.T) {
original := &dockertypes.Image{
ID: "aeeea",
RepoTags: []string{"abc", "def"},
RepoDigests: []string{"123", "456"},
VirtualSize: 1234,
}
expected := &kubecontainer.Image{
ID: "aeeea",
RepoTags: []string{"abc", "def"},
RepoDigests: []string{"123", "456"},
Size: 1234,
}

View File

@@ -164,6 +164,7 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
imagesInUse := sets.NewString()
for _, pod := range pods {
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)
}
}
@@ -174,10 +175,12 @@ func (im *realImageManager) detectImages(detectTime time.Time) error {
im.imageRecordsLock.Lock()
defer im.imageRecordsLock.Unlock()
for _, image := range images {
glog.V(5).Infof("Adding image ID %s to currentImages", image.ID)
currentImages.Insert(image.ID)
// New image, set it as detected now.
if _, ok := im.imageRecords[image.ID]; !ok {
glog.V(5).Infof("Image ID %s is new", image.ID)
im.imageRecords[image.ID] = &imageRecord{
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.
if isImageUsed(image, imagesInUse) {
glog.V(5).Infof("Setting Image ID %s lastUsed to %v", image.ID, 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
}
// Remove old images from our records.
for image := range im.imageRecords {
if !currentImages.Has(image) {
glog.V(5).Infof("Image ID %s is no longer present; removing from imageRecords", image)
delete(im.imageRecords, image)
}
}
@@ -266,8 +272,10 @@ func (im *realImageManager) freeSpace(bytesToFree int64, freeTime time.Time) (in
var lastErr error
spaceFreed := int64(0)
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.
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
}
@@ -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.
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
}
@@ -315,11 +324,11 @@ func (ev byLastUsedAndDetected) Less(i, j int) 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 {
return true
}
for _, tag := range image.RepoTags {
for _, tag := range append(image.RepoTags, image.RepoDigests...) {
if _, ok := imagesInUse[tag]; ok {
return true
}

View File

@@ -337,6 +337,34 @@ func TestFreeSpaceImagesAlsoDoesLookupByRepoTags(t *testing.T) {
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) {
policy := ImageGCPolicy{
HighThresholdPercent: 90,

View File

@@ -3009,7 +3009,7 @@ func (kl *Kubelet) setNodeStatusImages(node *api.Node) {
} else {
for _, image := range containerImages {
imagesOnNode = append(imagesOnNode, api.ContainerImage{
Names: image.RepoTags,
Names: append(image.RepoTags, image.RepoDigests...),
SizeBytes: image.Size,
})
}