Maison >développement back-end >Golang >Échec du chargement de l'image Docker

Échec du chargement de l'image Docker

WBOY
WBOYavant
2024-02-13 08:20:091124parcourir

Échec du chargement de limage Docker

l'éditeur php Strawberry peut rencontrer un problème courant lors de l'utilisation de Docker, à savoir "échec du chargement de l'image Docker". Ce problème peut nous empêcher d'utiliser Docker pour créer et exécuter normalement des conteneurs. Mais ne vous inquiétez pas, il existe généralement plusieurs solutions à ce problème. Cet article vous présentera quelques solutions courantes pour vous aider à charger avec succès l'image Docker et à résoudre ce problème ennuyeux. Que vous soyez un utilisateur débutant ou expérimenté de Docker, j'espère que cet article vous sera utile.

Contenu de la question

J'utilise l'image Docker au format golangdocker 客户端 加载 .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
}

// Fonction : 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("未找到存储库标记的图像 id: %s", repotag). De plus, lorsque je lance docker images, je ne vois pas l'image se charger. Il semble que le chargement de l'image ne soit pas encore terminé.

Dans mon autre code, le client Docker cli.imageload 立即返回,但 docker 映像加载通常需要一些时间。我通常会在检查 getimageidbyrepotag ajoute environ 30 secondes de temps d'attente avant. L'ajout d'un temps d'attente n'aide pas non plus dans ce cas.

Merci

Solution

Quelques questions :

  • Ces deux goroutines sont partagées err donc certaines gestions d'erreurs peuvent être perdues
    • Vous devez utiliser une variable d'erreur unique pour chaque goroutine ici et vérifier les deux erreurs après wg.wait()
  • Problème principal : vous recevez un flux d'octets du tar 阅读器中读取内容以查找清单文件并提取标签信息 - 这很好 - 但找到后,您将字节流的其余部分复制到管道中。因此,您将丢失一块永远不会到达 dockerclient

Pour éviter de lire deux fois le flux d'octets tar, vous pouvez utiliser io.teereader. Cela vous permet de lire les archives tar - scan manifest 文件 - 但也可以将此流完整写入其他地方(即写入 docker client).

Créer teereader :

tr := io.teereader(tarfile, pw)  // reading `tr` will read the tarfile - but simultaneously write to `pw`

Le chargement de l'image lira désormais à partir d'ici (au lieu du tuyau) :

//imageloadresponse, err = cli.imageload(context.background(), pr, false)
imageloadresponse, err = cli.imageload(context.background(), tr, false)

Puis changez votre archive/tarlecteur pour lire depuis le tuyau :

//tarreader := tar.newreader(tarfile) // direct from file
tarreader := tar.newreader(pr) // read from pipe (indirectly from the file)

Vous pouvez ensuite supprimer le bloc io.copy :

// no longer needed:
//
// _, err = io.copy(pw, tarreader)
//

Parce que le code d'inspection du goudron lira l'intégralité du flux dans eof.

P.S. Vous devrez peut-être convertir io.eof 重置为 nil 以避免在检查来自任一 goroutine 的任何潜在错误时认为 eof en une erreur plus grave :

header, err = tarReader.Next()
if err == io.EOF {
    err = nil  //  EOF is a non-fatal error here
    break
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer