Skip to content

Commit

Permalink
feat: buildx multiplatform images w/ buildkit
Browse files Browse the repository at this point in the history
Signed-off-by: <[email protected]>
  • Loading branch information
reingart committed Jan 31, 2025
1 parent c608380 commit 9bafc7c
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 13 deletions.
7 changes: 5 additions & 2 deletions pkg/skaffold/build/builder_mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type BuilderMux struct {
byImageName map[string]PipelineBuilder
store ArtifactStore
concurrency int
buildx bool
cache Cache
}

Expand Down Expand Up @@ -71,7 +72,8 @@ func NewBuilderMux(cfg Config, store ArtifactStore, cache Cache, builder func(p
}
}
concurrency := getConcurrency(pbs, cfg.BuildConcurrency())
return &BuilderMux{builders: pbs, byImageName: m, store: store, concurrency: concurrency, cache: cache}, nil
buildx := config.GetDetectBuildX(cfg.GlobalConfig())
return &BuilderMux{builders: pbs, byImageName: m, store: store, concurrency: concurrency, cache: cache, buildx: buildx}, nil
}

// Build executes the specific image builder for each artifact in the given artifact slice.
Expand Down Expand Up @@ -106,7 +108,8 @@ func (b *BuilderMux) Build(ctx context.Context, out io.Writer, tags tag.ImageTag
}
var built string

if platforms.IsMultiPlatform() && !SupportsMultiPlatformBuild(*artifact) {
// buildx creates multiplatform images via buildkit directly
if platforms.IsMultiPlatform() && !SupportsMultiPlatformBuild(*artifact) && !b.buildx {
built, err = CreateMultiPlatformImage(ctx, out, artifact, tag, platforms, artifactBuilder)
} else {
built, err = artifactBuilder(ctx, out, artifact, tag, platforms)
Expand Down
35 changes: 24 additions & 11 deletions pkg/skaffold/build/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"io"
"os"
"os/exec"
"strings"

v1 "github.com/google/go-containerregistry/pkg/v1"

Expand All @@ -44,9 +45,13 @@ func (b *Builder) SupportedPlatforms() platform.Matcher {
}

func (b *Builder) Build(ctx context.Context, out io.Writer, a *latest.Artifact, tag string, matcher platform.Matcher) (string, error) {
var pl v1.Platform
if len(matcher.Platforms) == 1 {
pl = util.ConvertToV1Platform(matcher.Platforms[0])
var pls []v1.Platform
if len(matcher.Platforms) > 0 {
for _, plat := range matcher.Platforms {
pls = append(pls, util.ConvertToV1Platform(plat))
}
} else {
pls = append(pls, v1.Platform{})
}
a = b.adjustCache(ctx, a, tag)
instrumentation.AddAttributesToCurrentSpanFromContext(ctx, map[string]string{
Expand All @@ -64,8 +69,10 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, a *latest.Artifact,
return "", dockerfileNotFound(err, a.ImageName)
}

if err := b.pullCacheFromImages(ctx, out, a.ArtifactType.DockerArtifact, pl); err != nil {
return "", cacheFromPullErr(err, a.ImageName)
for _, pl := range pls {
if err := b.pullCacheFromImages(ctx, out, a.ArtifactType.DockerArtifact, pl); err != nil {
return "", cacheFromPullErr(err, a.ImageName)
}
}
opts := docker.BuildOptions{Tag: tag, Mode: b.cfg.Mode(), ExtraBuildArgs: docker.ResolveDependencyImages(a.Dependencies, b.artifacts, true)}

Expand All @@ -75,7 +82,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, a *latest.Artifact,
// we might consider a different approach in the future.
// use CLI for cross-platform builds
if b.useCLI || (b.useBuildKit != nil && *b.useBuildKit) || len(a.DockerArtifact.CliFlags) > 0 || matcher.IsCrossPlatform() {
imageID, err = b.dockerCLIBuild(ctx, output.GetUnderlyingWriter(out), a.ImageName, a.Workspace, dockerfile, a.ArtifactType.DockerArtifact, opts, pl)
imageID, err = b.dockerCLIBuild(ctx, output.GetUnderlyingWriter(out), a.ImageName, a.Workspace, dockerfile, a.ArtifactType.DockerArtifact, opts, pls)
} else {
imageID, err = b.localDocker.Build(ctx, out, a.Workspace, a.ImageName, a.ArtifactType.DockerArtifact, opts)
}
Expand All @@ -93,7 +100,7 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, a *latest.Artifact,
return imageID, nil
}

func (b *Builder) dockerCLIBuild(ctx context.Context, out io.Writer, name string, workspace string, dockerfilePath string, a *latest.DockerArtifact, opts docker.BuildOptions, pl v1.Platform) (string, error) {
func (b *Builder) dockerCLIBuild(ctx context.Context, out io.Writer, name string, workspace string, dockerfilePath string, a *latest.DockerArtifact, opts docker.BuildOptions, pls []v1.Platform) (string, error) {
args := []string{"build", workspace, "--file", dockerfilePath, "-t", opts.Tag}
imgRef, err := docker.ParseReference(opts.Tag)
if err != nil {
Expand All @@ -118,8 +125,14 @@ func (b *Builder) dockerCLIBuild(ctx context.Context, out io.Writer, name string
args = append(args, "--force-rm")
}

if pl.String() != "" {
args = append(args, "--platform", pl.String())
var platforms []string
for _, pl := range pls {
if pl.String() != "" {
platforms = append(platforms, pl.String())
}
}
if len(platforms) > 0 {
args = append(args, "--platform", strings.Join(platforms, ","))
}

if b.useBuildKit != nil && *b.useBuildKit {
Expand Down Expand Up @@ -161,8 +174,8 @@ func (b *Builder) dockerCLIBuild(ctx context.Context, out io.Writer, name string
} else {
cmd.Env = append(cmd.Env, "DOCKER_BUILDKIT=0")
}
} else if pl.String() != "" { // cross-platform builds require buildkit
log.Entry(ctx).Debugf("setting DOCKER_BUILDKIT=1 for docker build for artifact %q since it targets platform %q", name, pl.String())
} else if len(platforms) > 0 { // cross-platform builds require buildkit
log.Entry(ctx).Debugf("setting DOCKER_BUILDKIT=1 for docker build for artifact %q since it targets platform %q", name, platforms[0])
cmd.Env = append(cmd.Env, "DOCKER_BUILDKIT=1")
}
cmd.Stdout = out
Expand Down

0 comments on commit 9bafc7c

Please sign in to comment.