使用Go Client将tar上下文作为Tar发送时找不到Dockerfile

时间:2019-11-14 04:37:39

标签: docker go docker-api

我正在使用Go Docker Client来尝试根据内容由代码定义的Dockerfile构建图像。

根据Docker Daemon API Documentation

  

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

     

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

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

但是,我遇到了常见错误:

Error response from daemon: Cannot locate specified Dockerfile: Dockerfile

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

要复制的代码段

    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
)

1 个答案:

答案 0 :(得分:2)

  1. 777前面为八进制数字系统添加一个零:07770o7770O777

  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