【问题标题】:Build Docker Image From Go Code从 Go 代码构建 Docker 镜像
【发布时间】:2016-12-12 18:13:24
【问题描述】:

我正在尝试使用 Docker API 和 Docker Go 库 (https://github.com/docker/engine-api/) 构建 Docker 映像。代码示例:

package main
import (
    "fmt"
    "github.com/docker/engine-api/client"
    "github.com/docker/engine-api/types"
    "golang.org/x/net/context"
)
func main() {
    defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
    cli, err := client.NewClient("unix:///var/run/docker.sock", "v1.22", nil, defaultHeaders)
    if err != nil {
        panic(err)
    }
    fmt.Print(cli.ClientVersion())
    opt := types.ImageBuildOptions{
        CPUSetCPUs:   "2",
        CPUSetMems:   "12",
        CPUShares:    20,
        CPUQuota:     10,
        CPUPeriod:    30,
        Memory:       256,
        MemorySwap:   512,
        ShmSize:      10,
        CgroupParent: "cgroup_parent",
        Dockerfile:   "dockerSrc/docker-debug-container/Dockerfile",
    }
    _, err = cli.ImageBuild(context.Background(), nil, opt)
    if err == nil || err.Error() != "Error response from daemon: Server error" {
        fmt.Printf("expected a Server Error, got %v", err)
    }
}

错误总是一样的:

来自守护进程的错误响应:找不到指定的 Dockerfile:dockerSrc/docker-debug-container/Dockerfile

来自守护进程的错误响应:找不到指定的 Dockerfile:Dockerfile

我检查过的内容:

  1. 该文件夹存在于构建路径中
  2. 我尝试了相对路径和绝对路径
  3. 路径中没有软链接
  4. 我尝试了二进制和Dockerfile 相同的文件夹
  5. docker build <path> 工作
  6. 还有一堆其他的东西

我的另一个选择是使用RemoteContext,它看起来很有效,但仅适用于完全自包含的 dockerfile,而不是具有“本地文件存在”的那些。


更新: 尝试将tar 作为缓冲区传递,但得到了相同的结果:

  dockerBuildContext, err := os.Open("<path to>/docker-debug-    container/docker-debug-container.tar")
  defer dockerBuildContext.Close()

    opt := types.ImageBuildOptions{
        Context:      dockerBuildContext,
        CPUSetCPUs:   "2",
        CPUSetMems:   "12",
        CPUShares:    20,
        CPUQuota:     10,
        CPUPeriod:    30,
        Memory:       256,
        MemorySwap:   512,
        ShmSize:      10,
        CgroupParent: "cgroup_parent",
        //  Dockerfile:   "Dockerfile",
    }

    _, err = cli.ImageBuild(context.Background(), nil, opt)

【问题讨论】:

    标签: go docker dockerfile docker-api


    【解决方案1】:

    以下对我有用;

    package main
    
    import (
        "archive/tar"
        "bytes"
        "context"
        "io"
        "io/ioutil"
        "log"
        "os"
    
        "github.com/docker/docker/api/types"
        "github.com/docker/docker/client"
    )
    
    func main() {
        ctx := context.Background()
        cli, err := client.NewEnvClient()
        if err != nil {
            log.Fatal(err, " :unable to init client")
        }
    
        buf := new(bytes.Buffer)
        tw := tar.NewWriter(buf)
        defer tw.Close()
    
        dockerFile := "myDockerfile"
        dockerFileReader, err := os.Open("/path/to/dockerfile")
        if err != nil {
            log.Fatal(err, " :unable to open Dockerfile")
        }
        readDockerFile, err := ioutil.ReadAll(dockerFileReader)
        if err != nil {
            log.Fatal(err, " :unable to read dockerfile")
        }
    
        tarHeader := &tar.Header{
            Name: dockerFile,
            Size: int64(len(readDockerFile)),
        }
        err = tw.WriteHeader(tarHeader)
        if err != nil {
            log.Fatal(err, " :unable to write tar header")
        }
        _, err = tw.Write(readDockerFile)
        if err != nil {
            log.Fatal(err, " :unable to write tar body")
        }
        dockerFileTarReader := bytes.NewReader(buf.Bytes())
    
        imageBuildResponse, err := cli.ImageBuild(
            ctx,
            dockerFileTarReader,
            types.ImageBuildOptions{
                Context:    dockerFileTarReader,
                Dockerfile: dockerFile,
                Remove:     true})
        if err != nil {
            log.Fatal(err, " :unable to build docker image")
        }
        defer imageBuildResponse.Body.Close()
        _, err = io.Copy(os.Stdout, imageBuildResponse.Body)
        if err != nil {
            log.Fatal(err, " :unable to read image build response")
        }
    }
    

    【讨论】:

    • 喜欢您使用本机“archive/tar”库来生成 tar,此代码从现在开始可以维护更多年。
    【解决方案2】:

    @Mangirdas:盯着屏幕看足够长的时间确实有帮助——至少对我来说是这样。我已经被同样的问题困扰了一段时间了。 您使用 tar 文件是正确的(您的第二个示例)。如果您查看此处的 API 文档 https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/build-image-from-a-dockerfile,您会发现它需要一个 tar。 真正帮助我的是在我的案例中查看客户端、perl 和 ruby​​ 的其他实现。当被要求从目录构建图像时,两者都会即时创建 tar。 无论如何,您只需要将 dockerBuildContext 放在其他地方(请参阅 cli.ImageBuild())

    dockerBuildContext, err := os.Open("/Path/to/your/docker/tarfile.tar")
    defer dockerBuildContext.Close()
    
    buildOptions := types.ImageBuildOptions{
        Dockerfile:   "Dockerfile", // optional, is the default
    }
    
    buildResponse, err := cli.ImageBuild(context.Background(), dockerBuildContext, buildOptions)
    if err != nil {
        log.Fatal(err)
    }
    defer buildResponse.Body.Close()
    

    我还没有正确命名图像,但至少我可以创建它们...... 希望这可以帮助。 干杯

    【讨论】:

    【解决方案3】:

    Docker 包具有从文件路径创建 TAR 的功能。它是 CLI 使用的。它不在客户端包中,所以需要单独安装:

    import (
        "github.com/mitchellh/go-homedir"
        "github.com/docker/docker/pkg/archive"
    )
    
    func GetContext(filePath string) io.Reader {
        // Use homedir.Expand to resolve paths like '~/repos/myrepo'
        filePath, _ := homedir.Expand(filePath)
        ctx, _ := archive.TarWithOptions(filePath, &archive.TarOptions{})
        return ctx
    }
    
    cli.ImageBuild(context.Background(), GetContext("~/repos/myrepo"), types.ImageBuildOptions{...})
    

    【讨论】:

      【解决方案4】:

      我同意 Marcus Havranek 的回答,这种方法对我有用。只想添加如何为图像添加名称,因为这似乎是一个悬而未决的问题:

      buildOptions := types.ImageBuildOptions{
          Tags:   []string{"imagename"},
      }
      

      希望这会有所帮助!

      【讨论】:

        【解决方案5】:

        结合几个答案,并添加如何使用DisplayJSONMessagesToStream 正确解析返回的 JSON。

        package main
        
        import (
            "os"
            "log"
        
            "github.com/docker/docker/api/types"
            "github.com/docker/docker/pkg/archive"
            "github.com/docker/docker/pkg/jsonmessage"
            "github.com/docker/docker/pkg/term"
            "golang.org/x/net/context"
        )
        
        // Build a dockerfile if it exists
        func Build(dockerFilePath, buildContextPath string, tags []string) {
        
            ctx := context.Background()
            cli := getCLI()
        
            buildOpts := types.ImageBuildOptions{
                Dockerfile: dockerFilePath,
                Tags:       tags,
            }
        
            buildCtx, _ := archive.TarWithOptions(buildContextPath, &archive.TarOptions{})
        
            resp, err := cli.ImageBuild(ctx, buildCtx, buildOpts)
            if err != nil {
                log.Fatalf("build error - %s", err)
            }
            defer resp.Body.Close()
        
            termFd, isTerm := term.GetFdInfo(os.Stderr)
            jsonmessage.DisplayJSONMessagesStream(resp.Body, os.Stderr, termFd, isTerm, nil)
        }
        

        我已经留下了一些方便的功能,例如getCLI,但我相信你也有自己的等价物。

        【讨论】:

        • 谢谢。我想知道你为什么选择写信给 Stderr?为什么不是标准输出?
        【解决方案6】:

        我遇到同样的问题。最后发现 tar 文件应该是 docker build context,即使使用 Dockerfile。

        这是我的代码,

        package main
        
        import (
            "log"
            "os"
        
            "github.com/docker/docker/api/types"
            "github.com/docker/docker/client"
            "golang.org/x/net/context"
        )
        
        func main() {
            dockerBuildContext, err := os.Open("/Users/elsvent/workspace/Go/src/test/test.tar")
            defer dockerBuildContext.Close()
        
            buildOptions := types.ImageBuildOptions{
                SuppressOutput: true,
                PullParent:     true,
                Tags:           []string{"xxx"},
                Dockerfile:     "test/Dockerfile",
            }
            defaultHeaders := map[string]string{"Content-Type": "application/tar"}
            cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
            buildResp, err := cli.ImageBuild(context.Background(), dockerBuildContext, buildOptions)
            if err != nil {
            log.Fatal(err)
            }
            defer buildResp.Body.Close()
        }
        

        【讨论】:

          【解决方案7】:
          opt := types.ImageBuildOptions{
              Dockerfile: "Dockerfile",
          }
          
          filePath, _ = homedir.Expand(".")
          buildCtx, _ := archive.TarWithOptions(filePath, &archive.TarOptions{})
          
          x, err := cli.ImageBuild(context.Background(), buildCtx, opt)
          io.Copy(os.Stdout, x.Body)
          

          【讨论】:

          • 去获取 github.com/mitchellh/go-homedir
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-06-22
          • 2021-11-03
          • 1970-01-01
          • 1970-01-01
          • 2020-10-06
          • 1970-01-01
          • 2022-11-11
          相关资源
          最近更新 更多