从Go代码构建Docker镜像

Man*_*das 6 go docker dockerfile docker-api

我正在尝试使用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

错误始终相同:

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

要么

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

我检查过的事情:

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

我的另一个选择是使用RemoteContext它看起来像它的工作原理,但仅适用于完全自包含的dockerfiles,而不是具有"本地文件存在"的那些.


更新: 尝试传递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)
Run Code Online (Sandbox Code Playgroud)

小智 9

@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()
Run Code Online (Sandbox Code Playgroud)

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


kyl*_*e.a 6

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{...})
Run Code Online (Sandbox Code Playgroud)


小智 5

我同意马库斯·哈夫拉内克的回答,这种方法对我有用。只是想添加如何向图像添加名称,因为这似乎是一个悬而未决的问题:

buildOptions := types.ImageBuildOptions{
    Tags:   []string{"imagename"},
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!


Kom*_*omu 5

以下为我工作;

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 喜欢您使用本机“archive/tar”库来生成 tar,此代码从现在起甚至可以维护多年。 (2认同)

小智 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)
}
Run Code Online (Sandbox Code Playgroud)

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