Skip to content

Commit

Permalink
Add --ignore-rootfs option for checkpoint/restore
Browse files Browse the repository at this point in the history
The newly added functionality to include the container's root
file-system changes into the checkpoint archive can now be explicitly
disabled. Either during checkpoint or during restore.

If a container changes a lot of files during its runtime it might be
more effective to migrated the root file-system changes in some other
way and to not needlessly increase the size of the checkpoint archive.

If a checkpoint archive does not contain the root file-system changes
information it will automatically be skipped. If the root file-system
changes are part of the checkpoint archive it is also possible to tell
Podman to ignore these changes.

Signed-off-by: Adrian Reber <[email protected]>
  • Loading branch information
adrianreber committed Jul 11, 2019
1 parent 1a32074 commit 05549e8
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 35 deletions.
1 change: 1 addition & 0 deletions cmd/podman/checkpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func init() {
flags.BoolVarP(&checkpointCommand.All, "all", "a", false, "Checkpoint all running containers")
flags.BoolVarP(&checkpointCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.StringVarP(&checkpointCommand.Export, "export", "e", "", "Export the checkpoint image to a tar.gz")
flags.BoolVar(&checkpointCommand.IgnoreRootfs, "ignore-rootfs", false, "Do not include root file-system changes when exporting")
markFlagHiddenForRemoteClient("latest", flags)
}

Expand Down
2 changes: 2 additions & 0 deletions cmd/podman/cliconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ type CheckpointValues struct {
All bool
Latest bool
Export string
IgnoreRootfs bool
}

type CommitValues struct {
Expand Down Expand Up @@ -433,6 +434,7 @@ type RestoreValues struct {
TcpEstablished bool
Import string
Name string
IgnoreRootfs bool
}

type RmValues struct {
Expand Down
5 changes: 5 additions & 0 deletions cmd/podman/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func init() {
flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Restore a container with established TCP connections")
flags.StringVarP(&restoreCommand.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)")
flags.StringVarP(&restoreCommand.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
flags.BoolVar(&restoreCommand.IgnoreRootfs, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint")

markFlagHiddenForRemoteClient("latest", flags)
}
Expand All @@ -60,6 +61,10 @@ func restoreCmd(c *cliconfig.RestoreValues, cmd *cobra.Command) error {
}
defer runtime.DeferredShutdown(false)

if c.Import == "" && c.IgnoreRootfs {
return errors.Errorf("--ignore-rootfs can only be used with --import")
}

if c.Import == "" && c.Name != "" {
return errors.Errorf("--name can only be used with --import")
}
Expand Down
2 changes: 2 additions & 0 deletions completions/bash/podman
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ _podman_container_checkpoint() {
-R
--leave-running
--tcp-established
--ignore-rootfs
"
case "$prev" in
-e|--export)
Expand Down Expand Up @@ -870,6 +871,7 @@ _podman_container_restore() {
-l
--latest
--tcp-established
--ignore-rootfs
"
case "$prev" in
-i|--import)
Expand Down
10 changes: 9 additions & 1 deletion docs/podman-container-checkpoint.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,15 @@ connections.

Export the checkpoint to a tar.gz file. The exported checkpoint can be used
to import the container on another system and thus enabling container live
migration.
migration. This checkpoint archive also includes all changes to the container's
root file-system, if not explicitly disabled using **--ignore-rootfs**

**--ignore-rootfs**

This only works in combination with **--export, -e**. If a checkpoint is
exported to a tar.gz file it is possible with the help of **--ignore-rootfs**
to explicitly disable including changes to the root file-system into
the checkpoint archive file.

## EXAMPLE

Expand Down
7 changes: 7 additions & 0 deletions docs/podman-container-restore.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ address to the container it was using before checkpointing as each IP address ca
be used once and the restored container will have another IP address. This also means
that **--name, -n** cannot be used in combination with **--tcp-established**.

**--ignore-rootfs**

This is only available in combination with **--import, -i**. If a container is restored
from a checkpoint tar.gz file it is possible that it also contains all root file-system
changes. With **--ignore-rootfs** it is possible to explicitly disable applying these
root file-system changes to the restored container.

## EXAMPLE

podman container restore mywebserver
Expand Down
3 changes: 3 additions & 0 deletions libpod/container_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,9 @@ type ContainerCheckpointOptions struct {
// checkpoint archive a new name should be used for the
// restored container
Name string
// IgnoreRootfs tells the API to not export changes to
// the container's root file-system (or to not import)
IgnoreRootfs bool
}

// Checkpoint checkpoints a container
Expand Down
73 changes: 39 additions & 34 deletions libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,41 +510,44 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
return nil
}

func (c *Container) exportCheckpoint(dest string) (err error) {
func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error) {
if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) {
return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies")
}
logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), dest)

includeFiles := []string{
"checkpoint",
"artifacts",
"ctr.log",
"config.dump",
"spec.dump",
"network.status"}

// Get root file-system changes included in the checkpoint archive
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
rootfsDiffFile, err := os.Create(rootfsDiffPath)
if err != nil {
return errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath)
}
tarStream, err := c.runtime.GetDiffTarStream("", c.ID())
if err != nil {
return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
}
_, err = io.Copy(rootfsDiffFile, tarStream)
if err != nil {
return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
if !ignoreRootfs {
rootfsDiffFile, err := os.Create(rootfsDiffPath)
if err != nil {
return errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath)
}
tarStream, err := c.runtime.GetDiffTarStream("", c.ID())
if err != nil {
return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
}
_, err = io.Copy(rootfsDiffFile, tarStream)
if err != nil {
return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
}
tarStream.Close()
rootfsDiffFile.Close()
includeFiles = append(includeFiles, "rootfs-diff.tar")
}
tarStream.Close()
rootfsDiffFile.Close()


input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{
Compression: archive.Gzip,
IncludeSourceDir: true,
IncludeFiles: []string{
"checkpoint",
"artifacts",
"ctr.log",
"config.dump",
"spec.dump",
"rootfs-diff.tar",
"network.status"},
IncludeFiles: includeFiles,
})

if err != nil {
Expand Down Expand Up @@ -627,7 +630,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
}

if options.TargetFile != "" {
if err = c.exportCheckpoint(options.TargetFile); err != nil {
if err = c.exportCheckpoint(options.TargetFile, options.IgnoreRootfs); err != nil {
return err
}
}
Expand Down Expand Up @@ -816,17 +819,19 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}

// Before actually restarting the container, apply the root file-system changes
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
if _, err := os.Stat(rootfsDiffPath); err == nil {
// Only do this if a rootfs-diff.tar actually exists
rootfsDiffFile, err := os.Open(rootfsDiffPath)
if err != nil {
return errors.Wrapf(err, "Failed to open root file-system diff file %s", rootfsDiffPath)
}
if err := c.runtime.ApplyDiffTarStream(c.ID(), rootfsDiffFile); err != nil {
return errors.Wrapf(err, "Failed to apply root file-system diff file %s", rootfsDiffPath)
if !options.IgnoreRootfs {
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
if _, err := os.Stat(rootfsDiffPath); err == nil {
// Only do this if a rootfs-diff.tar actually exists
rootfsDiffFile, err := os.Open(rootfsDiffPath)
if err != nil {
return errors.Wrapf(err, "Failed to open root file-system diff file %s", rootfsDiffPath)
}
if err := c.runtime.ApplyDiffTarStream(c.ID(), rootfsDiffFile); err != nil {
return errors.Wrapf(err, "Failed to apply root file-system diff file %s", rootfsDiffPath)
}
rootfsDiffFile.Close()
}
rootfsDiffFile.Close()
}

if err := c.ociRuntime.createContainer(c, c.config.CgroupParent, &options); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/adapter/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,10 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error {
KeepRunning: c.LeaveRunning,
TCPEstablished: c.TcpEstablished,
TargetFile: c.Export,
IgnoreRootfs: c.IgnoreRootfs,
}
if c.Export == "" && c.IgnoreRootfs {
return errors.Errorf("--ignore-rootfs can only be used with --export")
}
if c.All {
containers, err = r.Runtime.GetRunningContainers()
Expand Down Expand Up @@ -560,6 +564,7 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
TCPEstablished: c.TcpEstablished,
TargetFile: c.Import,
Name: c.Name,
IgnoreRootfs: c.IgnoreRootfs,
}

filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
Expand Down
6 changes: 6 additions & 0 deletions pkg/adapter/containers_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,9 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error {
if c.Export != "" {
return errors.New("the remote client does not support exporting checkpoints")
}
if c.IgnoreRootfs {
return errors.New("the remote client does not support --ignore-rootfs")
}

var lastError error
ids, err := iopodman.GetContainersByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
Expand Down Expand Up @@ -708,6 +711,9 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues)
if c.Import != "" {
return errors.New("the remote client does not support importing checkpoints")
}
if c.IgnoreRootfs {
return errors.New("the remote client does not support --ignore-rootfs")
}

var lastError error
ids, err := iopodman.GetContainersByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
Expand Down

0 comments on commit 05549e8

Please sign in to comment.