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 }