Skip to content

Commit

Permalink
example: add build-using-dockerfile
Browse files Browse the repository at this point in the history
This command mimics `docker build` CLI flags so that Docker users can
more easily get started with BuildKit.

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
  • Loading branch information
AkihiroSuda committed Dec 18, 2017
1 parent 5fa37d1 commit 742f567
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 7 deletions.
51 changes: 44 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,57 @@ Key features:

Read the proposal from https://github.com/moby/moby/issues/32925

#### Quick start
#### Quick start for Docker users in 5 steps

Dependencies:
- [runc](https://github.com/opencontainers/runc)
- [containerd](https://github.com/containerd/containerd) (if you want to use containerd worker)
1. Install [runc](https://github.com/opencontainers/runc)


The following command installs `buildd` and `buildctl` to `/usr/local/bin`:
2. Install BuildKit and the `build-using-dockerfile` example to `/usr/local/bin`:

```bash
$ make && sudo make install
$ go build ./examples/build-using-dockerfile && sudo install build-using-dockerfile /usr/local/bin
```

3. Start the BuildKit daemon `buildd`:
```bash
$ sudo buildd
```

4. Build your Dockerfile with the `build-using-dockerfile` example.

```bash
$ cd your-docker-app
$ ls
Dockerfile
...
$ sudo build-using-dockerfile .
$ ls
Dockerfile
oci.tar
...
```

You can omit `sudo` if the user can access `/run/buildkit/buildd.sock`.

5. Import the build artifact `oci.tar` to Docker:

If you use [skopeo](https://github.com/projectatomic/skopeo):
```bash
$ skopeo copy oci-archive:oci.tar docker-daemon:foo/bar:latest
```

You can also use `make binaries-all` to prepare `buildd-containerd` (containerd worker only) and `buildd-standalone` (OCI worker only).
------------------------------

## Advanced guide

#### Installation and usage

Dependencies:
- [runc](https://github.com/opencontainers/runc)
- [containerd](https://github.com/containerd/containerd) (if you want to use containerd worker)


In addition to `make`, you can also use `make binaries-all` to prepare `buildd-containerd` (containerd worker only) and `buildd-standalone` (OCI worker only).

`examples/buildkit*` directory contains scripts that define how to build different configurations of BuildKit and its dependencies using the `client` package. Running one of these script generates a protobuf definition of a build graph. Note that the script itself does not execute any steps of the build.

Expand Down
155 changes: 155 additions & 0 deletions examples/build-using-dockerfile/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package main

import (
"context"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/containerd/console"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/appdefaults"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/sync/errgroup"
)

func main() {
app := cli.NewApp()
app.Name = "build-using-dockerfile"
app.UsageText = `build-using-dockerfile [OPTIONS] PATH | URL | -`
app.Description = `
build using Dockerfile.
This command mimics behavior of "docker build" command so that people can easily get started with BuildKit.
This command is NOT the replacement of "docker build", and should NOT be used for building production images.
By default, the built image is exported as a tar archive with OCI Image Layout ("./oci.tar").
You can import oci.tar to Docker as follows:
$ skopeo copy oci-archive:oci.tar docker-daemon:foo/bar:latest
`
// TODO: call `docker load` rather than creating a file
exporterOptDefault := cli.StringSlice([]string{"output=./oci.tar"})
dockerIncompatibleFlags := []cli.Flag{
cli.StringFlag{
Name: "buildkit-exporter",
Usage: "Define exporter for build result",
Value: "oci", // TODO: docker v1 exporter (unless moby supports OCI importer)
},
cli.StringSliceFlag{
Name: "buildkit-exporter-opt",
Usage: "Define custom options for exporter",
Value: &exporterOptDefault,
},
cli.StringFlag{
Name: "buildkit-addr",
Usage: "listening address",
EnvVar: "BUILDKIT_HOST",
Value: appdefaults.Address,
},
}
app.Flags = append([]cli.Flag{
cli.StringFlag{
Name: "file, f",
Usage: "Name of the Dockerfile (Default is 'PATH/Dockerfile')",
},
cli.StringFlag{
Name: "target",
Usage: "Set the target build stage to build.",
},
cli.StringSliceFlag{
Name: "build-arg",
Usage: "Set build-time variables",
},
}, dockerIncompatibleFlags...)
app.Action = action
if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}

func action(clicontext *cli.Context) error {
c, err := client.New(clicontext.GlobalString("buildkit-addr"), client.WithBlock())
if err != nil {
return err
}
solveOpt, err := newSolveOpt(clicontext)
if err != nil {
return err
}
ch := make(chan *client.SolveStatus)
eg, ctx := errgroup.WithContext(appcontext.Context())
eg.Go(func() error {
return c.Solve(ctx, nil, *solveOpt, ch)
})
eg.Go(func() error {
if c, err := console.ConsoleFromFile(os.Stderr); err == nil {
// not using shared context to not disrupt display but let is finish reporting errors
return progressui.DisplaySolveStatus(context.TODO(), c, ch)
}
return nil
})
return eg.Wait()
}

func newSolveOpt(clicontext *cli.Context) (*client.SolveOpt, error) {
buildCtx := clicontext.Args().First()
if buildCtx == "" {
return nil, errors.New("please specify build context (e.g. \".\" for the current directory)")
} else if buildCtx == "-" {
return nil, errors.New("stdin not supported yet")
}

file := clicontext.String("file")
if file == "" {
file = filepath.Join(buildCtx, "Dockerfile")
}
exporterAttrs, err := attrMap(clicontext.StringSlice("buildkit-exporter-opt"))
if err != nil {
return nil, errors.Wrap(err, "invalid buildkit-exporter-opt")
}

localDirs := map[string]string{
"context": buildCtx,
"dockerfile": filepath.Dir(file),
}

frontendAttrs := map[string]string{
"filename": filepath.Base(file),
}
if target := clicontext.String("target"); target != "" {
frontendAttrs["target"] = target
}
buildArgs, err := attrMap(clicontext.StringSlice("build-arg"))
if err != nil {
return nil, err
}
for k, v := range buildArgs {
frontendAttrs["build-arg:"+k] = v
}
return &client.SolveOpt{
Exporter: clicontext.String("buildkit-exporter"),
ExporterAttrs: exporterAttrs,
LocalDirs: localDirs,
Frontend: "dockerfile.v0", // TODO: use gateway
FrontendAttrs: frontendAttrs,
}, nil
}

func attrMap(sl []string) (map[string]string, error) {
m := map[string]string{}
for _, v := range sl {
parts := strings.SplitN(v, "=", 2)
if len(parts) != 2 {
return nil, errors.Errorf("invalid value %s", v)
}
m[parts[0]] = parts[1]
}
return m, nil
}

0 comments on commit 742f567

Please sign in to comment.