Add support for providing options to CRIU.
In order to do more complex things with checkpointing and restoring of containers it's necessary to have control over where the image files are being saved and whether or not to kill the running process. It's possible more flags will be wanted in the future. Some things probably should always be auto-configured by libcontainer though. Docker-DCO-1.1-Signed-off-by: Ross Boucher <rboucher@gmail.com> (github: boucher)
This commit is contained in:
parent
cbe747d989
commit
a8d5fdf1fd
|
@ -115,13 +115,13 @@ type Container interface {
|
||||||
//
|
//
|
||||||
// errors:
|
// errors:
|
||||||
// Systemerror - System error.
|
// Systemerror - System error.
|
||||||
Checkpoint(string) error
|
Checkpoint(criuOpts *CriuOpts) error
|
||||||
|
|
||||||
// Restore restores the checkpointed container to a running state using the criu(8) utiity.
|
// Restore restores the checkpointed container to a running state using the criu(8) utiity.
|
||||||
//
|
//
|
||||||
// errors:
|
// errors:
|
||||||
// Systemerror - System error.
|
// Systemerror - System error.
|
||||||
Restore(*Process, string) error
|
Restore(process *Process, criuOpts *CriuOpts) error
|
||||||
|
|
||||||
// Destroys the container after killing all running processes.
|
// Destroys the container after killing all running processes.
|
||||||
//
|
//
|
||||||
|
|
|
@ -287,7 +287,7 @@ func (c *linuxContainer) checkCriuVersion() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) Checkpoint(imagePath string) error {
|
func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
||||||
c.m.Lock()
|
c.m.Lock()
|
||||||
defer c.m.Unlock()
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
@ -295,26 +295,37 @@ func (c *linuxContainer) Checkpoint(imagePath string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
workPath := filepath.Join(c.root, "criu.work")
|
if criuOpts.ImagesDirectory == "" {
|
||||||
if err := os.Mkdir(workPath, 0655); err != nil && !os.IsExist(err) {
|
criuOpts.ImagesDirectory = filepath.Join(c.root, "criu.image")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since a container can be C/R'ed multiple times,
|
||||||
|
// the checkpoint directory may already exist.
|
||||||
|
if err := os.Mkdir(criuOpts.ImagesDirectory, 0755); err != nil && !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
workDir, err := os.Open(workPath)
|
if criuOpts.WorkDirectory == "" {
|
||||||
|
criuOpts.WorkDirectory = filepath.Join(c.root, "criu.work")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Mkdir(criuOpts.WorkDirectory, 0755); err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
workDir, err := os.Open(criuOpts.WorkDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer workDir.Close()
|
defer workDir.Close()
|
||||||
|
|
||||||
imageDir, err := os.Open(imagePath)
|
imageDir, err := os.Open(criuOpts.ImagesDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer imageDir.Close()
|
defer imageDir.Close()
|
||||||
t := criurpc.CriuReqType_DUMP
|
|
||||||
req := criurpc.CriuReq{
|
rpcOpts := criurpc.CriuOpts{
|
||||||
Type: &t,
|
|
||||||
Opts: &criurpc.CriuOpts{
|
|
||||||
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
|
ImagesDirFd: proto.Int32(int32(imageDir.Fd())),
|
||||||
WorkDirFd: proto.Int32(int32(workDir.Fd())),
|
WorkDirFd: proto.Int32(int32(workDir.Fd())),
|
||||||
LogLevel: proto.Int32(4),
|
LogLevel: proto.Int32(4),
|
||||||
|
@ -323,8 +334,18 @@ func (c *linuxContainer) Checkpoint(imagePath string) error {
|
||||||
ManageCgroups: proto.Bool(true),
|
ManageCgroups: proto.Bool(true),
|
||||||
NotifyScripts: proto.Bool(true),
|
NotifyScripts: proto.Bool(true),
|
||||||
Pid: proto.Int32(int32(c.initProcess.pid())),
|
Pid: proto.Int32(int32(c.initProcess.pid())),
|
||||||
},
|
ShellJob: proto.Bool(criuOpts.ShellJob),
|
||||||
|
LeaveRunning: proto.Bool(criuOpts.LeaveRunning),
|
||||||
|
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
|
||||||
|
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t := criurpc.CriuReqType_DUMP
|
||||||
|
req := criurpc.CriuReq{
|
||||||
|
Type: &t,
|
||||||
|
Opts: &rpcOpts,
|
||||||
|
}
|
||||||
|
|
||||||
for _, m := range c.config.Mounts {
|
for _, m := range c.config.Mounts {
|
||||||
if m.Device == "bind" {
|
if m.Device == "bind" {
|
||||||
mountDest := m.Destination
|
mountDest := m.Destination
|
||||||
|
@ -340,9 +361,9 @@ func (c *linuxContainer) Checkpoint(imagePath string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.criuSwrk(nil, &req, imagePath)
|
err = c.criuSwrk(nil, &req, criuOpts.ImagesDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(filepath.Join(workPath, "dump.log"))
|
log.Errorf(filepath.Join(criuOpts.WorkDirectory, "dump.log"))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +371,7 @@ func (c *linuxContainer) Checkpoint(imagePath string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) Restore(process *Process, imagePath string) error {
|
func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
||||||
c.m.Lock()
|
c.m.Lock()
|
||||||
defer c.m.Unlock()
|
defer c.m.Unlock()
|
||||||
|
|
||||||
|
@ -358,19 +379,25 @@ func (c *linuxContainer) Restore(process *Process, imagePath string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
workPath := filepath.Join(c.root, "criu.work")
|
if criuOpts.WorkDirectory == "" {
|
||||||
|
criuOpts.WorkDirectory = filepath.Join(c.root, "criu.work")
|
||||||
|
}
|
||||||
// Since a container can be C/R'ed multiple times,
|
// Since a container can be C/R'ed multiple times,
|
||||||
// the work directory may already exist.
|
// the work directory may already exist.
|
||||||
if err := os.Mkdir(workPath, 0755); err != nil && !os.IsExist(err) {
|
if err := os.Mkdir(criuOpts.WorkDirectory, 0655); err != nil && !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
workDir, err := os.Open(workPath)
|
|
||||||
|
workDir, err := os.Open(criuOpts.WorkDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer workDir.Close()
|
defer workDir.Close()
|
||||||
|
|
||||||
imageDir, err := os.Open(imagePath)
|
if criuOpts.ImagesDirectory == "" {
|
||||||
|
criuOpts.ImagesDirectory = filepath.Join(c.root, "criu.image")
|
||||||
|
}
|
||||||
|
imageDir, err := os.Open(criuOpts.ImagesDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -412,6 +439,9 @@ func (c *linuxContainer) Restore(process *Process, imagePath string) error {
|
||||||
Root: proto.String(root),
|
Root: proto.String(root),
|
||||||
ManageCgroups: proto.Bool(true),
|
ManageCgroups: proto.Bool(true),
|
||||||
NotifyScripts: proto.Bool(true),
|
NotifyScripts: proto.Bool(true),
|
||||||
|
ShellJob: proto.Bool(criuOpts.ShellJob),
|
||||||
|
ExtUnixSk: proto.Bool(criuOpts.ExternalUnixConnections),
|
||||||
|
TcpEstablished: proto.Bool(criuOpts.TcpEstablished),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, m := range c.config.Mounts {
|
for _, m := range c.config.Mounts {
|
||||||
|
@ -446,9 +476,9 @@ func (c *linuxContainer) Restore(process *Process, imagePath string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.criuSwrk(process, &req, imagePath)
|
err = c.criuSwrk(process, &req, criuOpts.ImagesDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(filepath.Join(workPath, "restore.log"))
|
log.Errorf(filepath.Join(criuOpts.WorkDirectory, "restore.log"))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,6 +492,9 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, imageP
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stringOpts, _ := json.Marshal(*req.Opts)
|
||||||
|
log.Debugf("stringOpts: %s", stringOpts)
|
||||||
|
|
||||||
criuClient := os.NewFile(uintptr(fds[0]), "criu-transport-client")
|
criuClient := os.NewFile(uintptr(fds[0]), "criu-transport-client")
|
||||||
criuServer := os.NewFile(uintptr(fds[1]), "criu-transport-server")
|
criuServer := os.NewFile(uintptr(fds[1]), "criu-transport-server")
|
||||||
defer criuClient.Close()
|
defer criuClient.Close()
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
type CriuOpts struct {
|
||||||
|
ImagesDirectory string // directory for storing image files
|
||||||
|
WorkDirectory string // directory to cd and write logs/pidfiles/stats to
|
||||||
|
LeaveRunning bool // leave container in running state after checkpoint
|
||||||
|
TcpEstablished bool // checkpoint/restore established TCP connections
|
||||||
|
ExternalUnixConnections bool // allow external unix connections
|
||||||
|
ShellJob bool // allow to dump and restore shell jobs
|
||||||
|
}
|
|
@ -2,9 +2,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/docker/libcontainer"
|
||||||
)
|
)
|
||||||
|
|
||||||
var checkpointCommand = cli.Command{
|
var checkpointCommand = cli.Command{
|
||||||
|
@ -12,23 +11,31 @@ var checkpointCommand = cli.Command{
|
||||||
Usage: "checkpoint a running container",
|
Usage: "checkpoint a running container",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||||
cli.StringFlag{Name: "image-path", Value: "", Usage: "path where to save images"},
|
cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
|
||||||
|
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||||||
|
cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
|
||||||
|
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
||||||
|
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
||||||
|
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
||||||
},
|
},
|
||||||
Action: func(context *cli.Context) {
|
Action: func(context *cli.Context) {
|
||||||
imagePath := context.String("image-path")
|
imagePath := context.String("image-path")
|
||||||
if imagePath == "" {
|
if imagePath == "" {
|
||||||
fatal(fmt.Errorf("The --image-path option isn't specified"))
|
fatal(fmt.Errorf("The --image-path option isn't specified"))
|
||||||
}
|
}
|
||||||
|
|
||||||
container, err := getContainer(context)
|
container, err := getContainer(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
// Since a container can be C/R'ed multiple times,
|
if err := container.Checkpoint(&libcontainer.CriuOpts{
|
||||||
// the checkpoint directory may already exist.
|
ImagesDirectory: imagePath,
|
||||||
if err := os.Mkdir(imagePath, 0655); err != nil && !os.IsExist(err) {
|
WorkDirectory: context.String("work-path"),
|
||||||
fatal(err)
|
LeaveRunning: context.Bool("leave-running"),
|
||||||
}
|
TcpEstablished: context.Bool("tcp-established"),
|
||||||
if err := container.Checkpoint(imagePath); err != nil {
|
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
||||||
|
ShellJob: context.Bool("shell-job"),
|
||||||
|
}); err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,11 @@ var restoreCommand = cli.Command{
|
||||||
Usage: "restore a container from a previous checkpoint",
|
Usage: "restore a container from a previous checkpoint",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||||
cli.StringFlag{Name: "image-path", Value: "", Usage: "path where to save images"},
|
cli.StringFlag{Name: "image-path", Value: "", Usage: "path to criu image files for restoring"},
|
||||||
|
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||||||
|
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
||||||
|
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
||||||
|
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
||||||
},
|
},
|
||||||
Action: func(context *cli.Context) {
|
Action: func(context *cli.Context) {
|
||||||
imagePath := context.String("image-path")
|
imagePath := context.String("image-path")
|
||||||
|
@ -44,7 +48,13 @@ var restoreCommand = cli.Command{
|
||||||
if err := tty.attach(process); err != nil {
|
if err := tty.attach(process); err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
err = container.Restore(process, imagePath)
|
err = container.Restore(process, &libcontainer.CriuOpts{
|
||||||
|
ImagesDirectory: imagePath,
|
||||||
|
WorkDirectory: context.String("work-path"),
|
||||||
|
TcpEstablished: context.Bool("tcp-established"),
|
||||||
|
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
||||||
|
ShellJob: context.Bool("shell-job"),
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue