Skip to content
This repository has been archived by the owner on May 4, 2021. It is now read-only.

Commit

Permalink
rewrite using Golang
Browse files Browse the repository at this point in the history
  • Loading branch information
tifayuki committed Mar 16, 2015
1 parent e997bb3 commit d3d98d7
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 138 deletions.
37 changes: 37 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
*.py[cod]

# C extensions
*.so

# Packages
*.egg
*.egg-info
dist
build
eggs
parts
var
sdist
develop-eggs
.installed.cfg
lib
lib64
__pycache__

# Installer logs
pip-log.txt

# Unit test / coverage reports
.coverage
.tox
nosetests.xml

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject

.idea/
25 changes: 21 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
FROM ubuntu:trusty
FROM tutum/curl:trusty
MAINTAINER Feng Honglin <hfeng@tutum.co>

ENV CLEAN_PERIOD **None**
ENV DELAY_TIME **None**
ENV UNUSED_VOLUME_TIME **None**
ADD . /gopath/src/github.com/tutumcloud/image-cleanup

RUN apt-get update -y && \
apt-get install --no-install-recommends -y -q git && \
mkdir /goroot && \
curl -s https://storage.googleapis.com/golang/go1.3.linux-amd64.tar.gz | tar xzf - -C /goroot --strip-components=1 && \
export GOROOT=/goroot && \
export GOPATH=/gopath && \
export PATH=$PATH:/goroot/bin && \
go get github.com/tutumcloud/image-cleanup && \
cp /gopath/bin/* / && \
rm -fr /goroot /gopath /var/lib/apt/lists && \
apt-get autoremove -y git && \
apt-get clean

ENV IMAGE_CLEAN_INTERVAL 1
ENV IMAGE_CLEAN_DELAYED 1800
ENV VOLUME_CLEAN_INTERVAL 1800
ENV IMAGE_LOCKED **None**

ADD run.sh /run.sh
RUN chmod +x /run.sh

CMD ["/run.sh"]
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ tutum/image-cleanup
-v /var/run:/var/run:rw \
-v /usr/lib/tutum/docker:/usr/bin/docker:r \
-v /var/lib/docker:/var/lib/docker:rw \
-e CLEAN_PERIOD=1800 \
-e DELAY_TIME=1800 \
-e KEEP_IMAGES="ubuntu:trusty, ubuntu:latest" \
-e IMAGE_CLEAN_INTERVAL=1 \
-e IMAGE_CLEAN_DELAYED=1800 \
-e VOLUME_CLEAN_INTERVAL=1800 \
-e IMAGE_LOCKED="ubuntu:trusty, tutum/curl:trusty" \
tutum/image-cleanup
```

**Arguments**

```
CLEAN_PERIOD how many seconds to run the clean script, 1800 by default.
DELAY_TIME how many seconds delay to remove docker images, 1800 by default.
UNUSED_VOLUME_TIME how many seconde delay to remove docker volumes, 86400(24h) by default.
KEEP_IMAGES A list of Images that will not be cleaned by this container, separated by ","
IMAGE_CLEAN_INTERVAL how many seconds to clean the images, 1 by default.
IMAGE_CLEAN_DELAYED how many seconds delay to clean docker images, 1800 by default.
VOLUME_CLEAN_INTERVAL how many seconde to clean docker volumes, 1800 by default.
IMAGE_LOCKED A list of Images that will not be cleaned by this container, separated by ","
```
226 changes: 226 additions & 0 deletions cleanup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package main

import (
"flag"
"io/ioutil"
"log"
"os"
"runtime"
"strings"
"sync"
"time"

docker "github.com/fsouza/go-dockerclient"
)

var (
pDockerHost = flag.String("dockerHost", "unix:///var/run/docker.sock", "docker host")
pImageCleanInterval = flag.Int("imageCleanInterval", 1, "interval to run image cleanup")
pImageCleanDelayed = flag.Int("imageCleanDelayed", 1800, "delayed time to clean the images")
pVolumeCleanInterval = flag.Int("volumeCleanInterval", 1800, "interval to run volume cleanup")
pImageLocked = flag.String("imageLocked", "", "images to avoid being cleaned")
)

func init() {
runtime.GOMAXPROCS(4)
}

func main() {
flag.Parse()

client, err := getDockerClient(*pDockerHost)
if err != nil {
log.Fatalf("Docker %s:%s", err, *pDockerHost)
}

var wg sync.WaitGroup
wg.Add(2)
go cleanImages(client)
go cleanVolumes(client)
wg.Wait()
}

func getDockerClient(host string) (*docker.Client, error) {
client, err := docker.NewClient(host)
if err != nil {
return nil, err
}
return client, nil
}

func cleanImages(client *docker.Client) {
log.Printf("Img Cleanup: the following images will be locked: %s", *pImageLocked)
log.Println("Img Cleanup: starting image cleanup ...")
for {
// imageIdMap[imageID] = isRemovable
imageIdMap := make(map[string]bool)

// Get the image ID list before the cleanup
images, err := client.ListImages(docker.ListImagesOptions{All: false})
if err != nil {
log.Println("Img Cleanup: cannot get images list", err)
time.Sleep(time.Duration(*pImageCleanInterval+*pImageCleanDelayed) * time.Second)
continue
}

for _, image := range images {
imageIdMap[image.ID] = true
}

// Get the image IDs used by all the containers
containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
if err != nil {
log.Println("Img Cleanup: cannot get container list", err)
time.Sleep(time.Duration(*pImageCleanInterval+*pImageCleanDelayed) * time.Second)
continue
} else {
inspect_error := false
for _, container := range containers {
containerInspect, err := client.InspectContainer(container.ID)
if err != nil {
inspect_error = true
log.Println("Img Cleanup: cannot get container inspect", err)
break
}
imageIdMap[containerInspect.Image] = false
}
if inspect_error {
time.Sleep(time.Duration(*pImageCleanInterval+*pImageCleanDelayed) * time.Second)
continue
}
}

// Get all the locked image ID
if *pImageLocked != "" {
lockedImages := strings.Split(*pImageLocked, ",")
for _, lockedImage := range lockedImages {
imageInspect, err := client.InspectImage(strings.Trim(lockedImage, " "))
if err == nil {
imageIdMap[imageInspect.ID] = false
}

}
}

// Sleep for the delay time
log.Printf("Img Cleanup: wait %d seconds for the cleaning", *pImageCleanDelayed)
time.Sleep(time.Duration(*pImageCleanDelayed) * time.Second)

// Get the image IDs used by all the containers again after the delay time
containersDelayed, err := client.ListContainers(docker.ListContainersOptions{All: true})
if err != nil {
log.Println("Img Cleanup: cannot get container list", err)
time.Sleep(time.Duration(*pImageCleanInterval) * time.Second)
continue
} else {
inspect_error := false
for _, container := range containersDelayed {
containerInspect, err := client.InspectContainer(container.ID)
if err != nil {
inspect_error = true
log.Println("Img Cleanup: cannot get container inspect", err)
break
}
imageIdMap[containerInspect.Image] = false
}
if inspect_error {
time.Sleep(time.Duration(*pImageCleanInterval) * time.Second)
continue
}
}

// Remove the unused images
counter := 0
for id, removable := range imageIdMap {
if removable {
log.Printf("Img Cleanup: removing image %s", id)
err := client.RemoveImage(id)
if err != nil {
log.Printf("Img Cleanup: %s", err)
}
counter += 1
}
}
log.Printf("Img Cleanup: %d images have been removed", counter)

// Sleep again
log.Printf("Img Cleanup: next cleanup will be start in %d seconds", *pImageCleanInterval)
time.Sleep(time.Duration(*pImageCleanInterval) * time.Second)
}
}

func cleanVolumes(client *docker.Client) {
log.Println("Vol Cleanup: starting volume cleanup ...")

// volumesMap[path] = weight
// weight = 0 ~ 99, increace on every iteration if it is not used
// weight = 100, remove it
volumesMap := make(map[string]int)
volumeDir := "/var/lib/docker/vfs/dir/"
for {
containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
if err != nil {
log.Println("Vol Cleanup: cannot get container list", err)
time.Sleep(time.Duration(*pVolumeCleanInterval) * time.Second)
continue
} else {
inspect_error := false
for _, container := range containers {
containerInspect, err := client.InspectContainer(container.ID)
if err != nil {
inspect_error = true
log.Println("Vol Cleanup: cannot get container inspect", err)
break
}
for _, path := range containerInspect.Volumes {
volumesMap[path] = 0
}
}
if inspect_error {
time.Sleep(time.Duration(*pVolumeCleanInterval) * time.Second)
continue
}
}

files, err := ioutil.ReadDir("/var/lib/docker/vfs/dir/")
if err != nil {
log.Printf("Vol Cleanup: %s", err)
} else {
for _, f := range files {
path := volumeDir + f.Name()
weight := volumesMap[path]
volumesMap[path] = weight + 1
}
}

files, err = ioutil.ReadDir("/var/lib/docker/volumes/")
if err != nil {
log.Printf("Vol Cleanup: %s", err)
} else {
for _, f := range files {
path := volumeDir + f.Name()
weight := volumesMap[path]
volumesMap[path] = weight + 1
}
}

// Remove the unused volumes
counter := 0
for path, weight := range volumesMap {
if weight == 100 {
log.Printf("Vol Cleanup: removing volume %s", path)
err := os.RemoveAll(path)
if err != nil {
log.Printf("Img Cleanup: %s", err)
}
delete(volumesMap, path)
counter += 1
}
}
log.Printf("Vol Cleanup: %d volumes have been removed", counter)

// Sleep
log.Printf("Vol Cleanup: next cleanup will be start in %d seconds", *pVolumeCleanInterval)
time.Sleep(time.Duration(*pVolumeCleanInterval) * time.Second)
}
}
Loading

0 comments on commit d3d98d7

Please sign in to comment.