首页 > 解决方案 > 使用 Go 客户端将 Docker 上下文作为 Tar 发送找不到 Dockerfile

问题描述

我正在使用Go Docker 客户端尝试从其Dockerfile内容在代码中定义的图像构建图像。

根据Docker Daemon API Documentation

输入流必须是 tar 存档...

...存档必须包含一个构建指令文件,通常在存档的根目录中称为 Dockerfile。

所以我想在代码中创建构建上下文,将其写入一个 tar 文件,然后将其发送到要构建的 Docker 守护进程。为此,我可以使用ImageBuild 函数并将 tar 文件(构建上下文)作为io.ReadCloser. 只要 myDockerfile位于该压缩存档的根目录,它就应该找到并构建它。

但是,我得到了常见的错误:

Error response from daemon: Cannot locate specified Dockerfile: Dockerfile

这显然意味着它无法在存档的根目录中找到 Dockerfile。我不确定为什么。我相信我这样做的方式会Dockerfile在 tar 存档的根目录中添加一个。守护进程应该看到这一点。我在这里有什么误解?

重现的代码片段

    var buf bytes.Buffer
    tarWriter := tar.NewWriter(&buf)

    contents := "FROM alpine\nCMD [\"echo\", \"this is from the archive\"]"

    if err := tarWriter.WriteHeader(&tar.Header{
        Name:     "Dockerfile",
        Mode:     777,
        Size:     int64(len(contents)),
        Typeflag: tar.TypeReg,
    }); err != nil {
        panic(err)
    }

    if _, err := tarWriter.Write([]byte(contents)); err != nil {
        panic(err)
    }

    if err := tarWriter.Close(); err != nil {
        panic(err)
    }

    reader := tar.NewReader(&buf)
    c, err := client.NewEnvClient()
    if err != nil {
        panic(err)
    }

    _, err = c.ImageBuild(context.Background(), reader, types.ImageBuildOptions{
        Context:    reader,
        Dockerfile: "Dockerfile",
    })
    if err != nil {
        panic(err)
    }

go.mod 文件

module docker-tar

go 1.12

require (
    github.com/docker/distribution v2.7.1+incompatible // indirect
    github.com/docker/docker v1.13.1
    github.com/docker/go-connections v0.4.0 // indirect
    github.com/docker/go-units v0.4.0 // indirect
    github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
    github.com/pkg/errors v0.8.1 // indirect
    golang.org/x/net v0.0.0-20191112182307-2180aed22343 // indirect
)

标签: dockergodocker-api

解决方案


  1. 777在八进制数字系统前面添加一个零: 0777, 0o777, 或0O777

  2. reader := bytes.NewReader(buf.Bytes())不使用tar.NewReader(&buf)

  3. 也用于client.WithAPIVersionNegotiation()较新的版本。

  4. 试试这个工作版本:

package main

import (
    "archive/tar"
    "bytes"
    "context"
    "fmt"

    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
)

func main() {
    var buf bytes.Buffer
    tarWriter := tar.NewWriter(&buf)
    contents := `FROM alpine:3.10.3
CMD ["echo", "this is from the archive"]
`
    header := &tar.Header{
        Name:     "Dockerfile",
        Mode:     0o777,
        Size:     int64(len(contents)),
        Typeflag: tar.TypeReg,
    }
    err := tarWriter.WriteHeader(header)
    if err != nil {
        panic(err)
    }
    _, err = tarWriter.Write([]byte(contents))
    if err != nil {
        panic(err)
    }
    err = tarWriter.Close()
    if err != nil {
        panic(err)
    }

    c, err := client.NewClientWithOpts(client.WithAPIVersionNegotiation())
    if err != nil {
        panic(err)
    }
    fmt.Println(c.ClientVersion())

    reader := bytes.NewReader(buf.Bytes()) // tar.NewReader(&buf)
    ctx := context.Background()
    buildOptions := types.ImageBuildOptions{
        Context:    reader,
        Dockerfile: "Dockerfile",
        Tags:       []string{"alpine-echo:1.2.4"},
    }
    _, err = c.ImageBuild(ctx, reader, buildOptions)
    if err != nil {
        panic(err)
    }
}


docker image ls之后的输出go run .

REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
alpine-echo                      1.2.4               d81774f32812        26 seconds ago      5.55MB
alpine                           3.10.3              b168ac0e770e        4 days ago          5.55MB

输出docker run alpine-echo:1.2.4

this is from the archive

注意:您可能需要FROM alpine:3.10.3针对您的特定版本进行编辑。


推荐阅读