Merge pull request #29388 from ronnielai/image-gc-check

Automatic merge from submit-queue

Avoiding trying to gc images with no tags which are still in use

#29325
pull/6/head
k8s-merge-robot 2016-07-27 16:44:50 -07:00 committed by GitHub
commit ab7d039c81
7 changed files with 102 additions and 130 deletions

View File

@ -95,11 +95,12 @@ func ConvertPodStatusToRunningPod(podStatus *PodStatus) Pod {
continue continue
} }
container := &Container{ container := &Container{
ID: containerStatus.ID, ID: containerStatus.ID,
Name: containerStatus.Name, Name: containerStatus.Name,
Image: containerStatus.Image, Image: containerStatus.Image,
Hash: containerStatus.Hash, ImageID: containerStatus.ImageID,
State: containerStatus.State, Hash: containerStatus.Hash,
State: containerStatus.State,
} }
runningPod.Containers = append(runningPod.Containers, container) runningPod.Containers = append(runningPod.Containers, container)
} }

View File

@ -245,6 +245,8 @@ type Container struct {
// The image name of the container, this also includes the tag of the image, // The image name of the container, this also includes the tag of the image,
// the expected form is "NAME:TAG". // the expected form is "NAME:TAG".
Image string Image string
// The id of the image used by the container.
ImageID string
// Hash of the container, used for comparison. Optional for containers // Hash of the container, used for comparison. Optional for containers
// not managed by kubelet. // not managed by kubelet.
Hash uint64 Hash uint64

View File

@ -56,10 +56,11 @@ func toRuntimeContainer(c *dockertypes.Container) (*kubecontainer.Container, err
} }
return &kubecontainer.Container{ return &kubecontainer.Container{
ID: kubecontainer.DockerID(c.ID).ContainerID(), ID: kubecontainer.DockerID(c.ID).ContainerID(),
Name: dockerName.ContainerName, Name: dockerName.ContainerName,
Image: c.Image, Image: c.Image,
Hash: hash, ImageID: c.ImageID,
Hash: hash,
// (random-liu) docker uses status to indicate whether a container is running or exited. // (random-liu) docker uses status to indicate whether a container is running or exited.
// However, in kubernetes we usually use state to indicate whether a container is running or exited, // However, in kubernetes we usually use state to indicate whether a container is running or exited,
// while use status to indicate the comprehensive status of the container. So we have different naming // while use status to indicate the comprehensive status of the container. So we have different naming

View File

@ -170,8 +170,8 @@ 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) glog.V(5).Infof("Pod %s/%s, container %s uses image %s(%s)", pod.Namespace, pod.Name, container.Name, container.Image, container.ImageID)
imagesInUse.Insert(container.Image) imagesInUse.Insert(container.ImageID)
} }
} }
@ -341,14 +341,9 @@ 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 and RepoDigests. // Check the image ID.
if _, ok := imagesInUse[image.ID]; ok { if _, ok := imagesInUse[image.ID]; ok {
return true return true
} }
for _, tag := range append(image.RepoTags, image.RepoDigests...) {
if _, ok := imagesInUse[tag]; ok {
return true
}
}
return false return false
} }

View File

@ -59,15 +59,20 @@ func (im *realImageManager) getImageRecord(name string) (*imageRecord, bool) {
return &vCopy, ok return &vCopy, ok
} }
// Returns the id of the image with the given ID.
func imageID(id int) string {
return fmt.Sprintf("image-%d", id)
}
// Returns the name of the image with the given ID. // Returns the name of the image with the given ID.
func imageName(id int) string { func imageName(id int) string {
return fmt.Sprintf("image-%d", id) return imageID(id) + "-name"
} }
// Make an image with the specified ID. // Make an image with the specified ID.
func makeImage(id int, size int64) container.Image { func makeImage(id int, size int64) container.Image {
return container.Image{ return container.Image{
ID: imageName(id), ID: imageID(id),
Size: size, Size: size,
} }
} }
@ -75,8 +80,9 @@ func makeImage(id int, size int64) container.Image {
// Make a container with the specified ID. It will use the image with the same ID. // Make a container with the specified ID. It will use the image with the same ID.
func makeContainer(id int) *container.Container { func makeContainer(id int) *container.Container {
return &container.Container{ return &container.Container{
ID: container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", id)}, ID: container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", id)},
Image: imageName(id), Image: imageName(id),
ImageID: imageID(id),
} }
} }
@ -85,11 +91,21 @@ func TestDetectImagesInitialDetect(t *testing.T) {
fakeRuntime.ImageList = []container.Image{ fakeRuntime.ImageList = []container.Image{
makeImage(0, 1024), makeImage(0, 1024),
makeImage(1, 2048), makeImage(1, 2048),
makeImage(2, 2048),
} }
fakeRuntime.AllPodList = []*containertest.FakePod{ fakeRuntime.AllPodList = []*containertest.FakePod{
{Pod: &container.Pod{ {Pod: &container.Pod{
Containers: []*container.Container{ Containers: []*container.Container{
makeContainer(1), {
ID: container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", 1)},
ImageID: imageID(1),
// The image filed is not set to simulate a no-name image
},
{
ID: container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", 2)},
Image: imageName(2),
ImageID: imageID(2),
},
}, },
}}, }},
} }
@ -98,12 +114,16 @@ func TestDetectImagesInitialDetect(t *testing.T) {
err := manager.detectImages(zero) err := manager.detectImages(zero)
assert := assert.New(t) assert := assert.New(t)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 2) assert.Equal(manager.imageRecordsLen(), 3)
noContainer, ok := manager.getImageRecord(imageName(0)) noContainer, ok := manager.getImageRecord(imageID(0))
require.True(t, ok) require.True(t, ok)
assert.Equal(zero, noContainer.firstDetected) assert.Equal(zero, noContainer.firstDetected)
assert.Equal(zero, noContainer.lastUsed) assert.Equal(zero, noContainer.lastUsed)
withContainer, ok := manager.getImageRecord(imageName(1)) withContainerUsingNoNameImage, ok := manager.getImageRecord(imageID(1))
require.True(t, ok)
assert.Equal(zero, withContainerUsingNoNameImage.firstDetected)
assert.True(withContainerUsingNoNameImage.lastUsed.After(startTime))
withContainer, ok := manager.getImageRecord(imageID(2))
require.True(t, ok) require.True(t, ok)
assert.Equal(zero, withContainer.firstDetected) assert.Equal(zero, withContainer.firstDetected)
assert.True(withContainer.lastUsed.After(startTime)) assert.True(withContainer.lastUsed.After(startTime))
@ -141,15 +161,15 @@ func TestDetectImagesWithNewImage(t *testing.T) {
err = manager.detectImages(detectedTime) err = manager.detectImages(detectedTime)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 3) assert.Equal(manager.imageRecordsLen(), 3)
noContainer, ok := manager.getImageRecord(imageName(0)) noContainer, ok := manager.getImageRecord(imageID(0))
require.True(t, ok) require.True(t, ok)
assert.Equal(zero, noContainer.firstDetected) assert.Equal(zero, noContainer.firstDetected)
assert.Equal(zero, noContainer.lastUsed) assert.Equal(zero, noContainer.lastUsed)
withContainer, ok := manager.getImageRecord(imageName(1)) withContainer, ok := manager.getImageRecord(imageID(1))
require.True(t, ok) require.True(t, ok)
assert.Equal(zero, withContainer.firstDetected) assert.Equal(zero, withContainer.firstDetected)
assert.True(withContainer.lastUsed.After(startTime)) assert.True(withContainer.lastUsed.After(startTime))
newContainer, ok := manager.getImageRecord(imageName(2)) newContainer, ok := manager.getImageRecord(imageID(2))
require.True(t, ok) require.True(t, ok)
assert.Equal(detectedTime, newContainer.firstDetected) assert.Equal(detectedTime, newContainer.firstDetected)
assert.Equal(zero, noContainer.lastUsed) assert.Equal(zero, noContainer.lastUsed)
@ -173,7 +193,7 @@ func TestDetectImagesContainerStopped(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 2) assert.Equal(manager.imageRecordsLen(), 2)
withContainer, ok := manager.getImageRecord(imageName(1)) withContainer, ok := manager.getImageRecord(imageID(1))
require.True(t, ok) require.True(t, ok)
// Simulate container being stopped. // Simulate container being stopped.
@ -181,11 +201,11 @@ func TestDetectImagesContainerStopped(t *testing.T) {
err = manager.detectImages(time.Now()) err = manager.detectImages(time.Now())
require.NoError(t, err) require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 2) assert.Equal(manager.imageRecordsLen(), 2)
container1, ok := manager.getImageRecord(imageName(0)) container1, ok := manager.getImageRecord(imageID(0))
require.True(t, ok) require.True(t, ok)
assert.Equal(zero, container1.firstDetected) assert.Equal(zero, container1.firstDetected)
assert.Equal(zero, container1.lastUsed) assert.Equal(zero, container1.lastUsed)
container2, ok := manager.getImageRecord(imageName(1)) container2, ok := manager.getImageRecord(imageID(1))
require.True(t, ok) require.True(t, ok)
assert.Equal(zero, container2.firstDetected) assert.Equal(zero, container2.firstDetected)
assert.True(container2.lastUsed.Equal(withContainer.lastUsed)) assert.True(container2.lastUsed.Equal(withContainer.lastUsed))
@ -331,62 +351,6 @@ func TestFreeSpaceTiesBrokenByDetectedTime(t *testing.T) {
assert.Len(fakeRuntime.ImageList, 1) assert.Len(fakeRuntime.ImageList, 1)
} }
func TestFreeSpaceImagesAlsoDoesLookupByRepoTags(t *testing.T) {
manager, fakeRuntime, _ := newRealImageManager(ImageGCPolicy{})
fakeRuntime.ImageList = []container.Image{
makeImage(0, 1024),
{
ID: "5678",
RepoTags: []string{"potato", "salad"},
Size: 2048,
},
}
fakeRuntime.AllPodList = []*containertest.FakePod{
{Pod: &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 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 = []*containertest.FakePod{
{Pod: &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,

View File

@ -1528,9 +1528,10 @@ func (r *Runtime) convertRktPod(rktpod *rktapi.Pod) (*kubecontainer.Pod, error)
ID: buildContainerID(&containerID{rktpod.Id, app.Name}), ID: buildContainerID(&containerID{rktpod.Id, app.Name}),
Name: app.Name, Name: app.Name,
// By default, the version returned by rkt API service will be "latest" if not specified. // By default, the version returned by rkt API service will be "latest" if not specified.
Image: fmt.Sprintf("%s:%s", app.Image.Name, app.Image.Version), Image: fmt.Sprintf("%s:%s", app.Image.Name, app.Image.Version),
Hash: containerHash, ImageID: app.Image.Id,
State: appStateToContainerState(app.State), Hash: containerHash,
State: appStateToContainerState(app.State),
}) })
} }

View File

@ -374,18 +374,20 @@ func TestGetPods(t *testing.T) {
Namespace: "default", Namespace: "default",
Containers: []*kubecontainer.Container{ Containers: []*kubecontainer.Container{
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
Name: "app-1", Name: "app-1",
Image: "img-name-1:latest", Image: "img-name-1:latest",
Hash: 1001, ImageID: "img-id-1",
State: "running", Hash: 1001,
State: "running",
}, },
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
Name: "app-2", Name: "app-2",
Image: "img-name-2:latest", Image: "img-name-2:latest",
Hash: 1002, ImageID: "img-id-2",
State: "exited", Hash: 1002,
State: "exited",
}, },
}, },
}, },
@ -435,18 +437,20 @@ func TestGetPods(t *testing.T) {
Namespace: "default", Namespace: "default",
Containers: []*kubecontainer.Container{ Containers: []*kubecontainer.Container{
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
Name: "app-1", Name: "app-1",
Image: "img-name-1:latest", Image: "img-name-1:latest",
Hash: 1001, ImageID: "img-id-1",
State: "running", Hash: 1001,
State: "running",
}, },
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
Name: "app-2", Name: "app-2",
Image: "img-name-2:latest", Image: "img-name-2:latest",
Hash: 1002, ImageID: "img-id-2",
State: "exited", Hash: 1002,
State: "exited",
}, },
}, },
}, },
@ -456,32 +460,36 @@ func TestGetPods(t *testing.T) {
Namespace: "default", Namespace: "default",
Containers: []*kubecontainer.Container{ Containers: []*kubecontainer.Container{
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-11"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-11"),
Name: "app-11", Name: "app-11",
Image: "img-name-11:latest", Image: "img-name-11:latest",
Hash: 10011, ImageID: "img-id-11",
State: "exited", Hash: 10011,
State: "exited",
}, },
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-22"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4003:app-22"),
Name: "app-22", Name: "app-22",
Image: "img-name-22:latest", Image: "img-name-22:latest",
Hash: 10022, ImageID: "img-id-22",
State: "exited", Hash: 10022,
State: "exited",
}, },
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4004:app-11"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4004:app-11"),
Name: "app-11", Name: "app-11",
Image: "img-name-11:latest", Image: "img-name-11:latest",
Hash: 10011, ImageID: "img-id-11",
State: "running", Hash: 10011,
State: "running",
}, },
{ {
ID: kubecontainer.BuildContainerID("rkt", "uuid-4004:app-22"), ID: kubecontainer.BuildContainerID("rkt", "uuid-4004:app-22"),
Name: "app-22", Name: "app-22",
Image: "img-name-22:latest", Image: "img-name-22:latest",
Hash: 10022, ImageID: "img-id-22",
State: "running", Hash: 10022,
State: "running",
}, },
}, },
}, },