mirror of
https://github.com/containers/podman.git
synced 2025-07-01 16:17:06 +08:00
add pre checkpoint
Signed-off-by: Zhuohan Chen <chen_zhuohan@163.com>
This commit is contained in:
@ -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.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)
|
||||
}
|
||||
|
||||
@ -72,6 +75,9 @@ func checkpoint(cmd *cobra.Command, args []string) error {
|
||||
if checkpointOptions.Export == "" && checkpointOptions.IgnoreVolumes {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -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)")
|
||||
_ = 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.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")
|
||||
@ -71,6 +75,9 @@ func restore(_ *cobra.Command, args []string) error {
|
||||
if rootless.IsRootless() {
|
||||
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 {
|
||||
return errors.Errorf("--ignore-rootfs can only be used with --import")
|
||||
}
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
podman container checkpoint mywebserver
|
||||
|
||||
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
|
||||
podman(1), podman-container-restore(1)
|
||||
|
||||
|
@ -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*
|
||||
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**
|
||||
|
||||
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 --import-previous pre-checkpoint.tar.gz --import checkpoint.tar.gz
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-container-checkpoint(1)
|
||||
|
||||
|
@ -706,6 +706,13 @@ type ContainerCheckpointOptions struct {
|
||||
// IgnoreVolumes tells the API to not export or not to import
|
||||
// the content of volumes associated with the container
|
||||
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
|
||||
@ -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 {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
@ -134,6 +134,11 @@ func (c *Container) CheckpointPath() string {
|
||||
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
|
||||
func (c *Container) AttachSocketPath() (string, error) {
|
||||
return c.ociRuntime.AttachSocketPath(c)
|
||||
@ -2023,6 +2028,12 @@ func (c *Container) checkReadyForRemoval() error {
|
||||
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
|
||||
// in the bundle path
|
||||
func (c *Container) writeJSONFile(v interface{}, file string) error {
|
||||
|
@ -812,6 +812,9 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
|
||||
"spec.dump",
|
||||
"network.status"}
|
||||
|
||||
if options.PreCheckPoint {
|
||||
includeFiles[0] = "pre-checkpoint"
|
||||
}
|
||||
// Get root file-system changes included in the checkpoint archive
|
||||
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
|
||||
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)
|
||||
|
||||
// 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 err = c.exportCheckpoint(options); err != nil {
|
||||
return err
|
||||
@ -1023,7 +1035,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
|
||||
|
||||
logrus.Debugf("Checkpointed container %s", c.ID())
|
||||
|
||||
if !options.KeepRunning {
|
||||
if !options.KeepRunning && !options.PreCheckPoint {
|
||||
c.state.State = define.ContainerStateStopped
|
||||
|
||||
// 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{
|
||||
"dump.log",
|
||||
"stats-dump",
|
||||
@ -1080,6 +1092,21 @@ func (c *Container) importCheckpoint(input string) error {
|
||||
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) {
|
||||
if err := c.checkpointRestoreSupported(); err != nil {
|
||||
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())
|
||||
}
|
||||
|
||||
if options.ImportPrevious != "" {
|
||||
if err := c.importPreCheckpoint(options.ImportPrevious); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if options.TargetFile != "" {
|
||||
if err := c.importCheckpoint(options.TargetFile); err != nil {
|
||||
return err
|
||||
@ -1322,6 +1355,10 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
||||
if err != nil {
|
||||
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"}
|
||||
for _, del := range cleanup {
|
||||
file := filepath.Join(c.bundlePath(), del)
|
||||
|
@ -769,10 +769,14 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
|
||||
}
|
||||
// imagePath is used by CRIU to store the actual checkpoint files
|
||||
imagePath := ctr.CheckpointPath()
|
||||
if options.PreCheckPoint {
|
||||
imagePath = ctr.PreCheckPointPath()
|
||||
}
|
||||
// workPath will be used to store dump.log and stats-dump
|
||||
workPath := ctr.bundlePath()
|
||||
logrus.Debugf("Writing checkpoint to %s", imagePath)
|
||||
logrus.Debugf("Writing checkpoint logs to %s", workPath)
|
||||
logrus.Debugf("Pre-dump the container %t", options.PreCheckPoint)
|
||||
args := []string{}
|
||||
args = append(args, r.runtimeFlags...)
|
||||
args = append(args, "checkpoint")
|
||||
@ -786,6 +790,15 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
|
||||
if options.TCPEstablished {
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -794,6 +807,7 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
|
||||
return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
|
||||
}
|
||||
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...)
|
||||
}
|
||||
|
||||
|
@ -178,6 +178,8 @@ type CheckpointOptions struct {
|
||||
Latest bool
|
||||
LeaveRunning bool
|
||||
TCPEstablished bool
|
||||
PreCheckPoint bool
|
||||
WithPrevious bool
|
||||
}
|
||||
|
||||
type CheckpointReport struct {
|
||||
@ -196,6 +198,7 @@ type RestoreOptions struct {
|
||||
Latest bool
|
||||
Name string
|
||||
TCPEstablished bool
|
||||
ImportPrevious string
|
||||
}
|
||||
|
||||
type RestoreReport struct {
|
||||
|
@ -489,6 +489,8 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
|
||||
IgnoreRootfs: options.IgnoreRootFS,
|
||||
IgnoreVolumes: options.IgnoreVolumes,
|
||||
KeepRunning: options.LeaveRunning,
|
||||
PreCheckPoint: options.PreCheckPoint,
|
||||
WithPrevious: options.WithPrevious,
|
||||
}
|
||||
|
||||
if options.All {
|
||||
@ -529,6 +531,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
|
||||
IgnoreVolumes: options.IgnoreVolumes,
|
||||
IgnoreStaticIP: options.IgnoreStaticIP,
|
||||
IgnoreStaticMAC: options.IgnoreStaticMAC,
|
||||
ImportPrevious: options.ImportPrevious,
|
||||
}
|
||||
|
||||
filterFuncs := []libpod.ContainerFilter{
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/podman/v2/pkg/criu"
|
||||
. "github.com/containers/podman/v2/test/utils"
|
||||
@ -747,4 +748,78 @@ var _ = Describe("Podman checkpoint", func() {
|
||||
// Remove exported checkpoint
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user