#How to push container to local Docker

1 messages · Page 1 of 1 (latest)

delicate escarp
#

When I use buildx to create Docker images there is an option --load to push the built image to my local docker instance. This is super useful when debugging containers. Is there a similar option to do this in Dagger or is the only option to use Publish to push the built image to a remote registry?

cosmic urchin
#

This is not a core primitive in the Dagger API (to avoid excessive dependency on Docker), but you can implement the same thing as code on top of Dagger (or reuse that logic from someone else using a library)

#

One way I see is with these steps:

  1. Create your container with the Dagger API (type container) (example:
  2. Export the container image to your filesystem with container { export } (example: https://play.dagger.cloud/playground/nRBDKlh0OHw)
  3. Run the docker CLI in a container, with your client machine's docker engine unix socket mounted, and the exported container directory also mounted
  4. In that container, run docker load
#

It's not ideal because the image data has to be copied to the host filesystem, then back out. But in terms of performance, it's not materially different from what docker build --load does. Assuming you use /tmp or equivalent your host, it's all memory anyway.

delicate escarp
#

Awesome thanks so much Solomon, this is super useful step for when testing locally. I am currently building a little go wrapper I will add it here

cosmic urchin
#

in the future we should add support for streaming the image contents to a socket as a tar stream, then clients can skip the host filesystem

#

it’s not trivial because of how buildkit works

lone folio
#

hmm, is there some way to apply the steps above when using the SDK (Node in my case) instead of GraphQL API?

#

ideally I'd just change publish to load in the tutorial e.g. .publish('ttl.sh/hello-dagger-' + Math.floor(Math.random() * 10000000)) but sounds like it's non-trivial 😄

#

oh, I think I see now, you would use export to the local filesystem somewhere in the SDK and then manually docker load that? 🤔

#

OK, that works, was nice to find this at the top of #help 🙂

delicate escarp
#

I wonder if I am missing something, I am trying to export like so...

#
func exportLocalImage(builder *Builder, container *dagger.Container, platform dagger.Platform) {
    done := builder.LogSubSection(fmt.Sprintf("exporting %s", platform))

    imagePath := path.Join(os.TempDir(), "localexport")
    //defer os.RemoveAll(imagePath)

    // export the image to the local path
    _, err := builder.DaggerClient.
        Container(dagger.ContainerOpts{Platform: platform}).
        Export(
            builder.ctx,
            imagePath,
            dagger.ContainerExportOpts{PlatformVariants: []*dagger.Container{container}},
        )

    if err != nil {
        builder.LogError("error exporting image", err)
    }

    // run docker import to import to the local system
    cmd := exec.Command(
        "docker",
        "import",
        imagePath,
        fmt.Sprintf("%s:local", *dockerRegistry),
    )

    var out bytes.Buffer
    cmd.Stdout = &out
    cmd.Stderr = &out

    err = cmd.Run()
    if err != nil {
        builder.LogError("unable to import image", errors.New(out.String()))
    }

    done(fmt.Sprintf("export container complete for architecture %s", platform))
}
#

But when trying to run I get the following error

#
docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/bin/bash": stat /bin/bash: no such file or directory: unknown
#

When I do a publish this works fine

#

I think there must be an import problem, when I decompress the exported tarbal I can see all the right files

delicate escarp
#

My bad, I am doing an import not a load

distant crescent
#

@delicate escarp , did changing it to load work for you?

delicate escarp
#

Yes, thanks, I am just updating my example as there is a little more I need to do as load does not allow you add tags, it just uses the tags from export, of which there are none.

#

I just need to grab the sha from load and then tag

delicate escarp
#

This is what I finally came up with...

#
func exportLocalImage(builder *Builder, container *dagger.Container, platform dagger.Platform) error {
    done := builder.LogSubSection(fmt.Sprintf("exporting %s", platform))

    outputDirectory := path.Join(os.TempDir(), "build_output")
    os.MkdirAll(outputDirectory, os.ModePerm)

    imagePath := path.Join(outputDirectory, "localexport.tar")
    //defer os.RemoveAll(outputDirectory)

    // export the image to the local path
    _, err := builder.DaggerClient.
        Container(dagger.ContainerOpts{Platform: platform}).
        Export(
            builder.ctx,
            imagePath,
            dagger.ContainerExportOpts{PlatformVariants: []*dagger.Container{container}},
        )

    if err != nil {
        return err
    }

    // run docker import to import to the local system
    cmd := exec.Command(
        "docker",
        "load",
        "-i",
        imagePath,
    )

    out := &bytes.Buffer{}
    cmd.Stdout = out
    cmd.Stderr = out

    err = cmd.Run()
    if err != nil {
        return err
    }

    // get the sha from the output
    r, err := regexp.Compile(`sha256:(.*)`)
    if err != nil {
        return err
    }

    res := r.FindStringSubmatch(out.String())
    if len(res) != 2 {
        return fmt.Errorf("expected sha from docker load output, got: %s", out.String())
    }

    sha := res[1]
    cmd = exec.Command(
        "docker",
        "tag",
        sha,
        fmt.Sprintf("%s:local", *dockerRegistry),
    )

    out = &bytes.Buffer{}
    cmd.Stdout = out
    cmd.Stderr = out

    err = cmd.Run()
    if err != nil {
        return err
    }

    done(fmt.Sprintf("export container complete for architecture %s", platform))
    return nil
}