Skip to content

Commit

Permalink
Update checkpoint and restore to latest docker/master.
Browse files Browse the repository at this point in the history
- C/R is now an EXPERIMENTAL level feature.
- Requires CRIU 1.6 (and builds it from source in the Dockerfile)
- Introduces checkpoint and restore as top level cli methods (will likely change)

Signed-off-by: Ross Boucher <[email protected]>
  • Loading branch information
boucher committed Oct 6, 2015
1 parent c19d362 commit 183488f
Show file tree
Hide file tree
Showing 29 changed files with 786 additions and 382 deletions.
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ RUN echo deb http://ppa.launchpad.net/zfs-native/stable/ubuntu trusty main > /et
# Packaged dependencies
RUN apt-get update && apt-get install -y \
apparmor \
asciidoc \
aufs-tools \
automake \
bash-completion \
bsdmainutils \
btrfs-tools \
build-essential \
createrepo \
Expand All @@ -43,21 +45,29 @@ RUN apt-get update && apt-get install -y \
gcc-mingw-w64 \
git \
iptables \
libaio-dev \
libapparmor-dev \
libcap-dev \
libprotobuf-c0-dev \
libprotobuf-dev \
libsqlite3-dev \
libsystemd-journal-dev \
mercurial \
parallel \
pkg-config \
protobuf-compiler \
protobuf-c-compiler \
python-minimal \
python-mock \
python-pip \
python-protobuf \
python-websocket \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.0* \
ubuntu-zfs \
xmlto \
libzfs-dev \
--no-install-recommends

Expand All @@ -82,6 +92,13 @@ RUN cd /usr/src/lxc \
&& make install \
&& ldconfig

# Install Criu
RUN mkdir -p /usr/src/criu \
&& curl -sSL https://github.com/xemul/criu/archive/v1.6.tar.gz | tar -v -C /usr/src/criu/ -xz --strip-components=1
RUN cd /usr/src/criu \
&& make \
&& make install

# Install Go
ENV GO_VERSION 1.4.3
RUN curl -sSL https://golang.org/dl/go${GO_VERSION}.src.tar.gz | tar -v -C /usr/local -xz \
Expand Down
55 changes: 55 additions & 0 deletions api/client/checkpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// +build experimental

package client

import (
"fmt"

Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/runconfig"
)

// CmdCheckpoint checkpoints the process running in a container
//
// Usage: docker checkpoint CONTAINER
func (cli *DockerCli) CmdCheckpoint(args ...string) error {
cmd := Cli.Subcmd("checkpoint", []string{"CONTAINER [CONTAINER...]"}, "Checkpoint one or more running containers", true)
cmd.Require(flag.Min, 1)

var (
flImgDir = cmd.String([]string{"-image-dir"}, "", "directory for storing checkpoint image files")
flWorkDir = cmd.String([]string{"-work-dir"}, "", "directory for storing log file")
flLeaveRunning = cmd.Bool([]string{"-leave-running"}, false, "leave the container running after checkpoint")
)

if err := cmd.ParseFlags(args, true); err != nil {
return err
}

if cmd.NArg() < 1 {
cmd.Usage()
return nil
}

criuOpts := &runconfig.CriuConfig{
ImagesDirectory: *flImgDir,
WorkDirectory: *flWorkDir,
LeaveRunning: *flLeaveRunning,
TCPEstablished: true,
ExternalUnixConnections: true,
FileLocks: true,
}

var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/checkpoint", criuOpts, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to checkpoint one or more containers")
} else {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
return encounteredError
}
57 changes: 57 additions & 0 deletions api/client/restore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// +build experimental

package client

import (
"fmt"

Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/runconfig"
)

// CmdRestore restores the process in a checkpointed container
//
// Usage: docker restore CONTAINER
func (cli *DockerCli) CmdRestore(args ...string) error {
cmd := Cli.Subcmd("restore", []string{"CONTAINER [CONTAINER...]"}, "Restore one or more checkpointed containers", true)
cmd.Require(flag.Min, 1)

var (
flImgDir = cmd.String([]string{"-image-dir"}, "", "directory to restore image files from")
flWorkDir = cmd.String([]string{"-work-dir"}, "", "directory for restore log")
flForce = cmd.Bool([]string{"-force"}, false, "bypass checks for current container state")
)

if err := cmd.ParseFlags(args, true); err != nil {
return err
}

if cmd.NArg() < 1 {
cmd.Usage()
return nil
}

restoreOpts := &runconfig.RestoreConfig{
CriuOpts: runconfig.CriuConfig{
ImagesDirectory: *flImgDir,
WorkDirectory: *flWorkDir,
TCPEstablished: true,
ExternalUnixConnections: true,
FileLocks: true,
},
ForceRestore: *flForce,
}

var encounteredError error
for _, name := range cmd.Args() {
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restore", restoreOpts, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to restore one or more containers")
} else {
fmt.Fprintf(cli.out, "%s\n", name)
}
}
return encounteredError
}
2 changes: 2 additions & 0 deletions api/server/router/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ func (r *router) initRoutes() {
NewDeleteRoute("/images/{name:.*}", r.deleteImages),
NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
}

addExperimentalRoutes(r)
}

func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
Expand Down
65 changes: 65 additions & 0 deletions api/server/router/local/local_experimental.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// +build experimental

package local

import (
"encoding/json"
"fmt"
"net/http"

"github.com/docker/docker/api/server/httputils"
dkrouter "github.com/docker/docker/api/server/router"
"github.com/docker/docker/runconfig"
"golang.org/x/net/context"
)

func addExperimentalRoutes(r *router) {
newRoutes := []dkrouter.Route{
NewPostRoute("/containers/{name:.*}/checkpoint", r.postContainersCheckpoint),
NewPostRoute("/containers/{name:.*}/restore", r.postContainersRestore),
}

r.routes = append(r.routes, newRoutes...)
}

func (s *router) postContainersCheckpoint(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := httputils.CheckForJSON(r); err != nil {
return err
}

criuOpts := &runconfig.CriuConfig{}
if err := json.NewDecoder(r.Body).Decode(criuOpts); err != nil {
return err
}

if err := s.daemon.ContainerCheckpoint(vars["name"], criuOpts); err != nil {
return err
}

w.WriteHeader(http.StatusNoContent)
return nil
}

func (s *router) postContainersRestore(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := httputils.CheckForJSON(r); err != nil {
return err
}

restoreOpts := runconfig.RestoreConfig{}
if err := json.NewDecoder(r.Body).Decode(&restoreOpts); err != nil {
return err
}

if err := s.daemon.ContainerRestore(vars["name"], &restoreOpts.CriuOpts, restoreOpts.ForceRestore); err != nil {
return err
}

w.WriteHeader(http.StatusNoContent)
return nil
}
6 changes: 6 additions & 0 deletions api/server/router/local/local_stable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// +build !experimental

package local

func addExperimentalRoutes(r *router) {
}
30 changes: 0 additions & 30 deletions api/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,36 +115,6 @@ func (s *HTTPServer) Close() error {
return s.l.Close()
}

func postContainersCheckpoint(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
return err
}
job := eng.Job("checkpoint", vars["name"])
if err := job.Run(); err != nil {
return err
}
w.WriteHeader(http.StatusNoContent)
return nil
}

func postContainersRestore(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
if err := parseForm(r); err != nil {
return err
}
job := eng.Job("restore", vars["name"])
if err := job.Run(); err != nil {
return err
}
w.WriteHeader(http.StatusNoContent)
return nil
}

func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders)
w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
Expand Down
24 changes: 13 additions & 11 deletions api/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,17 +231,19 @@ type ExecStartCheck struct {
// ContainerState stores container's running state
// it's part of ContainerJSONBase and will return by "inspect" command
type ContainerState struct {
Status string
Running bool
Paused bool
Restarting bool
OOMKilled bool
Dead bool
Pid int
ExitCode int
Error string
StartedAt string
FinishedAt string
Status string
Running bool
Paused bool
Checkpointed bool
Restarting bool
OOMKilled bool
Dead bool
Pid int
ExitCode int
Error string
StartedAt string
FinishedAt string
CheckpointedAt string `json:"-"`
}

// ContainerJSONBase contains response of Remote API:
Expand Down
Loading

0 comments on commit 183488f

Please sign in to comment.