gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/cmd/checkpoint.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cmd
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  
    22  	"github.com/google/subcommands"
    23  	"gvisor.dev/gvisor/pkg/sentry/pgalloc"
    24  	"gvisor.dev/gvisor/pkg/state/statefile"
    25  	"gvisor.dev/gvisor/runsc/cmd/util"
    26  	"gvisor.dev/gvisor/runsc/config"
    27  	"gvisor.dev/gvisor/runsc/container"
    28  	"gvisor.dev/gvisor/runsc/flag"
    29  )
    30  
    31  // Checkpoint implements subcommands.Command for the "checkpoint" command.
    32  type Checkpoint struct {
    33  	imagePath                 string
    34  	leaveRunning              bool
    35  	compression               CheckpointCompression
    36  	excludeCommittedZeroPages bool
    37  
    38  	// direct indicates whether O_DIRECT should be used for writing the
    39  	// checkpoint pages file. It bypasses the kernel page cache. It is beneficial
    40  	// if the checkpoint files are not expected to be read again on this host.
    41  	// For example, if the checkpoint files will be stored on a network block
    42  	// device, which will be detached after the checkpoint is done.
    43  	direct bool
    44  }
    45  
    46  // Name implements subcommands.Command.Name.
    47  func (*Checkpoint) Name() string {
    48  	return "checkpoint"
    49  }
    50  
    51  // Synopsis implements subcommands.Command.Synopsis.
    52  func (*Checkpoint) Synopsis() string {
    53  	return "checkpoint current state of container (experimental)"
    54  }
    55  
    56  // Usage implements subcommands.Command.Usage.
    57  func (*Checkpoint) Usage() string {
    58  	return `checkpoint [flags] <container id> - save current state of container.
    59  `
    60  }
    61  
    62  // SetFlags implements subcommands.Command.SetFlags.
    63  func (c *Checkpoint) SetFlags(f *flag.FlagSet) {
    64  	f.StringVar(&c.imagePath, "image-path", "", "directory path to saved container image")
    65  	f.BoolVar(&c.leaveRunning, "leave-running", false, "restart the container after checkpointing")
    66  	f.Var(newCheckpointCompressionValue(statefile.CompressionLevelDefault, &c.compression), "compression", "compress checkpoint image on disk. Values: none|flate-best-speed.")
    67  	f.BoolVar(&c.excludeCommittedZeroPages, "exclude-committed-zero-pages", false, "exclude committed zero-filled pages from checkpoint")
    68  	f.BoolVar(&c.direct, "direct", false, "use O_DIRECT for writing checkpoint pages file")
    69  
    70  	// Unimplemented flags necessary for compatibility with docker.
    71  	var wp string
    72  	f.StringVar(&wp, "work-path", "", "ignored")
    73  }
    74  
    75  // Execute implements subcommands.Command.Execute.
    76  func (c *Checkpoint) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcommands.ExitStatus {
    77  	if f.NArg() != 1 {
    78  		f.Usage()
    79  		return subcommands.ExitUsageError
    80  	}
    81  
    82  	id := f.Arg(0)
    83  	conf := args[0].(*config.Config)
    84  
    85  	cont, err := container.Load(conf.RootDir, container.FullID{ContainerID: id}, container.LoadOpts{})
    86  	if err != nil {
    87  		util.Fatalf("loading container: %v", err)
    88  	}
    89  
    90  	if c.imagePath == "" {
    91  		util.Fatalf("image-path flag must be provided")
    92  	}
    93  
    94  	if err := os.MkdirAll(c.imagePath, 0755); err != nil {
    95  		util.Fatalf("making directories at path provided: %v", err)
    96  	}
    97  
    98  	sOpts := statefile.Options{
    99  		Compression: c.compression.Level(),
   100  	}
   101  	mfOpts := pgalloc.SaveOpts{
   102  		ExcludeCommittedZeroPages: c.excludeCommittedZeroPages,
   103  	}
   104  
   105  	if c.leaveRunning {
   106  		// Do not destroy the sandbox after saving.
   107  		sOpts.Resume = true
   108  	}
   109  
   110  	if err := cont.Checkpoint(c.imagePath, c.direct, sOpts, mfOpts); err != nil {
   111  		util.Fatalf("checkpoint failed: %v", err)
   112  	}
   113  
   114  	return subcommands.ExitSuccess
   115  }
   116  
   117  // CheckpointCompression represents checkpoint image writer behavior. The
   118  // default behavior is to compress because the default behavior used to be to
   119  // always compress.
   120  type CheckpointCompression statefile.CompressionLevel
   121  
   122  func newCheckpointCompressionValue(val statefile.CompressionLevel, p *CheckpointCompression) *CheckpointCompression {
   123  	*p = CheckpointCompression(val)
   124  	return (*CheckpointCompression)(p)
   125  }
   126  
   127  // Set implements flag.Value.
   128  func (g *CheckpointCompression) Set(v string) error {
   129  	t, err := statefile.CompressionLevelFromString(v)
   130  	if err != nil {
   131  		return fmt.Errorf("invalid checkpoint compression type %q", v)
   132  	}
   133  
   134  	*g = CheckpointCompression(t)
   135  
   136  	return nil
   137  }
   138  
   139  // Get implements flag.Getter.
   140  func (g *CheckpointCompression) Get() any {
   141  	return *g
   142  }
   143  
   144  // String implements flag.Value.
   145  func (g CheckpointCompression) String() string {
   146  	return string(g)
   147  }
   148  
   149  // Level returns corresponding statefile.CompressionLevel value.
   150  func (g CheckpointCompression) Level() statefile.CompressionLevel {
   151  	return statefile.CompressionLevel(g)
   152  }