Skip to content

Commit a6a4511

Browse files
committed
Update checkpoint and restore to latest docker/master.
- 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 <rboucher@gmail.com>
1 parent acf9200 commit a6a4511

29 files changed

+844
-354
lines changed

Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ RUN echo deb http://ppa.launchpad.net/zfs-native/stable/ubuntu trusty main > /et
3232
# Packaged dependencies
3333
RUN apt-get update && apt-get install -y \
3434
apparmor \
35+
asciidoc \
3536
aufs-tools \
3637
automake \
3738
bash-completion \
39+
bsdmainutils \
3840
btrfs-tools \
3941
build-essential \
4042
createrepo \
@@ -43,22 +45,30 @@ RUN apt-get update && apt-get install -y \
4345
gcc-mingw-w64 \
4446
git \
4547
iptables \
48+
libaio-dev \
4649
libapparmor-dev \
4750
libcap-dev \
51+
libprotobuf-c0-dev \
52+
libprotobuf-dev \
4853
libsqlite3-dev \
4954
libsystemd-journal-dev \
5055
mercurial \
5156
parallel \
5257
pkg-config \
58+
protobuf-compiler \
59+
protobuf-c-compiler \
60+
python-minimal \
5361
python-mock \
5462
python-pip \
63+
python-protobuf \
5564
python-websocket \
5665
reprepro \
5766
ruby1.9.1 \
5867
ruby1.9.1-dev \
5968
s3cmd=1.1.0* \
6069
ubuntu-zfs \
6170
xfsprogs \
71+
xmlto \
6272
libzfs-dev \
6373
--no-install-recommends
6474

@@ -73,6 +83,13 @@ RUN cd /usr/local/lvm2 \
7383
&& make install_device-mapper
7484
# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
7585

86+
# Install Criu
87+
RUN mkdir -p /usr/src/criu \
88+
&& curl -sSL https://github.com/xemul/criu/archive/v1.6.tar.gz | tar -v -C /usr/src/criu/ -xz --strip-components=1
89+
RUN cd /usr/src/criu \
90+
&& make \
91+
&& make install
92+
7693
# Install Go
7794
ENV GO_VERSION 1.5.1
7895
RUN curl -sSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar -v -C /usr/local -xz

api/client/checkpoint.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// +build experimental
2+
3+
package client
4+
5+
import (
6+
"fmt"
7+
8+
Cli "github.com/docker/docker/cli"
9+
flag "github.com/docker/docker/pkg/mflag"
10+
"github.com/docker/docker/runconfig"
11+
)
12+
13+
// CmdCheckpoint checkpoints the process running in a container
14+
//
15+
// Usage: docker checkpoint CONTAINER
16+
func (cli *DockerCli) CmdCheckpoint(args ...string) error {
17+
cmd := Cli.Subcmd("checkpoint", []string{"CONTAINER [CONTAINER...]"}, "Checkpoint one or more running containers", true)
18+
cmd.Require(flag.Min, 1)
19+
20+
var (
21+
flImgDir = cmd.String([]string{"-image-dir"}, "", "directory for storing checkpoint image files")
22+
flWorkDir = cmd.String([]string{"-work-dir"}, "", "directory for storing log file")
23+
flLeaveRunning = cmd.Bool([]string{"-leave-running"}, false, "leave the container running after checkpoint")
24+
)
25+
26+
if err := cmd.ParseFlags(args, true); err != nil {
27+
return err
28+
}
29+
30+
if cmd.NArg() < 1 {
31+
cmd.Usage()
32+
return nil
33+
}
34+
35+
criuOpts := &runconfig.CriuConfig{
36+
ImagesDirectory: *flImgDir,
37+
WorkDirectory: *flWorkDir,
38+
LeaveRunning: *flLeaveRunning,
39+
TCPEstablished: true,
40+
ExternalUnixConnections: true,
41+
FileLocks: true,
42+
}
43+
44+
var encounteredError error
45+
for _, name := range cmd.Args() {
46+
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/checkpoint", criuOpts, nil))
47+
if err != nil {
48+
fmt.Fprintf(cli.err, "%s\n", err)
49+
encounteredError = fmt.Errorf("Error: failed to checkpoint one or more containers")
50+
} else {
51+
fmt.Fprintf(cli.out, "%s\n", name)
52+
}
53+
}
54+
return encounteredError
55+
}

api/client/restore.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// +build experimental
2+
3+
package client
4+
5+
import (
6+
"fmt"
7+
8+
Cli "github.com/docker/docker/cli"
9+
flag "github.com/docker/docker/pkg/mflag"
10+
"github.com/docker/docker/runconfig"
11+
)
12+
13+
// CmdRestore restores the process in a checkpointed container
14+
//
15+
// Usage: docker restore CONTAINER
16+
func (cli *DockerCli) CmdRestore(args ...string) error {
17+
cmd := Cli.Subcmd("restore", []string{"CONTAINER [CONTAINER...]"}, "Restore one or more checkpointed containers", true)
18+
cmd.Require(flag.Min, 1)
19+
20+
var (
21+
flImgDir = cmd.String([]string{"-image-dir"}, "", "directory to restore image files from")
22+
flWorkDir = cmd.String([]string{"-work-dir"}, "", "directory for restore log")
23+
flForce = cmd.Bool([]string{"-force"}, false, "bypass checks for current container state")
24+
)
25+
26+
if err := cmd.ParseFlags(args, true); err != nil {
27+
return err
28+
}
29+
30+
if cmd.NArg() < 1 {
31+
cmd.Usage()
32+
return nil
33+
}
34+
35+
restoreOpts := &runconfig.RestoreConfig{
36+
CriuOpts: runconfig.CriuConfig{
37+
ImagesDirectory: *flImgDir,
38+
WorkDirectory: *flWorkDir,
39+
TCPEstablished: true,
40+
ExternalUnixConnections: true,
41+
FileLocks: true,
42+
},
43+
ForceRestore: *flForce,
44+
}
45+
46+
var encounteredError error
47+
for _, name := range cmd.Args() {
48+
_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restore", restoreOpts, nil))
49+
if err != nil {
50+
fmt.Fprintf(cli.err, "%s\n", err)
51+
encounteredError = fmt.Errorf("Error: failed to restore one or more containers")
52+
} else {
53+
fmt.Fprintf(cli.out, "%s\n", name)
54+
}
55+
}
56+
return encounteredError
57+
}

api/server/router/local/local.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ func (r *router) initRoutes() {
145145
NewDeleteRoute("/containers/{name:.*}", r.deleteContainers),
146146
NewDeleteRoute("/images/{name:.*}", r.deleteImages),
147147
}
148+
149+
addExperimentalRoutes(r)
148150
}
149151

150152
func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// +build experimental
2+
3+
package local
4+
5+
import (
6+
"encoding/json"
7+
"fmt"
8+
"net/http"
9+
10+
"github.com/docker/docker/api/server/httputils"
11+
dkrouter "github.com/docker/docker/api/server/router"
12+
"github.com/docker/docker/runconfig"
13+
"golang.org/x/net/context"
14+
)
15+
16+
func addExperimentalRoutes(r *router) {
17+
newRoutes := []dkrouter.Route{
18+
NewPostRoute("/containers/{name:.*}/checkpoint", r.postContainersCheckpoint),
19+
NewPostRoute("/containers/{name:.*}/restore", r.postContainersRestore),
20+
}
21+
22+
r.routes = append(r.routes, newRoutes...)
23+
}
24+
25+
func (s *router) postContainersCheckpoint(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
26+
if vars == nil {
27+
return fmt.Errorf("Missing parameter")
28+
}
29+
if err := httputils.CheckForJSON(r); err != nil {
30+
return err
31+
}
32+
33+
criuOpts := &runconfig.CriuConfig{}
34+
if err := json.NewDecoder(r.Body).Decode(criuOpts); err != nil {
35+
return err
36+
}
37+
38+
if err := s.daemon.ContainerCheckpoint(vars["name"], criuOpts); err != nil {
39+
return err
40+
}
41+
42+
w.WriteHeader(http.StatusNoContent)
43+
return nil
44+
}
45+
46+
func (s *router) postContainersRestore(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
47+
if vars == nil {
48+
return fmt.Errorf("Missing parameter")
49+
}
50+
if err := httputils.CheckForJSON(r); err != nil {
51+
return err
52+
}
53+
54+
restoreOpts := runconfig.RestoreConfig{}
55+
if err := json.NewDecoder(r.Body).Decode(&restoreOpts); err != nil {
56+
return err
57+
}
58+
59+
if err := s.daemon.ContainerRestore(vars["name"], &restoreOpts.CriuOpts, restoreOpts.ForceRestore); err != nil {
60+
return err
61+
}
62+
63+
w.WriteHeader(http.StatusNoContent)
64+
return nil
65+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// +build !experimental
2+
3+
package local
4+
5+
func addExperimentalRoutes(r *router) {
6+
}

api/server/server.go

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -119,36 +119,6 @@ func (s *HTTPServer) Close() error {
119119
return s.l.Close()
120120
}
121121

122-
func postContainersCheckpoint(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
123-
if vars == nil {
124-
return fmt.Errorf("Missing parameter")
125-
}
126-
if err := parseForm(r); err != nil {
127-
return err
128-
}
129-
job := eng.Job("checkpoint", vars["name"])
130-
if err := job.Run(); err != nil {
131-
return err
132-
}
133-
w.WriteHeader(http.StatusNoContent)
134-
return nil
135-
}
136-
137-
func postContainersRestore(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
138-
if vars == nil {
139-
return fmt.Errorf("Missing parameter")
140-
}
141-
if err := parseForm(r); err != nil {
142-
return err
143-
}
144-
job := eng.Job("restore", vars["name"])
145-
if err := job.Run(); err != nil {
146-
return err
147-
}
148-
w.WriteHeader(http.StatusNoContent)
149-
return nil
150-
}
151-
152122
func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
153123
logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders)
154124
w.Header().Add("Access-Control-Allow-Origin", corsHeaders)

api/types/types.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -235,17 +235,19 @@ type ExecStartCheck struct {
235235
// ContainerState stores container's running state
236236
// it's part of ContainerJSONBase and will return by "inspect" command
237237
type ContainerState struct {
238-
Status string
239-
Running bool
240-
Paused bool
241-
Restarting bool
242-
OOMKilled bool
243-
Dead bool
244-
Pid int
245-
ExitCode int
246-
Error string
247-
StartedAt string
248-
FinishedAt string
238+
Status string
239+
Running bool
240+
Paused bool
241+
Checkpointed bool
242+
Restarting bool
243+
OOMKilled bool
244+
Dead bool
245+
Pid int
246+
ExitCode int
247+
Error string
248+
StartedAt string
249+
FinishedAt string
250+
CheckpointedAt string `json:"-"`
249251
}
250252

251253
// ContainerJSONBase contains response of Remote API:

0 commit comments

Comments
 (0)