diff --git a/rm-docker-image/.gitignore b/rm-docker-image/.gitignore new file mode 100644 index 0000000..600d2d3 --- /dev/null +++ b/rm-docker-image/.gitignore @@ -0,0 +1 @@ +.vscode \ No newline at end of file diff --git a/rm-docker-image/README.md b/rm-docker-image/README.md new file mode 100644 index 0000000..a6dc5bd --- /dev/null +++ b/rm-docker-image/README.md @@ -0,0 +1,35 @@ +简介 +--- + +### 做什么的 +``` +累计总量images磁盘>100G 时,删除7天以上的镜像。 +如果删除后仍然超过100G 则天数逐渐递减删除 直至小于100G +``` + +### 大致流程 +``` +通过 docker system df 获取images的大小 + +再通过 docker images --format "{{.ID}}\t{{.CreateAt}}" 获取所有的id 以及镜像的创建时间 + +最后通过 docker rmi IDS... 删除符合条件的镜像 +``` + +### 使用说明 +``` +go run main.go -d 7 -s 100 +// -d 天数 默认7天 +// -s images 总和 GB 默认100GB +// image总和 大于100G时,删除超过7天的image, 如果还是超过100G 则天数递减,直至小于100G 或 小于0天 +``` + +### 为什么 image 没有被删除掉 +``` +本程序没有做docker rm -f 强制删除操作 + +原因有二: +1、有些image 的容器还在使用中 +2、有些image 做了tag ,image ID 重复,导致无法删除(本程序未考虑该情况,因为集群主要是pull image 不做tag) +、 +``` \ No newline at end of file diff --git a/rm-docker-image/funs/funs.go b/rm-docker-image/funs/funs.go new file mode 100644 index 0000000..bc772b1 --- /dev/null +++ b/rm-docker-image/funs/funs.go @@ -0,0 +1,27 @@ +package funs + +import ( + "context" + "errors" + "os/exec" + "time" +) + +var timeout = 10 * time.Second + +// Exec 执行 command +func Exec(name string, arg ...string) (string, error) { + ctxt, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() //releases resources if slowOperation completes before timeout elapses + cmd := exec.CommandContext(ctxt, name, arg...) + //当经过Timeout时间后,程序依然没有运行完,则会杀掉进程,ctxt也会有err信息 + if out, err := cmd.Output(); err != nil { + //检测报错是否是因为超时引起的 + if ctxt.Err() != nil && ctxt.Err() == context.DeadlineExceeded { + return "", errors.New("command timeout") + } + return string(out), err + } else { + return string(out), nil + } +} diff --git a/rm-docker-image/funs/funs_test.go b/rm-docker-image/funs/funs_test.go new file mode 100644 index 0000000..307db5f --- /dev/null +++ b/rm-docker-image/funs/funs_test.go @@ -0,0 +1,10 @@ +package funs + +import "testing" + +func TestExec(t *testing.T) { + _, err := Exec("ls") + if err != nil { + t.Error("exec err:", err) + } +} diff --git a/rm-docker-image/image/image.go b/rm-docker-image/image/image.go new file mode 100644 index 0000000..1c5471b --- /dev/null +++ b/rm-docker-image/image/image.go @@ -0,0 +1,141 @@ +package image + +import ( + "bufio" + "fmt" + "io" + "strconv" + "strings" + "time" + + "github.com/Ankr-network/dccn-tools/rm-docker-image/funs" +) + +// DelImage 删除镜像的条件 +type DelImage struct { + date int // 删除大于多少天的 + size float64 // 超过 size 继续删除 +} + +// Run 执行删除 条件达到的镜像 +func (i *DelImage) Run() { + for { + if i.date < 0 { + fmt.Println("DelImage.date 已小于0天,无可再删了的") + return + } + if i.getSize() > i.size { + i.delete() + i.date-- + } else { + return + } + time.Sleep(time.Second) + } +} + +func (i *DelImage) getSize() float64 { + str, err := funs.Exec("docker", "system", "df") + if err != nil { + fmt.Printf("docker system df 获取出错:%s\n", err) + return 0 + } + bf := bufio.NewReader(strings.NewReader(str)) + bf.ReadString('\n') + imageStr, err := bf.ReadString('\n') + + if err != nil { + fmt.Printf("readString 没有获取数据,err:%s\n", err) + return 0 + } + reclaimable := strings.Trim(imageStr, "\r\n ") + reclaimables := strings.Fields(reclaimable) + b := strings.Contains(reclaimables[3], "B") + var ( + sizenum float64 + numtype string + ) + if b { + str := reclaimables[3] + strbyte := str[:len(reclaimables[3])-2] + sizenum, _ = strconv.ParseFloat(string(strbyte), 64) + numtype = string(str[len(reclaimables[3])-2:]) + } else { + sizenum, _ = strconv.ParseFloat(reclaimables[3], 64) + numtype = reclaimables[4] + } + + switch numtype { + case "MB": + return sizenum / 1024 + case "GB": + return sizenum + case "KB": + return sizenum / 1024 / 1024 + case "TB": + return sizenum * 1024 + default: + fmt.Printf("未识别到images所占的内存总量:%s %s\n", reclaimables[3], reclaimables[4]) + } + return 0 +} + +func (i *DelImage) delete() { + images := make(images, 0) + images.getCreated() + var ids []string + ids = append(ids, "rmi") + for _, v := range images { + if v.created >= i.date { + ids = append(ids, v.ID) + } + } + if len(ids) == 1 { + fmt.Printf("delete %d day: 无images可删 \n", i.date) + return + } + + funs.Exec("docker", ids...) + + fmt.Printf("delete %d day: docker %v \n", i.date, strings.Join(ids, " ")) +} + +// CreateDelImage 创建镜像 +func CreateDelImage(date int, size float64) DelImage { + return DelImage{date, size} +} + +type image struct { + ID string + created int // 创建镜像的天数 +} + +// image 应该有 delete getSize 等功能呢 + +type images []image + +// images 应该有获取总size,总删除,筛选的功能 +func (i *images) getCreated() { + data, err := funs.Exec("docker", "images", "--format", `{{.ID}}\t{{.CreatedAt}}`) + if err != nil { + fmt.Printf("docker images --format 获取数据出错:%s\n", err) + return + } + bf := bufio.NewReader(strings.NewReader(data)) + now := time.Now() + timeLayout := "2006-01-02 15:04:05" //转化所需模板 + for { + str, err := bf.ReadString('\n') + + if err != nil { + if err == io.EOF { //文件已经结束 + break + } + fmt.Println("getCreated readeSteing err = ", err) + } + strs := strings.Fields(str) + tmp, _ := time.Parse(timeLayout, strs[1]+" "+strs[2]) + sub := now.Sub(tmp) + *i = append(*i, image{strs[0], int(sub.Hours() / 24)}) + } +} diff --git a/rm-docker-image/main.go b/rm-docker-image/main.go new file mode 100644 index 0000000..d10cddc --- /dev/null +++ b/rm-docker-image/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "flag" + "fmt" + "time" + + "github.com/Ankr-network/dccn-tools/rm-docker-image/image" +) + +var ( + h bool + date int + size float64 +) + +func init() { + flag.BoolVar(&h, "h", false, "帮助文档") + flag.IntVar(&date, "d", 7, "大于多少天的将会被删除") + flag.Float64Var(&size, "s", 100, "大于多少GB的将会被删除") + flag.Parse() +} + +func main() { + if h { + flag.Usage() + return + } + if date < 0 || size < 0 { + fmt.Println("请填写正确的信息,date >= 0 && size >= 0") + return + } + fmt.Printf("------- %s start -------\n", time.Now().Format("2006-01-02")) + defer fmt.Printf("------- %s end -------\n\n", time.Now().Format("2006-01-02")) + + dic := image.CreateDelImage(date, size) + dic.Run() +}