Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for checkpoint image #13505

Merged
merged 3 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/podman/containers/checkpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ func init() {
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")

createImageFlagName := "create-image"
flags.StringVarP(&checkpointOptions.CreateImage, createImageFlagName, "", "", "Create checkpoint image with specified name")
_ = checkpointCommand.RegisterFlagCompletionFunc(createImageFlagName, completion.AutocompleteNone)

flags.StringP("compress", "c", "zstd", "Select compression algorithm (gzip, none, zstd) for checkpoint archive.")
_ = checkpointCommand.RegisterFlagCompletionFunc("compress", common.AutocompleteCheckpointCompressType)

Expand Down
72 changes: 51 additions & 21 deletions cmd/podman/containers/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/cmd/podman/utils"
"github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

Expand All @@ -23,15 +23,16 @@ var (
Restores a container from a checkpoint. The container name or ID can be used.
`
restoreCommand = &cobra.Command{
Use: "restore [options] CONTAINER [CONTAINER...]",
Use: "restore [options] CONTAINER|IMAGE [CONTAINER|IMAGE...]",
rst0git marked this conversation as resolved.
Show resolved Hide resolved
Short: "Restores one or more containers from a checkpoint",
Long: restoreDescription,
RunE: restore,
Args: func(cmd *cobra.Command, args []string) error {
return validate.CheckAllLatestAndCIDFile(cmd, args, true, false)
},
ValidArgsFunction: common.AutocompleteContainers,
ValidArgsFunction: common.AutocompleteContainersAndImages,
Example: `podman container restore ctrID
podman container restore imageID
podman container restore --latest
podman container restore --all`,
}
Expand Down Expand Up @@ -60,7 +61,7 @@ func init() {
_ = restoreCommand.RegisterFlagCompletionFunc(importFlagName, completion.AutocompleteDefault)

nameFlagName := "name"
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 image or --import)")
_ = restoreCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone)

importPreviousFlagName := "import-previous"
Expand All @@ -78,7 +79,7 @@ func init() {
)
_ = restoreCommand.RegisterFlagCompletionFunc("publish", completion.AutocompleteNone)

flags.StringVar(&restoreOptions.Pod, "pod", "", "Restore container into existing Pod (only works with --import)")
flags.StringVar(&restoreOptions.Pod, "pod", "", "Restore container into existing Pod (only works with image or --import)")
_ = restoreCommand.RegisterFlagCompletionFunc("pod", common.AutocompletePodsRunning)

flags.BoolVar(
Expand All @@ -95,25 +96,51 @@ func restore(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
podmanStart := time.Now()
if rootless.IsRootless() {
return errors.New("restoring a container requires root")
return fmt.Errorf("restoring a container requires root")
}
if restoreOptions.Import == "" && restoreOptions.ImportPrevious != "" {
return errors.Errorf("--import-previous can only be used with --import")

// Find out if this is an image
inspectOpts := entities.InspectOptions{}
imgData, _, err := registry.ImageEngine().Inspect(context.Background(), args, inspectOpts)
if err != nil {
return err
}
if restoreOptions.Import == "" && restoreOptions.IgnoreRootFS {
return errors.Errorf("--ignore-rootfs can only be used with --import")

hostInfo, err := registry.ContainerEngine().Info(context.Background())
if err != nil {
return err
}
if restoreOptions.Import == "" && restoreOptions.IgnoreVolumes {
return errors.Errorf("--ignore-volumes can only be used with --import")

for i := range imgData {
restoreOptions.CheckpointImage = true
checkpointRuntimeName, found := imgData[i].Annotations[define.CheckpointAnnotationRuntimeName]
if !found {
return fmt.Errorf("image is not a checkpoint: %s", imgData[i].ID)
}
if hostInfo.Host.OCIRuntime.Name != checkpointRuntimeName {
return fmt.Errorf("container image \"%s\" requires runtime: \"%s\"", imgData[i].ID, checkpointRuntimeName)
}
}

notImport := (!restoreOptions.CheckpointImage && restoreOptions.Import == "")

if notImport && restoreOptions.ImportPrevious != "" {
return fmt.Errorf("--import-previous can only be used with image or --import")
}
if restoreOptions.Import == "" && restoreOptions.Name != "" {
return errors.Errorf("--name can only be used with --import")
if notImport && restoreOptions.IgnoreRootFS {
return fmt.Errorf("--ignore-rootfs can only be used with image or --import")
}
if restoreOptions.Import == "" && restoreOptions.Pod != "" {
return errors.Errorf("--pod can only be used with --import")
if notImport && restoreOptions.IgnoreVolumes {
return fmt.Errorf("--ignore-volumes can only be used with image or --import")
}
if notImport && restoreOptions.Name != "" {
return fmt.Errorf("--name can only be used with image or --import")
}
if notImport && restoreOptions.Pod != "" {
return fmt.Errorf("--pod can only be used with image or --import")
}
if restoreOptions.Name != "" && restoreOptions.TCPEstablished {
return errors.Errorf("--tcp-established cannot be used with --name")
return fmt.Errorf("--tcp-established cannot be used with --name")
}

inputPorts, err := cmd.Flags().GetStringSlice("publish")
Expand All @@ -125,17 +152,20 @@ func restore(cmd *cobra.Command, args []string) error {
argLen := len(args)
if restoreOptions.Import != "" {
if restoreOptions.All || restoreOptions.Latest {
return errors.Errorf("Cannot use --import with --all or --latest")
return fmt.Errorf("cannot use --import with --all or --latest")
}
if argLen > 0 {
return errors.Errorf("Cannot use --import with positional arguments")
return fmt.Errorf("cannot use --import with positional arguments")
}
}
if (restoreOptions.All || restoreOptions.Latest) && argLen > 0 {
return errors.Errorf("--all or --latest and containers cannot be used together")
return fmt.Errorf("--all or --latest and containers cannot be used together")
}
if argLen < 1 && !restoreOptions.All && !restoreOptions.Latest && restoreOptions.Import == "" {
return errors.Errorf("you must provide at least one name or id")
return fmt.Errorf("you must provide at least one name or id")
}
if argLen > 1 && restoreOptions.Name != "" {
return fmt.Errorf("--name can only be used with one checkpoint image")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that all these checks should happen before image inspect. It works as is just fine but the code is easier to maintain when we validate input before using it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slightly complicated because before image inspect we don't know if the input is an image or a container.

  • If it is a container, --name is not supported.
  • If it is an image, it should be only one.

responses, err := registry.ContainerEngine().ContainerRestore(context.Background(), args, restoreOptions)
if err != nil {
Expand Down
59 changes: 59 additions & 0 deletions docs/source/markdown/podman-container-checkpoint.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,60 @@ archives. Not compressing the checkpoint archive can result in faster checkpoint
archive creation.\
The default is **zstd**.

#### **--create-image**=*image*

Create a checkpoint image from a running container. This is a standard OCI image
created in the local image store. It consists of a single layer that contains
all of the checkpoint files. The content of this image layer is in the same format as a
checkpoint created with **--export**. A checkpoint image can be pushed to a
standard container registry and pulled on a different system to enable container
migration. In addition, the image can be exported with **podman image save** and
inspected with **podman inspect**. Inspecting a checkpoint image would display
additional information, stored as annotations, about the host environment used
to do the checkpoint:

- **io.podman.annotations.checkpoint.name**: Human-readable name of the original
container.

- **io.podman.annotations.checkpoint.rawImageName**: Unprocessed name of the
image used to create the original container (as specified by the user).

- **io.podman.annotations.checkpoint.rootfsImageID**: ID of the image used to
create the original container.

- **io.podman.annotations.checkpoint.rootfsImageName**: Image name used to
create the original container.

- **io.podman.annotations.checkpoint.podman.version**: Version of Podman used to
create the checkpoint.

- **io.podman.annotations.checkpoint.criu.version**: Version of CRIU used to
create the checkpoint.

- **io.podman.annotations.checkpoint.runtime.name**: Container runtime (e.g.,
runc, crun) used to create the checkpoint.

- **io.podman.annotations.checkpoint.runtime.version**: Version of the container
runtime used to create the checkpoint.

- **io.podman.annotations.checkpoint.conmon.version**: Version of conmon used
with the original container.

- **io.podman.annotations.checkpoint.host.arch**: CPU architecture of the host
on which the checkpoint was created.

- **io.podman.annotations.checkpoint.host.kernel**: Version of Linux kernel
of the host where the checkpoint was created.

- **io.podman.annotations.checkpoint.cgroups.version**: cgroup version used by
the host where the checkpoint was created.

- **io.podman.annotations.checkpoint.distribution.version**: Version of host
distribution on which the checkpoint was created.

- **io.podman.annotations.checkpoint.distribution.name**: Name of host
distribution on which the checkpoint was created.

#### **--export**, **-e**=*archive*

Export the checkpoint to a tar.gz file. The exported checkpoint can be used
Expand Down Expand Up @@ -145,6 +199,11 @@ Make a checkpoint for the container "mywebserver".
# podman container checkpoint mywebserver
```

Create a checkpoint image for the container "mywebserver".
```
# podman container checkpoint --create-image mywebserver-checkpoint-1 mywebserver
```

Dumps the container's memory information of the latest container into an archive.
```
# podman container checkpoint -P -e pre-checkpoint.tar.gz -l
Expand Down
20 changes: 16 additions & 4 deletions docs/source/markdown/podman-container-restore.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
podman\-container\-restore - Restores one or more containers from a checkpoint

## SYNOPSIS
**podman container restore** [*options*] *container* [*container* ...]
**podman container restore** [*options*] *name* [...]

## DESCRIPTION
**podman container restore** restores a container from a checkpoint. The *container IDs* or *names* are used as input.
**podman container restore** restores a container from a container checkpoint or
checkpoint image. The *container IDs*, *image IDs* or *names* are used as input.

## OPTIONS
#### **--all**, **-a**
Expand Down Expand Up @@ -106,14 +107,16 @@ If the **--name, -n** option is used, Podman will not attempt to assign the same
address to the *container* it was using before checkpointing as each IP address can only
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**.\
*IMPORTANT: This OPTION is only available in combination with __--import, -i__.*
*IMPORTANT: This OPTION is only available for a checkpoint image or in combination
with __--import, -i__.*

#### **--pod**=*name*

Restore a container into the pod *name*. The destination pod for this restore
has to have the same namespaces shared as the pod this container was checkpointed
from (see **[podman pod create --share](podman-pod-create.1.md#--share)**).\
*IMPORTANT: This OPTION is only available in combination with __--import, -i__.*
*IMPORTANT: This OPTION is only available for a checkpoint image or in combination
with __--import, -i__.*

This option requires at least CRIU 3.16.

Expand Down Expand Up @@ -175,6 +178,15 @@ $ podman run --rm -p 2345:80 -d webserver
# podman container restore -p 5432:8080 --import=dump.tar
```

Start a container with the name "foobar-1". Create a checkpoint image "foobar-checkpoint". Restore the container from the checkpoint image with a different name.
```
# podman run --name foobar-1 -d webserver
# podman container checkpoint --create-image foobar-checkpoint foobar-1
rst0git marked this conversation as resolved.
Show resolved Hide resolved
# podman inspect foobar-checkpoint
# podman container restore --name foobar-2 foobar-checkpoint
# podman container restore --name foobar-3 foobar-checkpoint
```

## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-container-checkpoint(1)](podman-container-checkpoint.1.md)**, **[podman-run(1)](podman-run.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **criu(8)**

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v1.1.0
github.com/blang/semver v3.5.1+incompatible
github.com/buger/goterm v1.0.4
github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681
github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0
github.com/checkpoint-restore/go-criu/v5 v5.3.0
github.com/container-orchestrated-devices/container-device-interface v0.3.2
github.com/containernetworking/cni v1.0.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg=
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU=
github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681 h1:Jj8mYL2K6peLJdvT10oGTyYyBPqOynmly37D+iL3xNw=
github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681/go.mod h1:67kWC1PXQLR3lM/mmNnu3Kzn7K4TSWZAGUuQP1JSngk=
github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0 h1:txB5jvhzUCSiiQmqmMWpo5CEB7Gj/Hq5Xqi7eaPl8ko=
github.com/checkpoint-restore/checkpointctl v0.0.0-20220321135231-33f4a66335f0/go.mod h1:67kWC1PXQLR3lM/mmNnu3Kzn7K4TSWZAGUuQP1JSngk=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/checkpoint-restore/go-criu/v5 v5.2.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
Expand Down
6 changes: 6 additions & 0 deletions libpod/container_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,9 @@ type ContainerCheckpointOptions struct {
// TargetFile tells the API to read (or write) the checkpoint image
// from (or to) the filename set in TargetFile
TargetFile string
// CheckpointImageID tells the API to restore the container from
// checkpoint image with ID set in CheckpointImageID
CheckpointImageID string
// Name tells the API that during restore from an exported
// checkpoint archive a new name should be used for the
// restored container
Expand Down Expand Up @@ -781,6 +784,9 @@ type ContainerCheckpointOptions struct {
// ImportPrevious tells the API to restore container with two
// images. One is TargetFile, the other is ImportPrevious.
ImportPrevious string
// CreateImage tells Podman to create an OCI image from container
// checkpoint in the local image store.
CreateImage string
// Compression tells the API which compression to use for
// the exported checkpoint archive.
Compression archive.Compression
Expand Down
5 changes: 5 additions & 0 deletions libpod/container_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ func (c *Container) ControlSocketPath() string {
return filepath.Join(c.bundlePath(), "ctl")
}

// CheckpointVolumesPath returns the path to the directory containing the checkpointed volumes
func (c *Container) CheckpointVolumesPath() string {
return filepath.Join(c.bundlePath(), metadata.CheckpointVolumesDirectory)
}

// CheckpointPath returns the path to the directory containing the checkpoint
func (c *Container) CheckpointPath() string {
return filepath.Join(c.bundlePath(), metadata.CheckpointDirectory)
Expand Down
Loading