add pre checkpoint

Signed-off-by: Zhuohan Chen <chen_zhuohan@163.com>
This commit is contained in:
unknown
2021-01-10 18:12:12 +08:00
parent 49db79e735
commit 2aa381f2d0
11 changed files with 192 additions and 2 deletions

View File

@ -58,6 +58,9 @@ func init() {
flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting") flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting")
flags.BoolVar(&checkpointOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container") flags.BoolVar(&checkpointOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container")
flags.BoolVarP(&checkpointOptions.PreCheckPoint, "pre-checkpoint", "P", false, "Dump container's memory information only, leave the container running")
flags.BoolVar(&checkpointOptions.WithPrevious, "with-previous", false, "Checkpoint container with pre-checkpoint images")
validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest) validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest)
} }
@ -72,6 +75,9 @@ func checkpoint(cmd *cobra.Command, args []string) error {
if checkpointOptions.Export == "" && checkpointOptions.IgnoreVolumes { if checkpointOptions.Export == "" && checkpointOptions.IgnoreVolumes {
return errors.Errorf("--ignore-volumes can only be used with --export") return errors.Errorf("--ignore-volumes can only be used with --export")
} }
if checkpointOptions.WithPrevious && checkpointOptions.PreCheckPoint {
return errors.Errorf("--with-previous can not be used with --pre-checkpoint")
}
responses, err := registry.ContainerEngine().ContainerCheckpoint(context.Background(), args, checkpointOptions) responses, err := registry.ContainerEngine().ContainerCheckpoint(context.Background(), args, checkpointOptions)
if err != nil { if err != nil {
return err return err

View File

@ -59,6 +59,10 @@ func init() {
flags.StringVarP(&restoreOptions.Name, nameFlagName, "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)") flags.StringVarP(&restoreOptions.Name, nameFlagName, "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
_ = restoreCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) _ = restoreCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone)
importPreviousFlagName := "import-previous"
flags.StringVar(&restoreOptions.ImportPrevious, importPreviousFlagName, "", "Restore from exported pre-checkpoint archive (tar.gz)")
_ = restoreCommand.RegisterFlagCompletionFunc(importPreviousFlagName, completion.AutocompleteDefault)
flags.BoolVar(&restoreOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint") flags.BoolVar(&restoreOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint")
flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip") flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip")
flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address") flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address")
@ -71,6 +75,9 @@ func restore(_ *cobra.Command, args []string) error {
if rootless.IsRootless() { if rootless.IsRootless() {
return errors.New("restoring a container requires root") return errors.New("restoring a container requires root")
} }
if restoreOptions.Import == "" && restoreOptions.ImportPrevious != "" {
return errors.Errorf("--import-previous can only be used with --import")
}
if restoreOptions.Import == "" && restoreOptions.IgnoreRootFS { if restoreOptions.Import == "" && restoreOptions.IgnoreRootFS {
return errors.Errorf("--ignore-rootfs can only be used with --import") return errors.Errorf("--ignore-rootfs can only be used with --import")
} }

View File

@ -58,12 +58,26 @@ This option must be used in combination with the **--export, -e** option.
When this option is specified, the content of volumes associated with When this option is specified, the content of volumes associated with
the container will not be included into the checkpoint tar.gz file. the container will not be included into the checkpoint tar.gz file.
#### **--pre-checkpoint**, **-P**
Dump the container's memory information only, leaving the container running. Later
operations will supersede prior dumps. It only works on runc 1.0-rc3 or higher.
#### **--with-previous**
Check out the container with previous criu image files in pre-dump. It only works
without **--pre-checkpoint** or **-P**. It only works on runc 1.0-rc3 or higher.
## EXAMPLE ## EXAMPLE
podman container checkpoint mywebserver podman container checkpoint mywebserver
podman container checkpoint 860a4b23 podman container checkpoint 860a4b23
podman container checkpoint -P -e pre-checkpoint.tar.gz -l
podman container checkpoint --with-previous -e checkpoint.tar.gz -l
## SEE ALSO ## SEE ALSO
podman(1), podman-container-restore(1) podman(1), podman-container-restore(1)

View File

@ -48,6 +48,11 @@ Import a checkpoint tar.gz file, which was exported by Podman. This can be used
to import a checkpointed container from another host. Do not specify a *container* to import a checkpointed container from another host. Do not specify a *container*
argument when using this option. argument when using this option.
#### **--import-previous**
Import a pre-checkpoint tar.gz file which was exported by Podman. This option
must be used with **-i** or **--import**. It only works on runc 1.0-rc3 or higher.
#### **--name**, **-n** #### **--name**, **-n**
This is only available in combination with **--import, -i**. If a container is restored This is only available in combination with **--import, -i**. If a container is restored
@ -98,6 +103,8 @@ podman container restore mywebserver
podman container restore 860a4b23 podman container restore 860a4b23
podman container restore --import-previous pre-checkpoint.tar.gz --import checkpoint.tar.gz
## SEE ALSO ## SEE ALSO
podman(1), podman-container-checkpoint(1) podman(1), podman-container-checkpoint(1)

View File

@ -706,6 +706,13 @@ type ContainerCheckpointOptions struct {
// IgnoreVolumes tells the API to not export or not to import // IgnoreVolumes tells the API to not export or not to import
// the content of volumes associated with the container // the content of volumes associated with the container
IgnoreVolumes bool IgnoreVolumes bool
// Pre Checkpoint container and leave container running
PreCheckPoint bool
// Dump container with Pre Checkpoint images
WithPrevious bool
// ImportPrevious tells the API to restore container with two
// images. One is TargetFile, the other is ImportPrevious.
ImportPrevious string
} }
// Checkpoint checkpoints a container // Checkpoint checkpoints a container
@ -718,6 +725,12 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
} }
} }
if options.WithPrevious {
if err := c.canWithPrevious(); err != nil {
return err
}
}
if !c.batched { if !c.batched {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()

View File

@ -134,6 +134,11 @@ func (c *Container) CheckpointPath() string {
return filepath.Join(c.bundlePath(), "checkpoint") return filepath.Join(c.bundlePath(), "checkpoint")
} }
// PreCheckpointPath returns the path to the directory containing the pre-checkpoint-images
func (c *Container) PreCheckPointPath() string {
return filepath.Join(c.bundlePath(), "pre-checkpoint")
}
// AttachSocketPath retrieves the path of the container's attach socket // AttachSocketPath retrieves the path of the container's attach socket
func (c *Container) AttachSocketPath() (string, error) { func (c *Container) AttachSocketPath() (string, error) {
return c.ociRuntime.AttachSocketPath(c) return c.ociRuntime.AttachSocketPath(c)
@ -2023,6 +2028,12 @@ func (c *Container) checkReadyForRemoval() error {
return nil return nil
} }
// canWithPrevious return the stat of the preCheckPoint dir
func (c *Container) canWithPrevious() error {
_, err := os.Stat(c.PreCheckPointPath())
return err
}
// writeJSONFile marshalls and writes the given data to a JSON file // writeJSONFile marshalls and writes the given data to a JSON file
// in the bundle path // in the bundle path
func (c *Container) writeJSONFile(v interface{}, file string) error { func (c *Container) writeJSONFile(v interface{}, file string) error {

View File

@ -812,6 +812,9 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
"spec.dump", "spec.dump",
"network.status"} "network.status"}
if options.PreCheckPoint {
includeFiles[0] = "pre-checkpoint"
}
// Get root file-system changes included in the checkpoint archive // Get root file-system changes included in the checkpoint archive
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar") rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
deleteFilesList := filepath.Join(c.bundlePath(), "deleted.files") deleteFilesList := filepath.Join(c.bundlePath(), "deleted.files")
@ -1015,6 +1018,15 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
defer c.newContainerEvent(events.Checkpoint) defer c.newContainerEvent(events.Checkpoint)
// There is a bug from criu: https://github.com/checkpoint-restore/criu/issues/116
// We have to change the symbolic link from absolute path to relative path
if options.WithPrevious {
os.Remove(path.Join(c.CheckpointPath(), "parent"))
if err := os.Symlink("../pre-checkpoint", path.Join(c.CheckpointPath(), "parent")); err != nil {
return err
}
}
if options.TargetFile != "" { if options.TargetFile != "" {
if err = c.exportCheckpoint(options); err != nil { if err = c.exportCheckpoint(options); err != nil {
return err return err
@ -1023,7 +1035,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
logrus.Debugf("Checkpointed container %s", c.ID()) logrus.Debugf("Checkpointed container %s", c.ID())
if !options.KeepRunning { if !options.KeepRunning && !options.PreCheckPoint {
c.state.State = define.ContainerStateStopped c.state.State = define.ContainerStateStopped
// Cleanup Storage and Network // Cleanup Storage and Network
@ -1032,7 +1044,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
} }
} }
if !options.Keep { if !options.Keep && !options.PreCheckPoint {
cleanup := []string{ cleanup := []string{
"dump.log", "dump.log",
"stats-dump", "stats-dump",
@ -1080,6 +1092,21 @@ func (c *Container) importCheckpoint(input string) error {
return nil return nil
} }
func (c *Container) importPreCheckpoint(input string) error {
archiveFile, err := os.Open(input)
if err != nil {
return errors.Wrap(err, "failed to open pre-checkpoint archive for import")
}
defer archiveFile.Close()
err = archive.Untar(archiveFile, c.bundlePath(), nil)
if err != nil {
return errors.Wrapf(err, "Unpacking of pre-checkpoint archive %s failed", input)
}
return nil
}
func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) { func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) {
if err := c.checkpointRestoreSupported(); err != nil { if err := c.checkpointRestoreSupported(); err != nil {
return err return err
@ -1089,6 +1116,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID()) return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID())
} }
if options.ImportPrevious != "" {
if err := c.importPreCheckpoint(options.ImportPrevious); err != nil {
return err
}
}
if options.TargetFile != "" { if options.TargetFile != "" {
if err := c.importCheckpoint(options.TargetFile); err != nil { if err := c.importCheckpoint(options.TargetFile); err != nil {
return err return err
@ -1322,6 +1355,10 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err != nil { if err != nil {
logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err) logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err)
} }
err = os.RemoveAll(c.PreCheckPointPath())
if err != nil {
logrus.Debugf("Non-fatal: removal of pre-checkpoint directory (%s) failed: %v", c.PreCheckPointPath(), err)
}
cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar", "deleted.files"} cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar", "deleted.files"}
for _, del := range cleanup { for _, del := range cleanup {
file := filepath.Join(c.bundlePath(), del) file := filepath.Join(c.bundlePath(), del)

View File

@ -769,10 +769,14 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
} }
// imagePath is used by CRIU to store the actual checkpoint files // imagePath is used by CRIU to store the actual checkpoint files
imagePath := ctr.CheckpointPath() imagePath := ctr.CheckpointPath()
if options.PreCheckPoint {
imagePath = ctr.PreCheckPointPath()
}
// workPath will be used to store dump.log and stats-dump // workPath will be used to store dump.log and stats-dump
workPath := ctr.bundlePath() workPath := ctr.bundlePath()
logrus.Debugf("Writing checkpoint to %s", imagePath) logrus.Debugf("Writing checkpoint to %s", imagePath)
logrus.Debugf("Writing checkpoint logs to %s", workPath) logrus.Debugf("Writing checkpoint logs to %s", workPath)
logrus.Debugf("Pre-dump the container %t", options.PreCheckPoint)
args := []string{} args := []string{}
args = append(args, r.runtimeFlags...) args = append(args, r.runtimeFlags...)
args = append(args, "checkpoint") args = append(args, "checkpoint")
@ -786,6 +790,15 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
if options.TCPEstablished { if options.TCPEstablished {
args = append(args, "--tcp-established") args = append(args, "--tcp-established")
} }
if !options.PreCheckPoint && options.KeepRunning {
args = append(args, "--leave-running")
}
if options.PreCheckPoint {
args = append(args, "--pre-dump")
}
if !options.PreCheckPoint && options.WithPrevious {
args = append(args, "--parent-path", ctr.PreCheckPointPath())
}
runtimeDir, err := util.GetRuntimeDir() runtimeDir, err := util.GetRuntimeDir()
if err != nil { if err != nil {
return err return err
@ -794,6 +807,7 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
} }
args = append(args, ctr.ID()) args = append(args, ctr.ID())
logrus.Debugf("the args to checkpoint: %s %s", r.path, strings.Join(args, " "))
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...) return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
} }

View File

@ -178,6 +178,8 @@ type CheckpointOptions struct {
Latest bool Latest bool
LeaveRunning bool LeaveRunning bool
TCPEstablished bool TCPEstablished bool
PreCheckPoint bool
WithPrevious bool
} }
type CheckpointReport struct { type CheckpointReport struct {
@ -196,6 +198,7 @@ type RestoreOptions struct {
Latest bool Latest bool
Name string Name string
TCPEstablished bool TCPEstablished bool
ImportPrevious string
} }
type RestoreReport struct { type RestoreReport struct {

View File

@ -489,6 +489,8 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
IgnoreRootfs: options.IgnoreRootFS, IgnoreRootfs: options.IgnoreRootFS,
IgnoreVolumes: options.IgnoreVolumes, IgnoreVolumes: options.IgnoreVolumes,
KeepRunning: options.LeaveRunning, KeepRunning: options.LeaveRunning,
PreCheckPoint: options.PreCheckPoint,
WithPrevious: options.WithPrevious,
} }
if options.All { if options.All {
@ -529,6 +531,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
IgnoreVolumes: options.IgnoreVolumes, IgnoreVolumes: options.IgnoreVolumes,
IgnoreStaticIP: options.IgnoreStaticIP, IgnoreStaticIP: options.IgnoreStaticIP,
IgnoreStaticMAC: options.IgnoreStaticMAC, IgnoreStaticMAC: options.IgnoreStaticMAC,
ImportPrevious: options.ImportPrevious,
} }
filterFuncs := []libpod.ContainerFilter{ filterFuncs := []libpod.ContainerFilter{

View File

@ -4,6 +4,7 @@ import (
"net" "net"
"os" "os"
"os/exec" "os/exec"
"strings"
"github.com/containers/podman/v2/pkg/criu" "github.com/containers/podman/v2/pkg/criu"
. "github.com/containers/podman/v2/test/utils" . "github.com/containers/podman/v2/test/utils"
@ -747,4 +748,78 @@ var _ = Describe("Podman checkpoint", func() {
// Remove exported checkpoint // Remove exported checkpoint
os.Remove(checkpointFileName) os.Remove(checkpointFileName)
}) })
It("podman checkpoint container with --pre-checkpoint", func() {
if !strings.Contains(podmanTest.OCIRuntime, "runc") {
Skip("Test only works on runc 1.0-rc3 or higher.")
}
localRunString := getRunString([]string{ALPINE, "top"})
session := podmanTest.Podman(localRunString)
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
cid := session.OutputToString()
result := podmanTest.Podman([]string{"container", "checkpoint", "-P", cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
result = podmanTest.Podman([]string{"container", "restore", cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
})
It("podman checkpoint container with --pre-checkpoint and export (migration)", func() {
if !strings.Contains(podmanTest.OCIRuntime, "runc") {
Skip("Test only works on runc 1.0-rc3 or higher.")
}
localRunString := getRunString([]string{ALPINE, "top"})
session := podmanTest.Podman(localRunString)
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
cid := session.OutputToString()
preCheckpointFileName := "/tmp/pre-checkpoint-" + cid + ".tar.gz"
checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz"
result := podmanTest.Podman([]string{"container", "checkpoint", "-P", "-e", preCheckpointFileName, cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", "-e", checkpointFileName, cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
result = podmanTest.Podman([]string{"rm", "-f", cid})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName, "--import-previous", preCheckpointFileName})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(0))
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
os.Remove(checkpointFileName)
os.Remove(preCheckpointFileName)
})
}) })