我试图在加载 docker 镜像 tar 文件后立即获取 ImageID。但是
getImageIDByRepoTag
功能总是失败。图像按预期加载,我可以看到 docker images
并且可以看到图像。 image Load from tar
函数按预期工作。但是在图像加载失败后立即检查getImageIDByRepoTag
。
func main() {
// Set up Docker client
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
panic(err)
}
// Path to your tar file
tarFilePath := "mongo.tar"
repoTag, err := loadImageFromTar(cli, tarFilePath)
if err != nil {
fmt.Printf("failed to load image from tar: %v", err)
panic(err)
}
fmt.Printf("RepoTag: %s\n", repoTag)
// sleep for 5 seconds to allow the image to load
// time.Sleep(5 * time.Second)
// Get the image ID
imageID, err := getImageIDByRepoTag(cli, repoTag)
if err != nil {
fmt.Printf("failed to get image ID: %v\n", err)
} else {
fmt.Printf("Image ID: %s\n", imageID)
}
}
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()
// Create a tar reader from the pipe reader
tr := io.TeeReader(tarFile, pw) // reading `tr` will read the tarFile - but simultaneously write to `pw`
// Set up a WaitGroup for synchronization
var wg sync.WaitGroup
wg.Add(2)
// Set up error channels
errChan1 := make(chan error, 1)
errChan2 := make(chan error, 1)
// Load the Docker image in a separate goroutine
var imageLoadResponse types.ImageLoadResponse
go func() {
defer wg.Done()
defer close(errChan1)
imageLoadResponse, err = cli.ImageLoad(context.Background(), tr, false)
fmt.Println("ImageLoadResponse: ", imageLoadResponse)
if err != nil {
errChan1 <- fmt.Errorf("failed to load Docker image: %w", err)
return
}
// Close the image load response body
defer imageLoadResponse.Body.Close()
fmt.Println("Done loading image")
}()
// 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()
defer close(errChan2)
tarReader := tar.NewReader(pr) // read from pipe (indirectly from the file)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
errChan2 <- fmt.Errorf("failed to read tar header: %w", err)
return
}
// Extract the repository and tag from the manifest file
if header.Name == "manifest.json" {
data, err := io.ReadAll(tarReader)
if err != nil {
errChan2 <- fmt.Errorf("failed to read manifest file: %w", err)
return
}
var manifest []map[string]interface{}
err = json.Unmarshal(data, &manifest)
if err != nil {
errChan2 <- fmt.Errorf("failed to unmarshal manifest: %w", err)
return
}
repoTag = manifest[0]["RepoTags"].([]interface{})[0].(string)
}
}
}()
fmt.Println("Waiting for goroutines to finish...")
// Wait for both goroutines to finish
wg.Wait()
// Check if any error occurred in the goroutines
if err != nil {
return "", err
}
// Check if any error occurred in the goroutines
select {
case err := <-errChan1:
if err != nil {
fmt.Printf("Error in errChan2: %v", err)
return "", err
}
case err := <-errChan2:
if err != nil {
fmt.Printf("Error in errChan2: %v", err)
return "", err
}
default:
}
return repoTag, nil
}
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)
}
但是如果我取消注释
time.Sleep(5 * time.Second)
程序工作/执行正常。
看起来在 docker 客户端的 load image
功能之后有延迟。
延迟/睡眠功能是否有任何解决方法,因为它可能因系统而异,基于 RAM 等。?
谢谢
根据我的评论,在某些情况下客户端主体不会关闭,因此请确保在靠近呼叫站点的地方进行清理:
go func() {
defer wg.Done()
imageLoadResponse, err = cli.ImageLoad(context.Background(), tr, false)
fmt.Println("ImageLoadResponse: ", imageLoadResponse)
if err != nil {
err = fmt.Errorf("failed to load Docker image: %w", err)
fmt.Println(err)
return
}
// call succeeded, so ensure we do clean-up ...
defer imageLoadResponse.Body.Close() // <-- move this line here
fmt.Println("Done loading image")
}()