提问人:BhanuKiran 提问时间:4/8/2023 最后编辑:BhanuKiran 更新时间:4/9/2023 访问量:158
加载 Docker 镜像失败
loading docker image fails
问:
我正在使用 ,以格式加载 docker 映像。golang
docker client
.tar
func loadImageFromTar(cli *client.Client, tarFilePath string) (string, error) {
// Read tar file
tarFile, err := os.Open(tarFilePath)
if err != nil {
return "", fmt.Errorf("failed to open tar file: %w", err)
}
defer tarFile.Close()
// Create a pipe to stream data between tar reader and Docker client
pr, pw := io.Pipe()
// Set up a WaitGroup for synchronization
var wg sync.WaitGroup
wg.Add(2)
// Load the Docker image in a separate goroutine
var imageLoadResponse types.ImageLoadResponse
go func() {
defer wg.Done()
imageLoadResponse, err = cli.ImageLoad(context.Background(), pr, false)
if err != nil {
err = fmt.Errorf("failed to load Docker image: %w", err)
}
}()
// Read tar file metadata and copy the tar file to the pipe writer in a separate goroutine
var repoTag string
go func() {
defer wg.Done()
defer pw.Close()
tarReader := tar.NewReader(tarFile)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
err = fmt.Errorf("failed to read tar header: %w", err)
fmt.Printf("Error: %v", err)
return
}
// Extract the repository and tag from the manifest file
if header.Name == "manifest.json" {
data, err := io.ReadAll(tarReader)
if err != nil {
err = fmt.Errorf("failed to read manifest file: %w", err)
fmt.Printf("Error: %v", err)
return
}
var manifest []map[string]interface{}
err = json.Unmarshal(data, &manifest)
if err != nil {
err = fmt.Errorf("failed to unmarshal manifest: %w", err)
fmt.Printf("Error: %v", err)
return
}
repoTag = manifest[0]["RepoTags"].([]interface{})[0].(string)
}
// Copy the tar file data to the pipe writer
_, err = io.Copy(pw, tarReader)
if err != nil {
err = fmt.Errorf("failed to copy tar data: %w", err)
fmt.Printf("Error: %v", err)
return
}
}
}()
// Wait for both goroutines to finish
wg.Wait()
// Check if any error occurred in the goroutines
if err != nil {
return "", err
}
// Close the image load response body
defer imageLoadResponse.Body.Close()
// Get the image ID
imageID, err := getImageIDByRepoTag(cli, repoTag)
if err != nil {
return "", fmt.Errorf("failed to get image ID: %w", err)
}
return imageID, nil
}
函数:getImageIDByRepoTag
func getImageIDByRepoTag(cli *client.Client, repoTag string) (string, error) {
images, err := cli.ImageList(context.Background(), types.ImageListOptions{})
if err != nil {
return "", fmt.Errorf("failed to list images: %w", err)
}
for _, image := range images {
for _, tag := range image.RepoTags {
if tag == repoTag {
return image.ID, nil
}
}
}
return "", fmt.Errorf("image ID not found for repo tag: %s", repoTag)
}
始终返回 .
此外,当我运行时,我没有看到正在加载的图像。看起来图像加载未完成。getImageIDByRepoTag
fmt.Errorf("image ID not found for repo tag: %s", repoTag)
docker images
在我的其他代码中,尽管 docker 客户端立即返回,但 docker 映像加载通常需要时间。我通常会在检查之前增加大约 30 秒的等待时间。在这种情况下,增加等待时间也无济于事。cli.ImageLoad
getImageIDByRepoTag
谢谢
答:
1赞
colm.anseo
4/9/2023
#1
有几个问题:
- 这两个 goroutines 共享,因此可能会丢失一些错误处理
err
- 您应该在此处为每个 goroutine 使用唯一的错误变量,并在
wg.Wait()
- 您应该在此处为每个 goroutine 使用唯一的错误变量,并在
- 主要问题:您正在从阅读器中读取以查找清单文件并提取标记信息 - 这很好 - 但是在找到此内容后,您将字节流的其余部分复制到管道中。因此,您将丢失从未到达客户端的字节流块
tar
docker
为了避免两次读取 tar 字节流,您可以使用 io。Tee阅读器。
这允许您读取 tar 存档 - 扫描文件 - 但也在其他地方(即客户端)完整地写入此流。manifest
docker
创建 :TeeReader
tr := io.TeeReader(tarFile, pw) // reading `tr` will read the tarFile - but simultaneously write to `pw`
图像加载现在将从中读取(而不是管道):
//imageLoadResponse, err = cli.ImageLoad(context.Background(), pr, false)
imageLoadResponse, err = cli.ImageLoad(context.Background(), tr, false)
然后更改您的阅读器以从管道中读取:archive/tar
//tarReader := tar.NewReader(tarFile) // direct from file
tarReader := tar.NewReader(pr) // read from pipe (indirectly from the file)
然后,您可以删除您的块:io.Copy
// no longer needed:
//
// _, err = io.Copy(pw, tarReader)
//
因为 tar-inspection 代码会将整个流读取到 EOF。
P.S. 当您检查来自任一 goroutines 的任何潜在错误时,您可能希望重置 to 以避免认为 a 是一个更严重的错误:io.EOF
nil
EOF
header, err = tarReader.Next()
if err == io.EOF {
err = nil // EOF is a non-fatal error here
break
}
评论
0赞
BhanuKiran
4/9/2023
按照建议修改后。图像按预期加载。但找不到 repoTag。在 .如果我添加 after 和 before 似乎工作正常并返回 imageID。有什么选择吗?getImageIDByRepoTag
imageLoad
time.Sleep(5 * time.Second)
imageLoad
getImageIDByRepoTag
0赞
colm.anseo
4/9/2023
我会将 2 个函数分开 - 您可以看到图像正文在函数返回时关闭 - 但在此之前,您正在尝试查询图像。可能存在 docker 客户端阻塞,因为一个请求仍处于待处理状态(因为正文尚未关闭) - 这可能解释了延迟。
0赞
BhanuKiran
4/10/2023
我试着把它们分开。但没有运气。当图像加载正常工作时,我将关闭此问题。并将提出一个关于getImageIDByRepoTag
评论
fmt.Errorf("image ID not found for repo tag: %s", repoTag)
docker