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
}
container := &Container{
ID: containerStatus.ID,
Name: containerStatus.Name,
Image: containerStatus.Image,
Hash: containerStatus.Hash,
State: containerStatus.State,
ID: containerStatus.ID,
Name: containerStatus.Name,
Image: containerStatus.Image,
ImageID: containerStatus.ImageID,
Hash: containerStatus.Hash,
State: containerStatus.State,
}
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 expected form is "NAME:TAG".
Image string
// The id of the image used by the container.
ImageID string
// Hash of the container, used for comparison. Optional for containers
// not managed by kubelet.
Hash uint64

View File

@ -56,10 +56,11 @@ func toRuntimeContainer(c *dockertypes.Container) (*kubecontainer.Container, err
}
return &kubecontainer.Container{
ID: kubecontainer.DockerID(c.ID).ContainerID(),
Name: dockerName.ContainerName,
Image: c.Image,
Hash: hash,
ID: kubecontainer.DockerID(c.ID).ContainerID(),
Name: dockerName.ContainerName,
Image: c.Image,
ImageID: c.ImageID,
Hash: hash,
// (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,
// 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()
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)
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.ImageID)
}
}
@ -341,14 +341,9 @@ 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 and RepoDigests.
// Check the image ID.
if _, ok := imagesInUse[image.ID]; ok {
return true
}
for _, tag := range append(image.RepoTags, image.RepoDigests...) {
if _, ok := imagesInUse[tag]; ok {
return true
}
}
return false
}

View File

@ -59,15 +59,20 @@ func (im *realImageManager) getImageRecord(name string) (*imageRecord, bool) {
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.
func imageName(id int) string {
return fmt.Sprintf("image-%d", id)
return imageID(id) + "-name"
}
// Make an image with the specified ID.
func makeImage(id int, size int64) container.Image {
return container.Image{
ID: imageName(id),
ID: imageID(id),
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.
func makeContainer(id int) *container.Container {
return &container.Container{
ID: container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", id)},
Image: imageName(id),
ID: container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", id)},
Image: imageName(id),
ImageID: imageID(id),
}
}
@ -85,11 +91,21 @@ func TestDetectImagesInitialDetect(t *testing.T) {
fakeRuntime.ImageList = []container.Image{
makeImage(0, 1024),
makeImage(1, 2048),
makeImage(2, 2048),
}
fakeRuntime.AllPodList = []*containertest.FakePod{
{Pod: &container.Pod{
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)
assert := assert.New(t)
require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 2)
noContainer, ok := manager.getImageRecord(imageName(0))
assert.Equal(manager.imageRecordsLen(), 3)
noContainer, ok := manager.getImageRecord(imageID(0))
require.True(t, ok)
assert.Equal(zero, noContainer.firstDetected)
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)
assert.Equal(zero, withContainer.firstDetected)
assert.True(withContainer.lastUsed.After(startTime))
@ -141,15 +161,15 @@ func TestDetectImagesWithNewImage(t *testing.T) {
err = manager.detectImages(detectedTime)
require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 3)
noContainer, ok := manager.getImageRecord(imageName(0))
noContainer, ok := manager.getImageRecord(imageID(0))
require.True(t, ok)
assert.Equal(zero, noContainer.firstDetected)
assert.Equal(zero, noContainer.lastUsed)
withContainer, ok := manager.getImageRecord(imageName(1))
withContainer, ok := manager.getImageRecord(imageID(1))
require.True(t, ok)
assert.Equal(zero, withContainer.firstDetected)
assert.True(withContainer.lastUsed.After(startTime))
newContainer, ok := manager.getImageRecord(imageName(2))
newContainer, ok := manager.getImageRecord(imageID(2))
require.True(t, ok)
assert.Equal(detectedTime, newContainer.firstDetected)
assert.Equal(zero, noContainer.lastUsed)
@ -173,7 +193,7 @@ func TestDetectImagesContainerStopped(t *testing.T) {
assert := assert.New(t)
require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 2)
withContainer, ok := manager.getImageRecord(imageName(1))
withContainer, ok := manager.getImageRecord(imageID(1))
require.True(t, ok)
// Simulate container being stopped.
@ -181,11 +201,11 @@ func TestDetectImagesContainerStopped(t *testing.T) {
err = manager.detectImages(time.Now())
require.NoError(t, err)
assert.Equal(manager.imageRecordsLen(), 2)
container1, ok := manager.getImageRecord(imageName(0))
container1, ok := manager.getImageRecord(imageID(0))
require.True(t, ok)
assert.Equal(zero, container1.firstDetected)
assert.Equal(zero, container1.lastUsed)
container2, ok := manager.getImageRecord(imageName(1))
container2, ok := manager.getImageRecord(imageID(1))
require.True(t, ok)
assert.Equal(zero, container2.firstDetected)
assert.True(container2.lastUsed.Equal(withContainer.lastUsed))
@ -331,62 +351,6 @@ func TestFreeSpaceTiesBrokenByDetectedTime(t *testing.T) {
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) {
policy := ImageGCPolicy{
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}),
Name: app.Name,
// 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),
Hash: containerHash,
State: appStateToContainerState(app.State),
Image: fmt.Sprintf("%s:%s", app.Image.Name, app.Image.Version),
ImageID: app.Image.Id,
Hash: containerHash,
State: appStateToContainerState(app.State),
})
}

View File

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