llb: update docs

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
docker-18.09
Akihiro Suda 2018-02-27 16:06:01 +09:00
parent ff7d75def0
commit ecf2e8dbc9
3 changed files with 63 additions and 10 deletions

View File

@ -58,7 +58,19 @@ We are open to adding more backends.
#### Exploring LLB #### Exploring LLB
BuildKit builds are based on a binary intermediate format called LLB that is used for defining the dependency graph for processes running part of your build. BuildKit builds are based on a binary intermediate format called LLB that is used for defining the dependency graph for processes running part of your build. tl;dr: LLB is to Dockerfile what LLVM IR is to C.
- Marshaled as Protobuf messages
- Concurrently executable
- Efficiently cacheable
- Vendor-neutral (i.e. non-Dockerfile languages can be easily implemented)
See [`solver/pb/ops.proto`](./solver/pb/ops.proto) for the format definition.
Currently, following high-level languages has been implemented for LLB:
- Dockerfile (See [Exploring Dockerfiles](#exploring-dockerfiles))
- (open a PR to add your own language)
For understanding the basics of LLB, `examples/buildkit*` directory contains scripts that define how to build different configurations of BuildKit itself and its dependencies using the `client` package. Running one of these scripts generates a protobuf definition of a build graph. Note that the script itself does not execute any steps of the build. For understanding the basics of LLB, `examples/buildkit*` directory contains scripts that define how to build different configurations of BuildKit itself and its dependencies using the `client` package. Running one of these scripts generates a protobuf definition of a build graph. Note that the script itself does not execute any steps of the build.

View File

@ -4,6 +4,9 @@
/* /*
Package pb is a generated protocol buffer package. Package pb is a generated protocol buffer package.
Package pb provides the protobuf definition of LLB: low-level builder instruction.
LLB is DAG-structured; Op represents a vertex, and Definition represents a graph.
It is generated from these files: It is generated from these files:
ops.proto ops.proto
@ -44,7 +47,9 @@ var _ = math.Inf
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
// Op represents a vertex of the LLB DAG.
type Op struct { type Op struct {
// inputs is a set of input edges.
Inputs []*Input `protobuf:"bytes,1,rep,name=inputs" json:"inputs,omitempty"` Inputs []*Input `protobuf:"bytes,1,rep,name=inputs" json:"inputs,omitempty"`
// Types that are valid to be assigned to Op: // Types that are valid to be assigned to Op:
// *Op_Exec // *Op_Exec
@ -237,9 +242,12 @@ func _Op_OneofSizer(msg proto.Message) (n int) {
return n return n
} }
// Input represents an input edge for an Op.
type Input struct { type Input struct {
// digest of the marshaled input Op
Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"`
Index OutputIndex `protobuf:"varint,2,opt,name=index,proto3,customtype=OutputIndex" json:"index"` // output index of the input Op
Index OutputIndex `protobuf:"varint,2,opt,name=index,proto3,customtype=OutputIndex" json:"index"`
} }
func (m *Input) Reset() { *m = Input{} } func (m *Input) Reset() { *m = Input{} }
@ -247,6 +255,7 @@ func (m *Input) String() string { return proto.CompactTextString(m) }
func (*Input) ProtoMessage() {} func (*Input) ProtoMessage() {}
func (*Input) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{1} } func (*Input) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{1} }
// ExecOp executes a command in a container.
type ExecOp struct { type ExecOp struct {
Meta *Meta `protobuf:"bytes,1,opt,name=meta" json:"meta,omitempty"` Meta *Meta `protobuf:"bytes,1,opt,name=meta" json:"meta,omitempty"`
Mounts []*Mount `protobuf:"bytes,2,rep,name=mounts" json:"mounts,omitempty"` Mounts []*Mount `protobuf:"bytes,2,rep,name=mounts" json:"mounts,omitempty"`
@ -271,6 +280,7 @@ func (m *ExecOp) GetMounts() []*Mount {
return nil return nil
} }
// Meta is a set of arguments for ExecOp.
// Meta is unrelated to LLB metadata. // Meta is unrelated to LLB metadata.
// FIXME: rename (ExecContext? ExecArgs?) // FIXME: rename (ExecContext? ExecArgs?)
type Meta struct { type Meta struct {
@ -313,6 +323,7 @@ func (m *Meta) GetUser() string {
return "" return ""
} }
// Mount specifies how to mount an input Op as a filesystem.
type Mount struct { type Mount struct {
Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"` Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"`
Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"` Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"`
@ -347,6 +358,7 @@ func (m *Mount) GetReadonly() bool {
return false return false
} }
// CopyOp copies files across Ops.
type CopyOp struct { type CopyOp struct {
Src []*CopySource `protobuf:"bytes,1,rep,name=src" json:"src,omitempty"` Src []*CopySource `protobuf:"bytes,1,rep,name=src" json:"src,omitempty"`
Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"` Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"`
@ -371,6 +383,7 @@ func (m *CopyOp) GetDest() string {
return "" return ""
} }
// CopySource specifies a source for CopyOp.
type CopySource struct { type CopySource struct {
Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"` Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"`
Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"` Selector string `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"`
@ -388,10 +401,13 @@ func (m *CopySource) GetSelector() string {
return "" return ""
} }
// SourceOp specifies a source such as build contexts and images.
type SourceOp struct { type SourceOp struct {
// source type? // TODO: use source type or any type instead of URL protocol.
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` // identifier e.g. local://, docker-image://, git://, https://...
Attrs map[string]string `protobuf:"bytes,2,rep,name=attrs" json:"attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
// attrs are defined in attr.go
Attrs map[string]string `protobuf:"bytes,2,rep,name=attrs" json:"attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
} }
func (m *SourceOp) Reset() { *m = SourceOp{} } func (m *SourceOp) Reset() { *m = SourceOp{} }
@ -413,6 +429,7 @@ func (m *SourceOp) GetAttrs() map[string]string {
return nil return nil
} }
// BuildOp is used for nested build invocation.
type BuildOp struct { type BuildOp struct {
Builder InputIndex `protobuf:"varint,1,opt,name=builder,proto3,customtype=InputIndex" json:"builder"` Builder InputIndex `protobuf:"varint,1,opt,name=builder,proto3,customtype=InputIndex" json:"builder"`
Inputs map[string]*BuildInput `protobuf:"bytes,2,rep,name=inputs" json:"inputs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` Inputs map[string]*BuildInput `protobuf:"bytes,2,rep,name=inputs" json:"inputs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
@ -446,6 +463,7 @@ func (m *BuildOp) GetAttrs() map[string]string {
return nil return nil
} }
// BuildInput is used for BuildOp.
type BuildInput struct { type BuildInput struct {
Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"` Input InputIndex `protobuf:"varint,1,opt,name=input,proto3,customtype=InputIndex" json:"input"`
} }
@ -455,8 +473,9 @@ func (m *BuildInput) String() string { return proto.CompactTextString
func (*BuildInput) ProtoMessage() {} func (*BuildInput) ProtoMessage() {}
func (*BuildInput) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{9} } func (*BuildInput) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{9} }
// OpMetadata is a per-vertex metadata entry, which can be defined for arbitrary Op vertex by both "script" and build client (e.g. buildctl). // OpMetadata is a per-vertex metadata entry, which can be defined for arbitrary Op vertex and overridable on the run time.
type OpMetadata struct { type OpMetadata struct {
// ignore_cache specifies to ignore the cache for this Op.
IgnoreCache bool `protobuf:"varint,1,opt,name=ignore_cache,json=ignoreCache,proto3" json:"ignore_cache,omitempty"` IgnoreCache bool `protobuf:"varint,1,opt,name=ignore_cache,json=ignoreCache,proto3" json:"ignore_cache,omitempty"`
// Description can be used for keeping any text fields that builder doesn't parse // Description can be used for keeping any text fields that builder doesn't parse
Description map[string]string `protobuf:"bytes,2,rep,name=description" json:"description,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Description map[string]string `protobuf:"bytes,2,rep,name=description" json:"description,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
@ -508,8 +527,10 @@ func (m *WorkerConstraint) GetFilter() []string {
// Definition is the LLB definition structure with per-vertex metadata entries // Definition is the LLB definition structure with per-vertex metadata entries
type Definition struct { type Definition struct {
// def is a list of marshaled Op messages
Def [][]byte `protobuf:"bytes,1,rep,name=def" json:"def,omitempty"` Def [][]byte `protobuf:"bytes,1,rep,name=def" json:"def,omitempty"`
// key = LLB op digest string. Currently, empty string is not expected but may change in the future. // metadata contains metadata for the each of the Op messages.
// A key must be an LLB op digest string. Currently, empty string is not expected as a key, but it may change in the future.
Metadata map[github_com_opencontainers_go_digest.Digest]OpMetadata `protobuf:"bytes,2,rep,name=metadata,castkey=github.com/opencontainers/go-digest.Digest" json:"metadata" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` Metadata map[github_com_opencontainers_go_digest.Digest]OpMetadata `protobuf:"bytes,2,rep,name=metadata,castkey=github.com/opencontainers/go-digest.Digest" json:"metadata" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
} }

View File

@ -1,10 +1,14 @@
syntax = "proto3"; syntax = "proto3";
// Package pb provides the protobuf definition of LLB: low-level builder instruction.
// LLB is DAG-structured; Op represents a vertex, and Definition represents a graph.
package pb; package pb;
import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto";
// Op represents a vertex of the LLB DAG.
message Op { message Op {
// inputs is a set of input edges.
repeated Input inputs = 1; repeated Input inputs = 1;
oneof op { oneof op {
ExecOp exec = 2; ExecOp exec = 2;
@ -14,16 +18,21 @@ message Op {
} }
} }
// Input represents an input edge for an Op.
message Input { message Input {
// digest of the marshaled input Op
string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; string digest = 1 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
// output index of the input Op
int64 index = 2 [(gogoproto.customtype) = "OutputIndex", (gogoproto.nullable) = false]; int64 index = 2 [(gogoproto.customtype) = "OutputIndex", (gogoproto.nullable) = false];
} }
// ExecOp executes a command in a container.
message ExecOp { message ExecOp {
Meta meta = 1; Meta meta = 1;
repeated Mount mounts = 2; repeated Mount mounts = 2;
} }
// Meta is a set of arguments for ExecOp.
// Meta is unrelated to LLB metadata. // Meta is unrelated to LLB metadata.
// FIXME: rename (ExecContext? ExecArgs?) // FIXME: rename (ExecContext? ExecArgs?)
message Meta { message Meta {
@ -33,6 +42,7 @@ message Meta {
string user = 4; string user = 4;
} }
// Mount specifies how to mount an input Op as a filesystem.
message Mount { message Mount {
int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false];
string selector = 2; string selector = 2;
@ -41,22 +51,28 @@ message Mount {
bool readonly = 5; bool readonly = 5;
} }
// CopyOp copies files across Ops.
message CopyOp { message CopyOp {
repeated CopySource src = 1; repeated CopySource src = 1;
string dest = 2; string dest = 2;
} }
// CopySource specifies a source for CopyOp.
message CopySource { message CopySource {
int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false];
string selector = 2; string selector = 2;
} }
// SourceOp specifies a source such as build contexts and images.
message SourceOp { message SourceOp {
// source type? // TODO: use source type or any type instead of URL protocol.
// identifier e.g. local://, docker-image://, git://, https://...
string identifier = 1; string identifier = 1;
// attrs are defined in attr.go
map<string, string> attrs = 2; map<string, string> attrs = 2;
} }
// BuildOp is used for nested build invocation.
message BuildOp { message BuildOp {
int64 builder = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; int64 builder = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false];
map<string, BuildInput> inputs = 2; map<string, BuildInput> inputs = 2;
@ -65,12 +81,14 @@ message BuildOp {
// outputs // outputs
} }
// BuildInput is used for BuildOp.
message BuildInput { message BuildInput {
int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false]; int64 input = 1 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false];
} }
// OpMetadata is a per-vertex metadata entry, which can be defined for arbitrary Op vertex by both "script" and build client (e.g. buildctl). // OpMetadata is a per-vertex metadata entry, which can be defined for arbitrary Op vertex and overridable on the run time.
message OpMetadata { message OpMetadata {
// ignore_cache specifies to ignore the cache for this Op.
bool ignore_cache = 1; bool ignore_cache = 1;
// Description can be used for keeping any text fields that builder doesn't parse // Description can be used for keeping any text fields that builder doesn't parse
map<string, string> description = 2; map<string, string> description = 2;
@ -84,7 +102,9 @@ message WorkerConstraint {
// Definition is the LLB definition structure with per-vertex metadata entries // Definition is the LLB definition structure with per-vertex metadata entries
message Definition { message Definition {
// def is a list of marshaled Op messages
repeated bytes def = 1; repeated bytes def = 1;
// key = LLB op digest string. Currently, empty string is not expected but may change in the future. // metadata contains metadata for the each of the Op messages.
// A key must be an LLB op digest string. Currently, empty string is not expected as a key, but it may change in the future.
map<string, OpMetadata> metadata = 2 [(gogoproto.castkey) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; map<string, OpMetadata> metadata = 2 [(gogoproto.castkey) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
} }