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

DRAFT: use reflink to speed up checkpoint/restore #25050

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
53 changes: 53 additions & 0 deletions cmd/podman/main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package main

import (
"bytes"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"

_ "github.com/containers/podman/v5/cmd/podman/completion"
_ "github.com/containers/podman/v5/cmd/podman/farm"
Expand Down Expand Up @@ -33,6 +36,43 @@ import (
"golang.org/x/term"
)

type LogFormat struct {
TimestampFormat string
}

func (f *LogFormat) Format(entry *logrus.Entry) ([]byte, error) {
var b *bytes.Buffer

if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}

b.WriteByte('[')
b.WriteString(strings.ToUpper(entry.Level.String()))
b.WriteString("]:")
b.WriteString(entry.Time.Format(f.TimestampFormat))

if entry.Message != "" {
b.WriteString(" - ")
b.WriteString(entry.Message)
}

if len(entry.Data) > 0 {
b.WriteString(" || ")
}
for key, value := range entry.Data {
b.WriteString(key)
b.WriteByte('=')
b.WriteByte('{')
fmt.Fprint(b, value)
b.WriteString("}, ")
}

b.WriteByte('\n')
return b.Bytes(), nil
}
func main() {
if reexec.Init() {
// We were invoked with a different argv[0] indicating that we
Expand All @@ -57,6 +97,19 @@ func main() {
}

rootCmd = parseCommands()
logrus.SetFormatter(&LogFormat{time.StampMilli})
if os.Getuid() != os.Geteuid() && os.Geteuid() == 0 {
// otherwise, we get "Failed to obtain podman
// configuration: path "$HOME/.config" exists and it
// is not owned by the current user". This comes from
// the global init, so we're too late to change the
// environment.
logrus.Info("Setting UID=0. Need startup with _CONTAINERS_ROOTLESS_UID=0 _CONTAINERS_ROOTLESS_GID=0")
logrus.Warning(". Running podman SUID root is not secure.")
if err := syscall.Setuid(0); err != nil {
logrus.Errorf("Setuid: %v", err)
}
}

Execute()
os.Exit(0)
Expand Down
12 changes: 11 additions & 1 deletion cmd/podman/system/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/url"
"os"
"path/filepath"
"strconv"
"syscall"
"time"

Expand Down Expand Up @@ -43,6 +44,7 @@ Enable a listening service for API access to Podman commands.
CorsHeaders string
PProfAddr string
Timeout uint
Umask string
}{}
)

Expand All @@ -61,6 +63,8 @@ func init() {
_ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
flags.SetNormalizeFunc(aliasTimeoutFlag)

flags.StringVarP(&srvArgs.Umask, "umask", "u", "0177", "umask in octal to apply before socket creation")

flags.StringVarP(&srvArgs.CorsHeaders, "cors", "", "", "Set CORS Headers")
_ = srvCmd.RegisterFlagCompletionFunc("cors", completion.AutocompleteNone)

Expand Down Expand Up @@ -94,7 +98,13 @@ func service(cmd *cobra.Command, args []string) error {
if err := syscall.Unlink(uri.Path); err != nil && !os.IsNotExist(err) {
return err
}
mask := syscall.Umask(0177)

sockMask, err := strconv.ParseInt(srvArgs.Umask, 8, 32)
if err != nil {
return err
}

mask := syscall.Umask(int(sockMask))
defer syscall.Umask(mask)
}
}
Expand Down
73 changes: 50 additions & 23 deletions libpod/container_internal_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"github.com/containers/podman/v5/pkg/util"
"github.com/containers/podman/v5/version"
"github.com/containers/storage/pkg/archive"
tar "github.com/containers/storage/pkg/archive/hacktar"
"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/lockfile"
Expand Down Expand Up @@ -1106,13 +1107,20 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
// Get root file-system changes included in the checkpoint archive
var addToTarFiles []string
if !options.IgnoreRootfs {
t := archive.Timer("getdiff", 0)
// To correctly track deleted files, let's go through the output of 'podman diff'
rootFsChanges, err := c.runtime.GetDiff("", c.ID(), define.DiffContainer)
if err != nil {
return fmt.Errorf("exporting root file-system diff for %q: %w", c.ID(), err)
}
t()

addToTarFiles, err := crutils.CRCreateRootFsDiffTar(&rootFsChanges, c.state.Mountpoint, c.bundlePath())
// TODO - this hack is not production quality
mp := c.state.Mountpoint
if filepath.Base(mp) == "merged" {
mp = filepath.Join(filepath.Dir(mp), "diff")
}
addToTarFiles, err := crutils.CRCreateRootFsDiffTar(&rootFsChanges, mp, c.bundlePath())
if err != nil {
return err
}
Expand Down Expand Up @@ -1151,6 +1159,7 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
return fmt.Errorf("volume %s is not mounted, cannot export: %w", volume.Name(), define.ErrInternal)
}

// TODO - should also use TarWithOptionsTo
input, err := archive.TarWithOptions(mp, &archive.TarOptions{
Compression: archive.Uncompressed,
IncludeSourceDir: true,
Expand All @@ -1169,28 +1178,35 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
}
}

input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{
Compression: options.Compression,
IncludeSourceDir: true,
IncludeFiles: includeFiles,
})

if err != nil {
return fmt.Errorf("reading checkpoint directory %q: %w", c.ID(), err)
}

outFile, err := os.Create(options.TargetFile)
if err != nil {
return fmt.Errorf("creating checkpoint export file %q: %w", options.TargetFile, err)
}
defer outFile.Close()
defer outFile.Close() // error handling?

if err := os.Chmod(options.TargetFile, 0600); err != nil {
return err
var dest io.WriteCloser
dest = outFile

opts := &archive.TarOptions{
IncludeSourceDir: true,
IncludeFiles: includeFiles,
}
if options.Compression != archive.Uncompressed {
dest, err = archive.CompressStream(dest, options.Compression)
if err != nil {
return err
}
defer dest.Close()
} else {
opts.AlignBlockFile = outFile
}
tw := tar.NewWriter(dest)
defer tw.Close()
if err := archive.TarWithOptionsTo(c.bundlePath(), tw, opts); err != nil {
return fmt.Errorf("reading checkpoint directory %q: %w", c.ID(), err)
}

_, err = io.Copy(outFile, input)
if err != nil {
if err := os.Chmod(options.TargetFile, 0600); err != nil {
return err
}

Expand Down Expand Up @@ -1253,17 +1269,19 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
if err != nil {
return nil, 0, err
}
defer shmDirTarFile.Close()

input, err := archive.TarWithOptions(c.config.ShmDir, &archive.TarOptions{
tw := tar.NewWriter(shmDirTarFile)
if err := archive.TarWithOptionsTo(c.config.ShmDir, tw, &archive.TarOptions{
Compression: archive.Uncompressed,
IncludeSourceDir: true,
})
if err != nil {
AlignBlockFile: shmDirTarFile,
}); err != nil {
return nil, 0, err
}

if _, err = io.Copy(shmDirTarFile, input); err != nil {
if err := tw.Close(); err != nil {
return nil, 0, err
}
if err := shmDirTarFile.Close(); err != nil {
return nil, 0, err
}
}
Expand Down Expand Up @@ -1464,9 +1482,13 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}

if options.TargetFile != "" {
fi, _ := os.Stat(options.TargetFile)
t := archive.Timer(options.TargetFile, int(fi.Size()))

if err := c.importCheckpointTar(options.TargetFile); err != nil {
return nil, 0, err
}
t()
} else if options.CheckpointImageID != "" {
if err := c.importCheckpointImage(ctx, options.CheckpointImageID); err != nil {
return nil, 0, err
Expand Down Expand Up @@ -1742,7 +1764,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti

// Before actually restarting the container, apply the root file-system changes
if !options.IgnoreRootfs {
if err := crutils.CRApplyRootFsDiffTar(c.bundlePath(), c.state.Mountpoint); err != nil {
mp := c.state.Mountpoint
// TODO - this is not production quality
if filepath.Base(mp) == "merged" {
mp = filepath.Join(filepath.Dir(mp), "diff")
}
if err := crutils.CRApplyRootFsDiffTar(c.bundlePath(), mp); err != nil {
return nil, 0, err
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/bindings/images/build.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package images

import (
"archive/tar"
"github.com/containers/storage/pkg/archive/hacktar"
"context"
"encoding/json"
"errors"
Expand Down
24 changes: 12 additions & 12 deletions pkg/checkpoint/crutils/checkpoint_restore_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import (
"bytes"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"

metadata "github.com/checkpoint-restore/checkpointctl/lib"
"github.com/checkpoint-restore/go-criu/v7/stats"
"github.com/containers/storage/pkg/archive"
tar "github.com/containers/storage/pkg/archive/hacktar"
"github.com/opencontainers/selinux/go-selinux/label"
)

Expand Down Expand Up @@ -108,6 +108,8 @@ func CRApplyRootFsDiffTar(baseDirectory, containerRootDirectory string) error {
return fmt.Errorf("failed to open root file-system diff file: %w", err)
}
defer rootfsDiffFile.Close()
fi, _ := rootfsDiffFile.Stat()
defer archive.Timer("rootfsdiff", int(fi.Size()))()

if err := archive.Untar(rootfsDiffFile, containerRootDirectory, nil); err != nil {
return fmt.Errorf("failed to apply root file-system diff file %s: %w", rootfsDiffPath, err)
Expand Down Expand Up @@ -152,21 +154,19 @@ func CRCreateRootFsDiffTar(changes *[]archive.Change, mountPoint, destination st
}

if len(rootfsIncludeFiles) > 0 {
rootfsTar, err := archive.TarWithOptions(mountPoint, &archive.TarOptions{
Compression: archive.Uncompressed,
IncludeSourceDir: true,
IncludeFiles: rootfsIncludeFiles,
})
if err != nil {
return includeFiles, fmt.Errorf("exporting root file-system diff to %q: %w", rootfsDiffPath, err)
}
rootfsDiffFile, err := os.Create(rootfsDiffPath)
if err != nil {
return includeFiles, fmt.Errorf("creating root file-system diff file %q: %w", rootfsDiffPath, err)
}
defer rootfsDiffFile.Close()
if _, err = io.Copy(rootfsDiffFile, rootfsTar); err != nil {
return includeFiles, err
defer rootfsDiffFile.Close() // error handling?
tw := tar.NewWriter(rootfsDiffFile)
defer tw.Close()
if err := archive.TarWithOptionsTo(mountPoint, tw, &archive.TarOptions{
IncludeSourceDir: true,
IncludeFiles: rootfsIncludeFiles,
AlignBlockFile: rootfsDiffFile,
}); err != nil {
return includeFiles, fmt.Errorf("exporting root file-system diff to %q: %w", rootfsDiffPath, err)
}

includeFiles = append(includeFiles, metadata.RootFsDiffTar)
Expand Down
2 changes: 1 addition & 1 deletion pkg/machine/ocipull/ocidir.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ocipull

import (
"archive/tar"
"github.com/containers/storage/pkg/archive/hacktar"
"context"
"errors"
"fmt"
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/github.com/containers/buildah/add.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading