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.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

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)")
_ = 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")
}

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
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)

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*
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)

View File

@ -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()

View File

@ -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 {

View File

@ -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)

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 := 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...)
}

View File

@ -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 {

View File

@ -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{

View File

@ -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)
})
})