Golang中解决使用github.com/docker/docker/client包时Docker镜像大小不一致的问题
Golang中解决使用github.com/docker/docker/client包时Docker镜像大小不一致的问题 我正在使用Go构建自己的Docker客户端,但出于某种原因,在显示/计算镜像大小时出现了不一致的情况。
使用标准的Docker客户端,我得到:
[12:59:18|jfgratton@zenika:~]: docker images |egrep "certbui|postgres"
nexus:9820/postgresql14 latest-arm64 53e6d093a323 3 months ago 87.2MB
nexus:9820/certbuilder 2.02.00-arm64 6fb52fc0da42 3 months ago 226MB
而我的工具得到的是:
[12:49:25|jfgratton@zenika:~]: dtools lsi |egrep "certbui|postgres"
┃ nexus:9820 ┃ certbuilder ┃ 2.02.00-arm64 ┃ 6fb52fc0da42 ┃ 2022.09.03 15:50:41 ┃ 215MB ┃
┃ nexus:9820 ┃ postgresql14 ┃ latest-arm64 ┃ 53e6d093a323 ┃ 2022.09.08 18:07:51 ┃ 83MB ┃
我使用的来自docker/docker/client包的代码非常简单直接:
images, err := cli.ImageList(ctx, types.ImageListOptions{All: true})
if err != nil {
errmsg := fmt.Sprintf("%v", err)
if errmsg == "Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?" {
fmt.Println(errmsg)
os.Exit(-1)
} else {
panic(err)
}
}
for _, image := range images {
//imgSpec := getImageTag(image.ID, image.RepoTags, image.Created, image.Size)
imgspecSlice = append(imgspecSlice, getImageTag(image.ID, image.RepoTags, image.Created, image.Size)...)
}
根据types包 - github.com/docker/docker/api/types - Go Packages,我期望镜像大小在Size(int64)结构体中(顺便问一下:VirtualSize是做什么用的?)。
我理解这本身不是一个Go问题,而更多是关于Docker的API…
更多关于Golang中解决使用github.com/docker/docker/client包时Docker镜像大小不一致的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
问题已解决……顺便说一句,我提问时疏忽了,漏掉了一段计算报告大小并将其格式化为MB的代码片段。
基本上,我之前是这样做的:
imgspec.size = (float32)(size / 1024.0 / 1024.0)
imgspecSlice = append(imgspecSlice, imgspec)
而不是:
imgspec.size = (float32)(size / 1000.0 / 1000.0)
imgspecSlice = append(imgspecSlice, imgspec)
我错误地将数值除以了“实际”的单位大小(1024),而不是人类可读的大小(1000)。
已解决。
更多关于Golang中解决使用github.com/docker/docker/client包时Docker镜像大小不一致的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Docker API中,镜像大小计算确实存在一些需要注意的细节。主要问题在于Docker使用不同的单位来计算和显示大小。
docker images命令显示的是可读格式的大小,而API返回的是字节数。你需要手动进行转换。以下是修正后的代码:
package main
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"math"
)
func formatSize(size int64) string {
// Docker CLI使用的单位转换
const unit = 1024
if size < unit {
return fmt.Sprintf("%dB", size)
}
div, exp := int64(unit), 0
for n := size / unit; n >= unit; n /= unit {
div *= unit
exp++
}
// Docker使用1000作为MB的除数,而不是1024
if exp >= 2 { // 从MB开始使用1000
div = int64(math.Pow(1000, float64(exp-1))) * 1024
}
sizeStr := fmt.Sprintf("%.1f", float64(size)/float64(div))
// 移除不必要的.0
if sizeStr[len(sizeStr)-2:] == ".0" {
sizeStr = sizeStr[:len(sizeStr)-2]
}
// 添加单位
units := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB"}
return fmt.Sprintf("%s%s", sizeStr, units[exp])
}
func main() {
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
panic(err)
}
images, err := cli.ImageList(ctx, types.ImageListOptions{All: true})
if err != nil {
panic(err)
}
for _, image := range images {
// 使用VirtualSize而不是Size
// VirtualSize包含所有分层的大小,而Size可能只包含顶层
size := image.VirtualSize
if size == 0 {
size = image.Size
}
formattedSize := formatSize(size)
fmt.Printf("Image: %v, Size: %s\n", image.RepoTags, formattedSize)
}
}
关于VirtualSize字段:在Docker的早期版本中,VirtualSize表示包含所有分层的总大小,而Size可能只表示顶层的大小。但在现代Docker版本中,这两个值通常是相同的。建议优先使用VirtualSize,如果为0则回退到Size。
大小不一致的原因:
- Docker CLI使用1000作为MB/GB的除数(SI单位),而Go的
fmt包默认使用1024(二进制单位) - 舍入差异:Docker CLI显示一位小数,你的代码可能使用了不同的舍入方式
- 单位标签:Docker使用"MB"、“GB"等,而标准库可能使用"MiB”、“GiB”
要完全匹配docker images的输出,你需要实现与Docker CLI完全相同的格式化逻辑。上面的formatSize函数模拟了Docker CLI的行为。

