mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-20 09:39:08 +00:00
Merge pull request #3823 from giggsoff/clean_builder_cache
Builder disk usage and clean
This commit is contained in:
commit
5f3856c94c
@ -12,6 +12,7 @@ func pkgUsage() {
|
||||
|
||||
fmt.Printf("'subcommand' is one of:\n")
|
||||
fmt.Printf(" build\n")
|
||||
fmt.Printf(" builder\n")
|
||||
fmt.Printf(" push\n")
|
||||
fmt.Printf(" show-tag\n")
|
||||
fmt.Printf("\n")
|
||||
@ -28,6 +29,8 @@ func pkg(args []string) {
|
||||
switch args[0] {
|
||||
case "build":
|
||||
pkgBuild(args[1:])
|
||||
case "builder":
|
||||
pkgBuilder(args[1:])
|
||||
case "push":
|
||||
pkgPush(args[1:])
|
||||
case "show-tag":
|
||||
|
84
src/cmd/linuxkit/pkg_builder.go
Normal file
84
src/cmd/linuxkit/pkg_builder.go
Normal file
@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/pkglib"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func pkgBuilderUsage() {
|
||||
invoked := filepath.Base(os.Args[0])
|
||||
fmt.Printf("USAGE: %s builder command [options]\n\n", invoked)
|
||||
fmt.Printf("Supported commands are\n")
|
||||
// Please keep these in alphabetical order
|
||||
fmt.Printf(" du\n")
|
||||
fmt.Printf(" prune\n")
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("'options' are the backend specific options.\n")
|
||||
fmt.Printf("See '%s builder [command] --help' for details.\n\n", invoked)
|
||||
}
|
||||
|
||||
// Process the builder
|
||||
func pkgBuilder(args []string) {
|
||||
if len(args) < 1 {
|
||||
pkgBuilderUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
switch args[0] {
|
||||
// Please keep cases in alphabetical order
|
||||
case "du":
|
||||
pkgBuilderCommands(args[0], args[1:])
|
||||
case "prune":
|
||||
pkgBuilderCommands(args[0], args[1:])
|
||||
case "help", "-h", "-help", "--help":
|
||||
pkgBuilderUsage()
|
||||
os.Exit(0)
|
||||
default:
|
||||
log.Errorf("No 'builder' command specified.")
|
||||
}
|
||||
}
|
||||
|
||||
func pkgBuilderCommands(command string, args []string) {
|
||||
flags := flag.NewFlagSet(command, flag.ExitOnError)
|
||||
builders := flags.String("builders", "", "Which builders to use for which platforms, e.g. linux/arm64=docker-context-arm64, overrides defaults and environment variables, see https://github.com/linuxkit/linuxkit/blob/master/docs/packages.md#Providing-native-builder-nodes")
|
||||
platforms := flags.String("platforms", fmt.Sprintf("linux/%s", runtime.GOARCH), "Which platforms we built images for")
|
||||
builderImage := flags.String("builder-image", defaultBuilderImage, "buildkit builder container image to use")
|
||||
verbose := flags.Bool("v", false, "Verbose output")
|
||||
if err := flags.Parse(args); err != nil {
|
||||
log.Fatal("Unable to parse args")
|
||||
}
|
||||
// build the builders map
|
||||
buildersMap := make(map[string]string)
|
||||
// look for builders env var
|
||||
buildersMap, err := buildPlatformBuildersMap(os.Getenv(buildersEnvVar), buildersMap)
|
||||
if err != nil {
|
||||
log.Fatalf("%s in environment variable %s\n", err.Error(), buildersEnvVar)
|
||||
}
|
||||
// any CLI options override env var
|
||||
buildersMap, err = buildPlatformBuildersMap(*builders, buildersMap)
|
||||
if err != nil {
|
||||
log.Fatalf("%s in --builders flag\n", err.Error())
|
||||
}
|
||||
|
||||
platformsToClean := strings.Split(*platforms, ",")
|
||||
switch command {
|
||||
case "du":
|
||||
if err := pkglib.DiskUsage(buildersMap, *builderImage, platformsToClean, *verbose); err != nil {
|
||||
log.Fatalf("Unable to print disk usage of builder: %v", err)
|
||||
}
|
||||
case "prune":
|
||||
if err := pkglib.PruneBuilder(buildersMap, *builderImage, platformsToClean, *verbose); err != nil {
|
||||
log.Fatalf("Unable to prune builder: %v", err)
|
||||
}
|
||||
default:
|
||||
log.Errorf("unexpected command %s", command)
|
||||
pkgBuilderUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||
lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||
buildkitClient "github.com/moby/buildkit/client"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@ -52,7 +53,9 @@ func (d *dockerMocker) contextSupportCheck() error {
|
||||
}
|
||||
return errors.New("contexts not supported")
|
||||
}
|
||||
|
||||
func (d *dockerMocker) builder(_ context.Context, _, _, _ string, _ bool) (*buildkitClient.Client, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
func (d *dockerMocker) build(ctx context.Context, tag, pkg, dockerContext, builderImage, platform string, builderRestart bool, c spec.CacheProvider, r io.Reader, stdout io.Writer, imageBuildOpts dockertypes.ImageBuildOptions) error {
|
||||
if !d.enableBuild {
|
||||
return errors.New("build disabled")
|
||||
|
@ -57,6 +57,7 @@ type dockerRunner interface {
|
||||
load(src io.Reader) error
|
||||
pull(img string) (bool, error)
|
||||
contextSupportCheck() error
|
||||
builder(ctx context.Context, dockerContext, builderImage, platform string, restart bool) (*buildkitClient.Client, error)
|
||||
}
|
||||
|
||||
type dockerRunnerImpl struct {
|
||||
|
183
src/cmd/linuxkit/pkglib/utils.go
Normal file
183
src/cmd/linuxkit/pkglib/utils.go
Normal file
@ -0,0 +1,183 @@
|
||||
package pkglib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/go-units"
|
||||
buildkitClient "github.com/moby/buildkit/client"
|
||||
)
|
||||
|
||||
func printTableHeader(tw *tabwriter.Writer) {
|
||||
fmt.Fprintln(tw, "ID\tRECLAIMABLE\tSIZE\tLAST ACCESSED")
|
||||
}
|
||||
|
||||
func printTableRow(tw *tabwriter.Writer, di *buildkitClient.UsageInfo) {
|
||||
id := di.ID
|
||||
if di.Mutable {
|
||||
id += "*"
|
||||
}
|
||||
size := units.HumanSize(float64(di.Size))
|
||||
if di.Shared {
|
||||
size += "*"
|
||||
}
|
||||
lastAccessed := ""
|
||||
if di.LastUsedAt != nil {
|
||||
lastAccessed = units.HumanDuration(time.Since(*di.LastUsedAt)) + " ago"
|
||||
}
|
||||
fmt.Fprintf(tw, "%-40s\t%-5v\t%-10s\t%s\n", id, !di.InUse, size, lastAccessed)
|
||||
}
|
||||
|
||||
func printSummary(tw *tabwriter.Writer, du []*buildkitClient.UsageInfo) {
|
||||
total := int64(0)
|
||||
reclaimable := int64(0)
|
||||
shared := int64(0)
|
||||
|
||||
for _, di := range du {
|
||||
if di.Size > 0 {
|
||||
total += di.Size
|
||||
if !di.InUse {
|
||||
reclaimable += di.Size
|
||||
}
|
||||
}
|
||||
if di.Shared {
|
||||
shared += di.Size
|
||||
}
|
||||
}
|
||||
|
||||
if shared > 0 {
|
||||
fmt.Fprintf(tw, "Shared:\t%s\n", units.HumanSize(float64(shared)))
|
||||
fmt.Fprintf(tw, "Private:\t%s\n", units.HumanSize(float64(total-shared)))
|
||||
}
|
||||
|
||||
fmt.Fprintf(tw, "Reclaimable:\t%s\n", units.HumanSize(float64(reclaimable)))
|
||||
fmt.Fprintf(tw, "Total:\t%s\n", units.HumanSize(float64(total)))
|
||||
tw.Flush()
|
||||
}
|
||||
|
||||
func printKV(w io.Writer, k string, v interface{}) {
|
||||
fmt.Fprintf(w, "%s:\t%v\n", k, v)
|
||||
}
|
||||
|
||||
func printVerbose(tw *tabwriter.Writer, du []*buildkitClient.UsageInfo) {
|
||||
for _, di := range du {
|
||||
printKV(tw, "ID", di.ID)
|
||||
if len(di.Parents) != 0 {
|
||||
printKV(tw, "Parent", strings.Join(di.Parents, ","))
|
||||
}
|
||||
printKV(tw, "Created at", di.CreatedAt)
|
||||
printKV(tw, "Mutable", di.Mutable)
|
||||
printKV(tw, "Reclaimable", !di.InUse)
|
||||
printKV(tw, "Shared", di.Shared)
|
||||
printKV(tw, "Size", units.HumanSize(float64(di.Size)))
|
||||
if di.Description != "" {
|
||||
printKV(tw, "Description", di.Description)
|
||||
}
|
||||
printKV(tw, "Usage count", di.UsageCount)
|
||||
if di.LastUsedAt != nil {
|
||||
printKV(tw, "Last used", units.HumanDuration(time.Since(*di.LastUsedAt))+" ago")
|
||||
}
|
||||
if di.RecordType != "" {
|
||||
printKV(tw, "Type", di.RecordType)
|
||||
}
|
||||
|
||||
fmt.Fprintf(tw, "\n")
|
||||
}
|
||||
|
||||
tw.Flush()
|
||||
}
|
||||
|
||||
func getClientForPlatform(ctx context.Context, buildersMap map[string]string, builderImage, platform string) (*buildkitClient.Client, error) {
|
||||
p, err := platforms.Parse(platform)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse platform: %s", err)
|
||||
}
|
||||
dr := newDockerRunner(false)
|
||||
builderName := getBuilderForPlatform(p.Architecture, buildersMap)
|
||||
client, err := dr.builder(ctx, builderName, builderImage, platform, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to ensure builder container: %v", err)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// DiskUsage of builder
|
||||
func DiskUsage(buildersMap map[string]string, builderImage string, platformsToClean []string, verbose bool) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
for _, platform := range platformsToClean {
|
||||
client, err := getClientForPlatform(ctx, buildersMap, builderImage, platform)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get client: %s", err)
|
||||
}
|
||||
|
||||
du, err := client.DiskUsage(ctx)
|
||||
if err != nil {
|
||||
_ = client.Close()
|
||||
return err
|
||||
}
|
||||
err = client.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot close client: %s", err)
|
||||
}
|
||||
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0)
|
||||
if len(du) > 0 {
|
||||
if verbose {
|
||||
printVerbose(tw, du)
|
||||
} else {
|
||||
printTableHeader(tw)
|
||||
for _, di := range du {
|
||||
printTableRow(tw, di)
|
||||
}
|
||||
}
|
||||
}
|
||||
printSummary(tw, du)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PruneBuilder clean build cache of builder
|
||||
func PruneBuilder(buildersMap map[string]string, builderImage string, platformsToClean []string, verbose bool) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
total := int64(0)
|
||||
for _, platform := range platformsToClean {
|
||||
client, err := getClientForPlatform(ctx, buildersMap, builderImage, platform)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get client: %s", err)
|
||||
}
|
||||
|
||||
ch := make(chan buildkitClient.UsageInfo)
|
||||
processed := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
defer close(processed)
|
||||
for du := range ch {
|
||||
if verbose {
|
||||
fmt.Printf("%s\t%s\tremoved\n", du.ID, units.HumanSize(float64(du.Size)))
|
||||
}
|
||||
total += du.Size
|
||||
}
|
||||
}()
|
||||
err = client.Prune(ctx, ch)
|
||||
if err != nil {
|
||||
_ = client.Close()
|
||||
close(ch)
|
||||
return err
|
||||
}
|
||||
err = client.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot close client: %s", err)
|
||||
}
|
||||
close(ch)
|
||||
<-processed
|
||||
}
|
||||
fmt.Printf("Reclaimed:\t%s\n", units.BytesSize(float64(total)))
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user