Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/rm docker image #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rm-docker-image/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vscode
35 changes: 35 additions & 0 deletions rm-docker-image/README.md
Original file line number Diff line number Diff line change
@@ -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)
```
27 changes: 27 additions & 0 deletions rm-docker-image/funs/funs.go
Original file line number Diff line number Diff line change
@@ -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
}
}
10 changes: 10 additions & 0 deletions rm-docker-image/funs/funs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package funs

import "testing"

func TestExec(t *testing.T) {
_, err := Exec("ls")
if err != nil {
t.Error("exec err:", err)
}
}
141 changes: 141 additions & 0 deletions rm-docker-image/image/image.go
Original file line number Diff line number Diff line change
@@ -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)})
}
}
38 changes: 38 additions & 0 deletions rm-docker-image/main.go
Original file line number Diff line number Diff line change
@@ -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()
}