Skip to content

Commit 8fd276b

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 <[email protected]>
1 parent c19d362 commit 8fd276b

29 files changed

+750
-379
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,21 +45,29 @@ 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 \
70+
xmlto \
6171
libzfs-dev \
6272
--no-install-recommends
6373

@@ -82,6 +92,13 @@ RUN cd /usr/src/lxc \
8292
&& make install \
8393
&& ldconfig
8494

95+
# Install Criu
96+
RUN mkdir -p /usr/src/criu \
97+
&& curl -sSL https://github.com/xemul/criu/archive/v1.6.tar.gz | tar -v -C /usr/src/criu/ -xz --strip-components=1
98+
RUN cd /usr/src/criu \
99+
&& make \
100+
&& make install
101+
85102
# Install Go
86103
ENV GO_VERSION 1.4.3
87104
RUN curl -sSL https://golang.org/dl/go${GO_VERSION}.src.tar.gz | tar -v -C /usr/local -xz \

api/client/checkpoint.go

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

api/client/restore.go

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

api/server/router/local/local.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ func (r *router) initRoutes() {
148148
NewDeleteRoute("/images/{name:.*}", r.deleteImages),
149149
NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes),
150150
}
151+
152+
addExperimentalRoutes(r)
151153
}
152154

153155
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
@@ -115,36 +115,6 @@ func (s *HTTPServer) Close() error {
115115
return s.l.Close()
116116
}
117117

118-
func postContainersCheckpoint(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
119-
if vars == nil {
120-
return fmt.Errorf("Missing parameter")
121-
}
122-
if err := parseForm(r); err != nil {
123-
return err
124-
}
125-
job := eng.Job("checkpoint", vars["name"])
126-
if err := job.Run(); err != nil {
127-
return err
128-
}
129-
w.WriteHeader(http.StatusNoContent)
130-
return nil
131-
}
132-
133-
func postContainersRestore(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
134-
if vars == nil {
135-
return fmt.Errorf("Missing parameter")
136-
}
137-
if err := parseForm(r); err != nil {
138-
return err
139-
}
140-
job := eng.Job("restore", vars["name"])
141-
if err := job.Run(); err != nil {
142-
return err
143-
}
144-
w.WriteHeader(http.StatusNoContent)
145-
return nil
146-
}
147-
148118
func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
149119
logrus.Debugf("CORS header is enabled and set to: %s", corsHeaders)
150120
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
@@ -231,17 +231,19 @@ type ExecStartCheck struct {
231231
// ContainerState stores container's running state
232232
// it's part of ContainerJSONBase and will return by "inspect" command
233233
type ContainerState struct {
234-
Status string
235-
Running bool
236-
Paused bool
237-
Restarting bool
238-
OOMKilled bool
239-
Dead bool
240-
Pid int
241-
ExitCode int
242-
Error string
243-
StartedAt string
244-
FinishedAt string
234+
Status string
235+
Running bool
236+
Paused bool
237+
Checkpointed bool
238+
Restarting bool
239+
OOMKilled bool
240+
Dead bool
241+
Pid int
242+
ExitCode int
243+
Error string
244+
StartedAt string
245+
FinishedAt string
246+
CheckpointedAt string `json:"-"`
245247
}
246248

247249
// ContainerJSONBase contains response of Remote API:

0 commit comments

Comments
 (0)