From df86cb9a5c949530336b43100b303876f07c69ba Mon Sep 17 00:00:00 2001 From: unclejack Date: Sat, 20 Jul 2013 13:47:13 +0300 Subject: [PATCH 01/55] make docker run handle SIGINT/SIGTERM --- commands.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/commands.go b/commands.go index f0e1695b3..db8a12669 100644 --- a/commands.go +++ b/commands.go @@ -1393,6 +1393,21 @@ func (cli *DockerCli) CmdRun(args ...string) error { v.Set("stderr", "1") } + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) + go func() { + for { + sig := <-signals + if sig == syscall.SIGINT || sig == syscall.SIGTERM { + fmt.Printf("\nReceived signal: %s; cleaning up\n", sig) + if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { + fmt.Printf("failed to stop container:", err) + } + return + } + } + }() + if err := cli.hijack("POST", "/containers/"+runResult.ID+"/attach?"+v.Encode(), config.Tty, cli.in, cli.out); err != nil { utils.Debugf("Error hijack: %s", err) return err From cd6aeaf97912a0c18994c978a4b58678e671d9ee Mon Sep 17 00:00:00 2001 From: David Calavera Date: Sat, 3 Aug 2013 15:33:51 -0700 Subject: [PATCH 02/55] Sort APIImages by most recent creation date. Fixes #985. --- server.go | 2 ++ sorter.go | 36 ++++++++++++++++++++++++++++++++++++ sorter_test.go | 30 ++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 sorter.go create mode 100644 sorter_test.go diff --git a/server.go b/server.go index cb7b2cf1b..5e30dd011 100644 --- a/server.go +++ b/server.go @@ -241,6 +241,8 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) { outs = append(outs, out) } } + + sortImagesByCreation(outs) return outs, nil } diff --git a/sorter.go b/sorter.go new file mode 100644 index 000000000..a61be0ef7 --- /dev/null +++ b/sorter.go @@ -0,0 +1,36 @@ +package docker + +import "sort" + +type imageSorter struct { + images []APIImages + by func(i1, i2 *APIImages) bool // Closure used in the Less method. +} + +// Len is part of sort.Interface. +func (s *imageSorter) Len() int { + return len(s.images) +} + +// Swap is part of sort.Interface. +func (s *imageSorter) Swap(i, j int) { + s.images[i], s.images[j] = s.images[j], s.images[i] +} + +// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. +func (s *imageSorter) Less(i, j int) bool { + return s.by(&s.images[i], &s.images[j]) +} + +// Sort []ApiImages by most recent creation date. +func sortImagesByCreation(images []APIImages) { + creation := func(i1, i2 *APIImages) bool { + return i1.Created > i2.Created + } + + sorter := &imageSorter{ + images: images, + by: creation} + + sort.Sort(sorter) +} diff --git a/sorter_test.go b/sorter_test.go new file mode 100644 index 000000000..3c4b3b487 --- /dev/null +++ b/sorter_test.go @@ -0,0 +1,30 @@ +package docker + +import ( + "testing" +) + +func TestServerListOrderedImages(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + archive, err := fakeTar() + if err != nil { + t.Fatal(err) + } + _, err = runtime.graph.Create(archive, nil, "Testing", "", nil) + if err != nil { + t.Fatal(err) + } + + srv := &Server{runtime: runtime} + + images, err := srv.Images(true, "") + if err != nil { + t.Fatal(err) + } + + if images[0].Created < images[1].Created { + t.Error("Expected []APIImges to be ordered by most recent creation date.") + } +} From 88cb9f3116e41b00b00fdccf6359a555e87061bd Mon Sep 17 00:00:00 2001 From: unclejack Date: Fri, 9 Aug 2013 20:33:17 +0300 Subject: [PATCH 03/55] keep processing signals after the first one --- commands.go | 1 - 1 file changed, 1 deletion(-) diff --git a/commands.go b/commands.go index db8a12669..d045625c7 100644 --- a/commands.go +++ b/commands.go @@ -1403,7 +1403,6 @@ func (cli *DockerCli) CmdRun(args ...string) error { if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { fmt.Printf("failed to stop container:", err) } - return } } }() From 2ba5c915473ce6fe769fb059db4120e2a21fb42e Mon Sep 17 00:00:00 2001 From: unclejack Date: Fri, 9 Aug 2013 23:23:27 +0300 Subject: [PATCH 04/55] minor cleanup for signal handling --- commands.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/commands.go b/commands.go index d045625c7..ffe4ce230 100644 --- a/commands.go +++ b/commands.go @@ -1396,13 +1396,10 @@ func (cli *DockerCli) CmdRun(args ...string) error { signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) go func() { - for { - sig := <-signals - if sig == syscall.SIGINT || sig == syscall.SIGTERM { - fmt.Printf("\nReceived signal: %s; cleaning up\n", sig) - if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { - fmt.Printf("failed to stop container:", err) - } + for sig := range signals { + fmt.Printf("\nReceived signal: %s; cleaning up\n", sig) + if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { + fmt.Printf("failed to stop container:", err) } } }() From 641ddaeb03f8b8eee5c1ca11e3024976378ceb6d Mon Sep 17 00:00:00 2001 From: unclejack Date: Fri, 9 Aug 2013 23:27:34 +0300 Subject: [PATCH 05/55] add formatting directive to failure to stop container error --- commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands.go b/commands.go index ffe4ce230..85b5c9abe 100644 --- a/commands.go +++ b/commands.go @@ -1399,7 +1399,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { for sig := range signals { fmt.Printf("\nReceived signal: %s; cleaning up\n", sig) if err := cli.CmdStop("-t", "4", runResult.ID); err != nil { - fmt.Printf("failed to stop container:", err) + fmt.Printf("failed to stop container: %v", err) } } }() From 3bd73a96333e011738136f6a9eda23642cc204ab Mon Sep 17 00:00:00 2001 From: Greg Thornton Date: Sat, 10 Aug 2013 04:55:23 +0000 Subject: [PATCH 06/55] Apply volumes-from before creating volumes Copies the volumes from the container specified in `Config.VolumesFrom` before creating volumes from `Config.Volumes`. Skips any preexisting volumes when processing `Config.Volumes`. Fixes #1351 --- container.go | 60 ++++++++++++++++++++++++----------------------- container_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 29 deletions(-) diff --git a/container.go b/container.go index 8721d45a5..44797e672 100644 --- a/container.go +++ b/container.go @@ -574,40 +574,12 @@ func (container *Container) Start(hostConfig *HostConfig) error { binds[path.Clean(dst)] = bindMap } - // FIXME: evaluate volumes-from before individual volumes, so that the latter can override the former. - // Create the requested volumes volumes if container.Volumes == nil || len(container.Volumes) == 0 { container.Volumes = make(map[string]string) container.VolumesRW = make(map[string]bool) - - for volPath := range container.Config.Volumes { - volPath = path.Clean(volPath) - // If an external bind is defined for this volume, use that as a source - if bindMap, exists := binds[volPath]; exists { - container.Volumes[volPath] = bindMap.SrcPath - if strings.ToLower(bindMap.Mode) == "rw" { - container.VolumesRW[volPath] = true - } - // Otherwise create an directory in $ROOT/volumes/ and use that - } else { - c, err := container.runtime.volumes.Create(nil, container, "", "", nil) - if err != nil { - return err - } - srcPath, err := c.layer() - if err != nil { - return err - } - container.Volumes[volPath] = srcPath - container.VolumesRW[volPath] = true // RW by default - } - // Create the mountpoint - if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { - return nil - } - } } + // Apply volumes from another container if requested if container.Config.VolumesFrom != "" { c := container.runtime.Get(container.Config.VolumesFrom) if c == nil { @@ -627,6 +599,36 @@ func (container *Container) Start(hostConfig *HostConfig) error { } } + // Create the requested volumes if they don't exist + for volPath := range container.Config.Volumes { + volPath = path.Clean(volPath) + // If an external bind is defined for this volume, use that as a source + if _, exists := container.Volumes[volPath]; exists { + // Skip existing mounts + } else if bindMap, exists := binds[volPath]; exists { + container.Volumes[volPath] = bindMap.SrcPath + if strings.ToLower(bindMap.Mode) == "rw" { + container.VolumesRW[volPath] = true + } + // Otherwise create an directory in $ROOT/volumes/ and use that + } else { + c, err := container.runtime.volumes.Create(nil, container, "", "", nil) + if err != nil { + return err + } + srcPath, err := c.layer() + if err != nil { + return err + } + container.Volumes[volPath] = srcPath + container.VolumesRW[volPath] = true // RW by default + } + // Create the mountpoint + if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { + return nil + } + } + if err := container.generateLXCConfig(); err != nil { return err } diff --git a/container_test.go b/container_test.go index aca53e5eb..c4f219373 100644 --- a/container_test.go +++ b/container_test.go @@ -1276,6 +1276,65 @@ func TestRestartWithVolumes(t *testing.T) { } } +// Test for #1351 +func TestVolumesFromWithVolumes(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + container, err := NewBuilder(runtime).Create(&Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + + for key := range container.Config.Volumes { + if key != "/test" { + t.Fail() + } + } + + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + + expected := container.Volumes["/test"] + if expected == "" { + t.Fail() + } + + container2, err := NewBuilder(runtime).Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"cat", "/test/foo"}, + VolumesFrom: container.ID, + Volumes: map[string]struct{}{"/test": {}}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container2) + + output, err := container2.Output() + if err != nil { + t.Fatal(err) + } + + if string(output) != "bar" { + t.Fail() + } + + if container.Volumes["/test"] != container2.Volumes["/test"] { + t.Fail() + } +} + func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) From 57b49efc98d2f4605c95d5579a6cd952dfd6f124 Mon Sep 17 00:00:00 2001 From: Greg Thornton Date: Sat, 10 Aug 2013 06:37:57 +0000 Subject: [PATCH 07/55] Skip existing volumes in volumes-from Removes the error when a container already has a volume that would otherwise be created by `Config.VolumesFrom`. Allows restarting containers with a `Config.VolumesFrom` set. --- container.go | 10 ++++++---- container_test.go | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/container.go b/container.go index 44797e672..326c0c55f 100644 --- a/container.go +++ b/container.go @@ -587,7 +587,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { } for volPath, id := range c.Volumes { if _, exists := container.Volumes[volPath]; exists { - return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.ID) + continue } if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { return nil @@ -602,10 +602,12 @@ func (container *Container) Start(hostConfig *HostConfig) error { // Create the requested volumes if they don't exist for volPath := range container.Config.Volumes { volPath = path.Clean(volPath) - // If an external bind is defined for this volume, use that as a source + // Skip existing volumes if _, exists := container.Volumes[volPath]; exists { - // Skip existing mounts - } else if bindMap, exists := binds[volPath]; exists { + continue + } + // If an external bind is defined for this volume, use that as a source + if bindMap, exists := binds[volPath]; exists { container.Volumes[volPath] = bindMap.SrcPath if strings.ToLower(bindMap.Mode) == "rw" { container.VolumesRW[volPath] = true diff --git a/container_test.go b/container_test.go index c4f219373..644e1c058 100644 --- a/container_test.go +++ b/container_test.go @@ -1333,6 +1333,12 @@ func TestVolumesFromWithVolumes(t *testing.T) { if container.Volumes["/test"] != container2.Volumes["/test"] { t.Fail() } + + // Ensure it restarts successfully + _, err = container2.Output() + if err != nil { + t.Fatal(err) + } } func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { From 025c759e443cc4eb43fc20b1f7da5520956b3b30 Mon Sep 17 00:00:00 2001 From: Brandon Liu Date: Sun, 11 Aug 2013 00:37:16 -0700 Subject: [PATCH 08/55] Fix Graph ByParent() to generate list of child images per parent image. --- graph.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph.go b/graph.go index 606a6833e..c54725fdb 100644 --- a/graph.go +++ b/graph.go @@ -323,9 +323,9 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) { return } if children, exists := byParent[parent.ID]; exists { - byParent[parent.ID] = []*Image{image} - } else { byParent[parent.ID] = append(children, image) + } else { + byParent[parent.ID] = []*Image{image} } }) return byParent, err From 02b8d14bdd1837aad5b8fb667d1f4e7eace59687 Mon Sep 17 00:00:00 2001 From: Brandon Liu Date: Sun, 11 Aug 2013 01:24:21 -0700 Subject: [PATCH 09/55] Add test case for Graph ByParent(). --- graph_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/graph_test.go b/graph_test.go index 2898fccf9..32fb0ef44 100644 --- a/graph_test.go +++ b/graph_test.go @@ -234,6 +234,45 @@ func TestDelete(t *testing.T) { assertNImages(graph, t, 1) } +func TestByParent(t *testing.T) { + archive1, _ := fakeTar() + archive2, _ := fakeTar() + archive3, _ := fakeTar() + + graph := tempGraph(t) + defer os.RemoveAll(graph.Root) + parentImage := &Image{ + ID: GenerateID(), + Comment: "parent", + Created: time.Now(), + Parent: "", + } + childImage1 := &Image{ + ID: GenerateID(), + Comment: "child1", + Created: time.Now(), + Parent: parentImage.ID, + } + childImage2 := &Image{ + ID: GenerateID(), + Comment: "child2", + Created: time.Now(), + Parent: parentImage.ID, + } + _ = graph.Register(nil, archive1, parentImage) + _ = graph.Register(nil, archive2, childImage1) + _ = graph.Register(nil, archive3, childImage2) + + byParent, err := graph.ByParent() + if err != nil { + t.Fatal(err) + } + numChildren := len(byParent[parentImage.ID]) + if numChildren != 2 { + t.Fatalf("Expected 2 children, found %d", numChildren) + } +} + func assertNImages(graph *Graph, t *testing.T, n int) { if images, err := graph.All(); err != nil { t.Fatal(err) From def9598ed968eac934699db1b8717f852652b1ef Mon Sep 17 00:00:00 2001 From: Kawsar Saiyeed Date: Mon, 12 Aug 2013 05:22:33 +0100 Subject: [PATCH 10/55] Install websocket library before building docker --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 46f9b585c..7430ec6c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,9 @@ run echo 'deb http://archive.ubuntu.com/ubuntu precise main universe' > /etc/apt run apt-get update run apt-get install -y lxc run apt-get install -y aufs-tools +# Docker requires code.google.com/p/go.net/websocket +run apt-get install -y -q mercurial +run PKG=code.google.com/p/go.net REV=78ad7f42aa2e; hg clone https://$PKG /go/src/$PKG && cd /go/src/$PKG && hg checkout -r $REV # Upload docker source add . /go/src/github.com/dotcloud/docker # Build the binary From 703905d7ece5b4a71ae1faf2743341ace98c4fbb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 12 Aug 2013 11:50:03 +0000 Subject: [PATCH 11/55] ensure the use oh IDs and add image's name in /events --- container.go | 2 +- server.go | 22 +++++++++++----------- utils/utils.go | 4 ++++ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/container.go b/container.go index 8721d45a5..18c56c834 100644 --- a/container.go +++ b/container.go @@ -813,7 +813,7 @@ func (container *Container) monitor() { } utils.Debugf("Process finished") if container.runtime != nil && container.runtime.srv != nil { - container.runtime.srv.LogEvent("die", container.ShortID()) + container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image)) } exitCode := -1 if container.cmd != nil { diff --git a/server.go b/server.go index f06b5ce68..663b9683b 100644 --- a/server.go +++ b/server.go @@ -76,7 +76,7 @@ func (srv *Server) ContainerKill(name string) error { if err := container.Kill(); err != nil { return fmt.Errorf("Error killing container %s: %s", name, err) } - srv.LogEvent("kill", name) + srv.LogEvent("kill", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) } else { return fmt.Errorf("No such container: %s", name) } @@ -95,7 +95,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error { if _, err := io.Copy(out, data); err != nil { return err } - srv.LogEvent("export", name) + srv.LogEvent("export", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) return nil } return fmt.Errorf("No such container: %s", name) @@ -832,7 +832,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) { } return "", err } - srv.LogEvent("create", container.ShortID()) + srv.LogEvent("create", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) return container.ShortID(), nil } @@ -841,7 +841,7 @@ func (srv *Server) ContainerRestart(name string, t int) error { if err := container.Restart(t); err != nil { return fmt.Errorf("Error restarting container %s: %s", name, err) } - srv.LogEvent("restart", name) + srv.LogEvent("restart", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) } else { return fmt.Errorf("No such container: %s", name) } @@ -861,7 +861,7 @@ func (srv *Server) ContainerDestroy(name string, removeVolume bool) error { if err := srv.runtime.Destroy(container); err != nil { return fmt.Errorf("Error destroying container %s: %s", name, err) } - srv.LogEvent("destroy", name) + srv.LogEvent("destroy", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) if removeVolume { // Retrieve all volumes from all remaining containers @@ -928,7 +928,7 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error { return err } *imgs = append(*imgs, APIRmi{Deleted: utils.TruncateID(id)}) - srv.LogEvent("delete", utils.TruncateID(id)) + srv.LogEvent("delete", utils.TruncateID(id), "") return nil } return nil @@ -975,7 +975,7 @@ func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, erro } if tagDeleted { imgs = append(imgs, APIRmi{Untagged: img.ShortID()}) - srv.LogEvent("untag", img.ShortID()) + srv.LogEvent("untag", img.ShortID(), "") } if len(srv.runtime.repositories.ByID()[img.ID]) == 0 { if err := srv.deleteImageAndChildren(img.ID, &imgs); err != nil { @@ -1042,7 +1042,7 @@ func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error { if err := container.Start(hostConfig); err != nil { return fmt.Errorf("Error starting container %s: %s", name, err) } - srv.LogEvent("start", name) + srv.LogEvent("start", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) } else { return fmt.Errorf("No such container: %s", name) } @@ -1054,7 +1054,7 @@ func (srv *Server) ContainerStop(name string, t int) error { if err := container.Stop(t); err != nil { return fmt.Errorf("Error stopping container %s: %s", name, err) } - srv.LogEvent("stop", name) + srv.LogEvent("stop", container.ShortID(), srv.runtime.repositories.ImageName(container.Image)) } else { return fmt.Errorf("No such container: %s", name) } @@ -1222,9 +1222,9 @@ func (srv *Server) HTTPRequestFactory() *utils.HTTPRequestFactory { return srv.reqFactory } -func (srv *Server) LogEvent(action, id string) { +func (srv *Server) LogEvent(action, id, from string) { now := time.Now().Unix() - jm := utils.JSONMessage{Status: action, ID: id, Time: now} + jm := utils.JSONMessage{Status: action, ID: id, From: from, Time: now} srv.events = append(srv.events, jm) for _, c := range srv.listeners { select { // non blocking channel diff --git a/utils/utils.go b/utils/utils.go index df21e75ae..497d7f4e4 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -622,6 +622,7 @@ type JSONMessage struct { Progress string `json:"progress,omitempty"` ErrorMessage string `json:"error,omitempty"` //deprecated ID string `json:"id,omitempty"` + From string `json:"from,omitempty"` Time int64 `json:"time,omitempty"` Error *JSONError `json:"errorDetail,omitempty"` } @@ -650,6 +651,9 @@ func (jm *JSONMessage) Display(out io.Writer) error { if jm.ID != "" { fmt.Fprintf(out, "%s: ", jm.ID) } + if jm.From != "" { + fmt.Fprintf(out, "(from %s) ", jm.From) + } if jm.Progress != "" { fmt.Fprintf(out, "%c[2K", 27) fmt.Fprintf(out, "%s %s\r", jm.Status, jm.Progress) From 123c80467bb6e7bef22827b55640a4789e42558d Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 12 Aug 2013 11:55:23 +0000 Subject: [PATCH 12/55] Added docs --- docs/sources/api/docker_remote_api.rst | 4 +++ docs/sources/api/docker_remote_api_v1.4.rst | 31 +++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 7e4b67434..ec19fed48 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -48,6 +48,10 @@ What's new **New!** You can now use ps args with docker top, like `docker top aux` +.. http:get:: /events: + + **New!** Image's name added in the events + :doc:`docker_remote_api_v1.3` ***************************** diff --git a/docs/sources/api/docker_remote_api_v1.4.rst b/docs/sources/api/docker_remote_api_v1.4.rst index 06e8f46f9..1073ddcd6 100644 --- a/docs/sources/api/docker_remote_api_v1.4.rst +++ b/docs/sources/api/docker_remote_api_v1.4.rst @@ -1095,6 +1095,37 @@ Create a new image from a container's changes :statuscode 404: no such container :statuscode 500: server error + +Monitor Docker's events +*********************** + +.. http:get:: /events + + Get events from docker, either in real time via streaming, or via polling (using `since`) + + **Example request**: + + .. sourcecode:: http + + POST /events?since=1374067924 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924} + {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924} + {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} + {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} + + :query since: timestamp used for polling + :statuscode 200: no error + :statuscode 500: server error + + 3. Going further ================ From ec61c46bf73b8c727fe8de1982d86a1417a8a0c4 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 12 Aug 2013 22:42:29 +0000 Subject: [PATCH 13/55] Add import for dotcloud/tar to replace std tar pkg --- Dockerfile | 1 + utils/tarsum.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 46f9b585c..2e4953fb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,7 @@ run cd /tmp && echo 'package main' > t.go && go test -a -i -v run PKG=github.com/kr/pty REV=27435c699; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV run PKG=github.com/gorilla/context/ REV=708054d61e5; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV run PKG=github.com/gorilla/mux/ REV=9b36453141c; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV +run PKG=github.com/dotcloud/tar/ REV=d06045a6d9; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV # Run dependencies run apt-get install -y iptables # lxc requires updating ubuntu sources diff --git a/utils/tarsum.go b/utils/tarsum.go index d3e1db61f..290be241a 100644 --- a/utils/tarsum.go +++ b/utils/tarsum.go @@ -1,11 +1,11 @@ package utils import ( - "archive/tar" "bytes" "compress/gzip" "crypto/sha256" "encoding/hex" + "github.com/dotcloud/tar" "hash" "io" "sort" From c015d26e96e1f6ebee2a577468c747bf3d2aeeb9 Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Mon, 12 Aug 2013 11:07:58 -0700 Subject: [PATCH 14/55] API, issue 1471: Allow users belonging to the docker group to use the docker client --- api.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/api.go b/api.go index 221cabed5..6b692ed98 100644 --- a/api.go +++ b/api.go @@ -13,6 +13,7 @@ import ( "net/http" "os" "os/exec" + "regexp" "strconv" "strings" ) @@ -974,7 +975,20 @@ func ListenAndServe(proto, addr string, srv *Server, logging bool) error { return e } if proto == "unix" { - os.Chmod(addr, 0700) + os.Chmod(addr, 0660) + groups, err := ioutil.ReadFile("/etc/group") + if err != nil { + return err + } + re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)") + if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil { + gid, err := strconv.Atoi(gidMatch[2]) + if err != nil { + return err + } + utils.Debugf("docker group found. gid: %d", gid) + os.Chown(addr, 0, gid) + } } httpSrv := http.Server{Addr: addr, Handler: r} return httpSrv.Serve(l) From ef1d1aefa73f71296911b0f5593e46a81c1f5c55 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Sat, 10 Aug 2013 03:06:08 +0000 Subject: [PATCH 15/55] Revert "docker.upstart: avoid spawning a `sh` process" This reverts commit 24dd50490a027f01ea086eb90663d53348fa770e. --- packaging/ubuntu/docker.upstart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packaging/ubuntu/docker.upstart b/packaging/ubuntu/docker.upstart index f4d2fbe92..143be0340 100644 --- a/packaging/ubuntu/docker.upstart +++ b/packaging/ubuntu/docker.upstart @@ -5,4 +5,6 @@ stop on runlevel [!2345] respawn -exec /usr/bin/docker -d +script + /usr/bin/docker -d +end script From 68934878f1e707b126ab754d48ff6c6eb858b37e Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 7 Aug 2013 17:23:49 -0700 Subject: [PATCH 16/55] Make sure ENV instruction within build perform a commit each time --- buildfile.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildfile.go b/buildfile.go index 33e68c621..b13643fd8 100644 --- a/buildfile.go +++ b/buildfile.go @@ -167,9 +167,9 @@ func (b *buildFile) CmdEnv(args string) error { if envKey >= 0 { b.config.Env[envKey] = replacedVar - return nil + } else { + b.config.Env = append(b.config.Env, replacedVar) } - b.config.Env = append(b.config.Env, replacedVar) return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s", replacedVar)) } From 0ca133dd7681bb3af1d1de18a5ea6ed42142a11e Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 1 Aug 2013 02:42:22 +0200 Subject: [PATCH 17/55] Handle ip route showing mask-less IP addresses Sometimes `ip route` will show mask-less IPs, so net.ParseCIDR will fail. If it does we check if we can net.ParseIP, and fail only if we can't. Fixes #1214 Fixes #362 --- network.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/network.go b/network.go index 4e3c7456a..02268314a 100644 --- a/network.go +++ b/network.go @@ -104,7 +104,11 @@ func checkRouteOverlaps(dockerNetwork *net.IPNet) error { continue } if _, network, err := net.ParseCIDR(strings.Split(line, " ")[0]); err != nil { - return fmt.Errorf("Unexpected ip route output: %s (%s)", err, line) + // is this a mask-less IP address? + if ip := net.ParseIP(strings.Split(line, " ")[0]); ip == nil { + // fail only if it's neither a network nor a mask-less IP address + return fmt.Errorf("Unexpected ip route output: %s (%s)", err, line) + } } else if networkOverlaps(dockerNetwork, network) { return fmt.Errorf("Network %s is already routed: '%s'", dockerNetwork.String(), line) } From c3773740d982d62c5c478d3fb27aa4494383b11b Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 12 Aug 2013 23:55:42 +0000 Subject: [PATCH 18/55] Bump to 0.5.3 --- CHANGELOG.md | 6 ++++++ commands.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab145e595..7a8122416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.5.3 (2013-08-13) +* Runtime: Use docker group for socket permissions +- Runtime: Spawn shell within upstart script +- Builder: Make sure ENV instruction within build perform a commit each time +- Runtime: Handle ip route showing mask-less IP addresses + ## 0.5.2 (2013-08-08) * Builder: Forbid certain paths within docker build ADD - Runtime: Change network range to avoid conflict with EC2 DNS diff --git a/commands.go b/commands.go index 7f70c8c09..e1246d588 100644 --- a/commands.go +++ b/commands.go @@ -27,7 +27,7 @@ import ( "unicode" ) -const VERSION = "0.5.2" +const VERSION = "0.5.3" var ( GITCOMMIT string From 6cb908bb823409661bfedab806da924d232bf200 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 13 Aug 2013 13:35:34 +0000 Subject: [PATCH 19/55] fix merge issue --- commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands.go b/commands.go index 9b9dd51e7..d9d4f1b62 100644 --- a/commands.go +++ b/commands.go @@ -857,7 +857,7 @@ func (cli *DockerCli) CmdPush(args ...string) error { } if err := push(); err != nil { - if err == fmt.Errorf("Authentication is required.") { + if err.Error() == "Authentication is required." { if err = cli.checkIfLogged("push"); err == nil { return push() } From 2ba1300773857273585288c79aa65f011b045b4c Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 13 Aug 2013 13:51:49 +0000 Subject: [PATCH 20/55] remove checkIfLogged --- commands.go | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/commands.go b/commands.go index d9d4f1b62..8cefe3408 100644 --- a/commands.go +++ b/commands.go @@ -858,9 +858,11 @@ func (cli *DockerCli) CmdPush(args ...string) error { if err := push(); err != nil { if err.Error() == "Authentication is required." { - if err = cli.checkIfLogged("push"); err == nil { - return push() + fmt.Fprintln(cli.out, "\nPlease login prior to push:") + if err := cli.CmdLogin(""); err != nil { + return err } + return push() } return err } @@ -1512,19 +1514,6 @@ func (cli *DockerCli) CmdCp(args ...string) error { return nil } -func (cli *DockerCli) checkIfLogged(action string) error { - // If condition AND the login failed - if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" { - if err := cli.CmdLogin(""); err != nil { - return err - } - if cli.configFile.Configs[auth.IndexServerAddress()].Username == "" { - return fmt.Errorf("Please login prior to %s. ('docker login')", action) - } - } - return nil -} - func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) { var params io.Reader if data != nil { From e09863fedb1b2fec4672d2d1ebad29ecdb8eed1a Mon Sep 17 00:00:00 2001 From: unclejack Date: Tue, 13 Aug 2013 19:48:30 +0300 Subject: [PATCH 21/55] use Go 1.1.2 for dockerbuilder --- hack/dockerbuilder/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/dockerbuilder/Dockerfile b/hack/dockerbuilder/Dockerfile index 60cd93b17..496ee45e7 100644 --- a/hack/dockerbuilder/Dockerfile +++ b/hack/dockerbuilder/Dockerfile @@ -23,7 +23,7 @@ run add-apt-repository -y ppa:dotcloud/docker-golang/ubuntu run apt-get update # Packages required to checkout, build and upload docker run DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd curl -run curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.1.linux-amd64.tar.gz +run curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz run tar -C /usr/local -xzf /go.tar.gz run echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc run echo "export PATH=/usr/local/go/bin:$PATH" > /.bash_profile From 05219d6b52d8448fdad72f89b192d61480483aff Mon Sep 17 00:00:00 2001 From: Nolan Date: Tue, 30 Jul 2013 13:23:34 -0500 Subject: [PATCH 22/55] Add hostname to the container environment. --- container.go | 1 + 1 file changed, 1 insertion(+) diff --git a/container.go b/container.go index d610c3c7d..ccc7ab3e9 100644 --- a/container.go +++ b/container.go @@ -652,6 +652,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { "-e", "HOME=/", "-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "-e", "container=lxc", + "-e", "HOSTNAME="+container.Config.Hostname, ) for _, elem := range container.Config.Env { From 1a1c89556f3869baded68eb56ae20f8a7e90a708 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 2 Aug 2013 15:58:10 -0700 Subject: [PATCH 23/55] Fix TestEnv --- container_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/container_test.go b/container_test.go index a1ac0bd33..f29ae9e4e 100644 --- a/container_test.go +++ b/container_test.go @@ -960,6 +960,7 @@ func TestEnv(t *testing.T) { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOME=/", "container=lxc", + "HOSTNAME=" + container.ShortID(), } sort.Strings(goodEnv) if len(goodEnv) != len(actualEnv) { From 5d25f3232c38d6a7ed31860948058b8ec1d95656 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 13 Aug 2013 17:36:24 +0000 Subject: [PATCH 24/55] Update changelog to include hostname commit --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8122416..cfbd86cd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Runtime: Spawn shell within upstart script - Builder: Make sure ENV instruction within build perform a commit each time - Runtime: Handle ip route showing mask-less IP addresses +- Runtime: Add hostname to environment ## 0.5.2 (2013-08-08) * Builder: Forbid certain paths within docker build ADD From fb7c4214ced3b0533316e3eebd90ac07fe7b2933 Mon Sep 17 00:00:00 2001 From: shin- Date: Mon, 12 Aug 2013 19:45:12 +0200 Subject: [PATCH 25/55] brew: Reuse repositories when possible --- contrib/brew/brew/brew.py | 12 ++++++++++-- contrib/brew/brew/git.py | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/contrib/brew/brew/brew.py b/contrib/brew/brew/brew.py index 8cbbaca06..352d20c77 100644 --- a/contrib/brew/brew/brew.py +++ b/contrib/brew/brew/brew.py @@ -14,6 +14,7 @@ logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level='INFO') client = docker.Client() processed = {} +processed_folders = [] def build_library(repository=None, branch=None, namespace=None, push=False, @@ -92,20 +93,27 @@ def build_library(repository=None, branch=None, namespace=None, push=False, f.close() if dst_folder != repository: rmtree(dst_folder, True) + for d in processed_folders: + rmtree(d, True) summary.print_summary(logger) def build_repo(repository, ref, docker_repo, docker_tag, namespace, push, registry): docker_repo = '{0}/{1}'.format(namespace or 'library', docker_repo) img_id = None + dst_folder = None if '{0}@{1}'.format(repository, ref) not in processed.keys(): logger.info('Cloning {0} (ref: {1})'.format(repository, ref)) - dst_folder = git.clone(repository, ref) + if repository not in processed: + rep, dst_folder = git.clone(repository, ref) + processed[repository] = rep + processed_folders.append(dst_folder) + else: + dst_folder = git.checkout(processed[repository], ref) if not 'Dockerfile' in os.listdir(dst_folder): raise RuntimeError('Dockerfile not found in cloned repository') logger.info('Building using dockerfile...') img_id, logs = client.build(path=dst_folder, quiet=True) - rmtree(dst_folder, True) else: img_id = processed['{0}@{1}'.format(repository, ref)] logger.info('Committing to {0}:{1}'.format(docker_repo, diff --git a/contrib/brew/brew/git.py b/contrib/brew/brew/git.py index 40cae8753..e45e99545 100644 --- a/contrib/brew/brew/git.py +++ b/contrib/brew/brew/git.py @@ -16,6 +16,21 @@ def clone_tag(repo_url, tag, folder=None): return clone(repo_url, 'refs/tags/' + tag, folder) +def checkout(rep, ref=None): + is_commit = False + if ref is None: + ref = 'refs/heads/master' + elif not ref.startswith('refs/'): + is_commit = True + if is_commit: + rep['HEAD'] = rep.commit(ref) + else: + rep['HEAD'] = rep.refs[ref] + indexfile = rep.index_path() + tree = rep["HEAD"].tree + index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree) + return rep.path + def clone(repo_url, ref=None, folder=None): is_commit = False if ref is None: @@ -45,4 +60,4 @@ def clone(repo_url, ref=None, folder=None): tree = rep["HEAD"].tree index.build_index_from_tree(rep.path, indexfile, rep.object_store, tree) logger.debug("done") - return folder + return rep, folder From 79fc90b6463d9b20391b4edd1540bc0a8e84da6f Mon Sep 17 00:00:00 2001 From: shin- Date: Mon, 12 Aug 2013 19:52:09 +0200 Subject: [PATCH 26/55] brew: Don't build if docker daemon can't be reached --- contrib/brew/brew/brew.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/brew/brew/brew.py b/contrib/brew/brew/brew.py index 352d20c77..e07fdfdbb 100644 --- a/contrib/brew/brew/brew.py +++ b/contrib/brew/brew/brew.py @@ -32,6 +32,15 @@ def build_library(repository=None, branch=None, namespace=None, push=False, logger.info('Repository provided assumed to be a local path') dst_folder = repository + try: + client.version() + except Exception as e: + logger.error('Could not reach the docker daemon. Please make sure it ' + 'is running.') + logger.warning('Also make sure you have access to the docker UNIX ' + 'socket (use sudo)') + return + #FIXME: set destination folder and only pull latest changes instead of # cloning the whole repo everytime if not dst_folder: From e5f1b6b9a4b934eab9c42d6534fe52672c018405 Mon Sep 17 00:00:00 2001 From: shin- Date: Tue, 13 Aug 2013 20:12:44 +0200 Subject: [PATCH 27/55] brew: Updated requirements --- contrib/brew/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/brew/requirements.txt b/contrib/brew/requirements.txt index 78a574953..6100b01d0 100644 --- a/contrib/brew/requirements.txt +++ b/contrib/brew/requirements.txt @@ -1,2 +1,2 @@ dulwich==0.9.0 -docker-py==0.1.3 \ No newline at end of file +docker-py==0.1.4 \ No newline at end of file From 2cebe09924c9afea47bb1f2444ba1cd8fc423669 Mon Sep 17 00:00:00 2001 From: shin- Date: Tue, 13 Aug 2013 20:28:06 +0200 Subject: [PATCH 28/55] brew: Display a clear error message when the path is invalid --- contrib/brew/brew/brew.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contrib/brew/brew/brew.py b/contrib/brew/brew/brew.py index e07fdfdbb..22fe5b7b4 100644 --- a/contrib/brew/brew/brew.py +++ b/contrib/brew/brew/brew.py @@ -53,7 +53,13 @@ def build_library(repository=None, branch=None, namespace=None, push=False, logger.error('Source repository could not be fetched. Check ' 'that the address is correct and the branch exists.') return - for buildfile in os.listdir(os.path.join(dst_folder, 'library')): + try: + dirlist = os.listdir(os.path.join(dst_folder, 'library')) + except OSError as e: + logger.error('The path provided ({0}) could not be found or didn\'t' + 'contain a library/ folder.'.format(dst_folder)) + return + for buildfile in dirlist: if buildfile == 'MAINTAINERS': continue f = open(os.path.join(dst_folder, 'library', buildfile)) From e4f35dd4cf81a7f2d19a61cc8b1084c3adcc5253 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 13 Aug 2013 12:02:20 -0700 Subject: [PATCH 29/55] Update docs for docker group --- docs/sources/api/docker_remote_api.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 9113d8e15..aa11ba50a 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -16,6 +16,7 @@ Docker Remote API - The Remote API is replacing rcli - By default the Docker daemon listens on unix:///var/run/docker.sock and the client must have root access to interact with the daemon +- If a group named *docker* exists on your system, docker will apply ownership of the socket to the group - The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr From e2409ad3376baeb36d1011732f7d7b1a239320ae Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Tue, 13 Aug 2013 13:45:07 -0700 Subject: [PATCH 30/55] Added information about Docker's high level tools over LXC. Formatting cleanup. Mailing list cleanup. --- docs/sources/api/registry_index_spec.rst | 3 +- docs/sources/faq.rst | 124 ++++++++++++++++++++--- docs/sources/use/builder.rst | 2 + 3 files changed, 116 insertions(+), 13 deletions(-) diff --git a/docs/sources/api/registry_index_spec.rst b/docs/sources/api/registry_index_spec.rst index a41523e81..4ea0c687d 100644 --- a/docs/sources/api/registry_index_spec.rst +++ b/docs/sources/api/registry_index_spec.rst @@ -2,9 +2,10 @@ :description: Documentation for docker Registry and Registry API :keywords: docker, registry, api, index +.. _registryindexspec: ===================== -Registry & index Spec +Registry & Index Spec ===================== .. contents:: Table of Contents diff --git a/docs/sources/faq.rst b/docs/sources/faq.rst index 3cc0086c5..dd5fd11fd 100644 --- a/docs/sources/faq.rst +++ b/docs/sources/faq.rst @@ -9,40 +9,140 @@ FAQ Most frequently asked questions. -------------------------------- -1. **How much does Docker cost?** +How much does Docker cost? +.......................... Docker is 100% free, it is open source, so you can use it without paying. -2. **What open source license are you using?** +What open source license are you using? +....................................... - We are using the Apache License Version 2.0, see it here: https://github.com/dotcloud/docker/blob/master/LICENSE + We are using the Apache License Version 2.0, see it here: + https://github.com/dotcloud/docker/blob/master/LICENSE -3. **Does Docker run on Mac OS X or Windows?** +Does Docker run on Mac OS X or Windows? +....................................... - Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a - virtual machine on your box, and get the best of both worlds. Check out the :ref:`install_using_vagrant` and :ref:`windows` installation guides. + Not at this time, Docker currently only runs on Linux, but you can + use VirtualBox to run Docker in a virtual machine on your box, and + get the best of both worlds. Check out the + :ref:`install_using_vagrant` and :ref:`windows` installation + guides. -4. **How do containers compare to virtual machines?** +How do containers compare to virtual machines? +.............................................. - They are complementary. VMs are best used to allocate chunks of hardware resources. Containers operate at the process level, which makes them very lightweight and perfect as a unit of software delivery. + They are complementary. VMs are best used to allocate chunks of + hardware resources. Containers operate at the process level, which + makes them very lightweight and perfect as a unit of software + delivery. -5. **Can I help by adding some questions and answers?** +What does Docker add to just plain LXC? +....................................... + + Docker is not a replacement for LXC. "LXC" refers to capabilities + of the Linux kernel (specifically namespaces and control groups) + which allow sandboxing processes from one another, and controlling + their resource allocations. On top of this low-level foundation of + kernel features, Docker offers a high-level tool with several + powerful functionalities: + + * *Portable deployment across machines.* + Docker defines a format for bundling an application and all its + dependencies into a single object which can be transferred to + any Docker-enabled machine, and executed there with the + guarantee that the execution environment exposed to the + application will be the same. LXC implements process sandboxing, + which is an important pre-requisite for portable deployment, but + that alone is not enough for portable deployment. If you sent me + a copy of your application installed in a custom LXC + configuration, it would almost certainly not run on my machine + the way it does on yours, because it is tied to your machine's + specific configuration: networking, storage, logging, distro, + etc. Docker defines an abstraction for these machine-specific + settings, so that the exact same Docker container can run - + unchanged - on many different machines, with many different + configurations. + + * *Application-centric.* + Docker is optimized for the deployment of applications, as + opposed to machines. This is reflected in its API, user + interface, design philosophy and documentation. By contrast, the + ``lxc`` helper scripts focus on containers as lightweight + machines - basically servers that boot faster and need less + RAM. We think there's more to containers than just that. + + * *Automatic build.* + Docker includes :ref:`a tool for developers to automatically + assemble a container from their source code `, + with full control over application dependencies, build tools, + packaging etc. They are free to use ``make, maven, chef, puppet, + salt,`` Debian packages, RPMs, source tarballs, or any + combination of the above, regardless of the configuration of the + machines. + + * *Versioning.* + Docker includes git-like capabilities for tracking successive + versions of a container, inspecting the diff between versions, + committing new versions, rolling back etc. The history also + includes how a container was assembled and by whom, so you get + full traceability from the production server all the way back to + the upstream developer. Docker also implements incremental + uploads and downloads, similar to ``git pull``, so new versions + of a container can be transferred by only sending diffs. + + * *Component re-use.* + Any container can be used as a :ref:`"base image" + ` to create more specialized components. This + can be done manually or as part of an automated build. For + example you can prepare the ideal Python environment, and use it + as a base for 10 different applications. Your ideal Postgresql + setup can be re-used for all your future projects. And so on. + + * *Sharing.* + Docker has access to a `public registry + `_ where thousands of people have + uploaded useful containers: anything from Redis, CouchDB, + Postgres to IRC bouncers to Rails app servers to Hadoop to base + images for various Linux distros. The :ref:`registry + ` also includes an official "standard + library" of useful containers maintained by the Docker team. The + registry itself is open-source, so anyone can deploy their own + registry to store and transfer private containers, for internal + server deployments for example. + + * *Tool ecosystem.* + Docker defines an API for automating and customizing the + creation and deployment of containers. There are a huge number + of tools integrating with Docker to extend its + capabilities. PaaS-like deployment (Dokku, Deis, Flynn), + multi-node orchestration (Maestro, Salt, Mesos, Openstack Nova), + management dashboards (docker-ui, Openstack Horizon, Shipyard), + configuration management (Chef, Puppet), continuous integration + (Jenkins, Strider, Travis), etc. Docker is rapidly establishing + itself as the standard for container-based tooling. + +Can I help by adding some questions and answers? +................................................ Definitely! You can fork `the repo`_ and edit the documentation sources. -42. **Where can I find more answers?** +Where can I find more answers? +.............................. You can find more answers on: - * `Docker club mailinglist`_ + * `Docker user mailinglist`_ + * `Docker developer mailinglist`_ * `IRC, docker on freenode`_ * `Github`_ * `Ask questions on Stackoverflow`_ * `Join the conversation on Twitter`_ - .. _Docker club mailinglist: https://groups.google.com/d/forum/docker-club + .. _Docker user mailinglist: https://groups.google.com/d/forum/docker-user + .. _Docker developer mailinglist: https://groups.google.com/d/forum/docker-dev .. _the repo: http://www.github.com/dotcloud/docker .. _IRC, docker on freenode: irc://chat.freenode.net#docker .. _Github: http://www.github.com/dotcloud/docker diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index d111e335a..293ad3206 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -2,6 +2,8 @@ :description: Dockerfiles use a simple DSL which allows you to automate the steps you would normally manually take to create an image. :keywords: builder, docker, Dockerfile, automation, image creation +.. _dockerbuilder: + ================== Dockerfile Builder ================== From f14db4934605235bd77e9d1dc22377ca710e4c7b Mon Sep 17 00:00:00 2001 From: Thatcher Peskens Date: Tue, 13 Aug 2013 16:18:32 -0700 Subject: [PATCH 31/55] [docs] Some user-friendly changes to the documentation. - Added parmalinks (closes #1527) - Changed the 'fork us on github' button to 'Edit this page on github', so people can edit quickly (closes #1532) - Changed the favicon --- docs/sources/conf.py | 5 ++--- docs/theme/docker/layout.html | 4 ++-- docs/theme/docker/static/css/main.css | 16 ++++++++++++++++ docs/theme/docker/static/css/main.less | 18 ++++++++++++++++++ docs/theme/docker/static/favicon.png | Bin 404 -> 1475 bytes 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/docs/sources/conf.py b/docs/sources/conf.py index b4c23f0c5..9342ab503 100644 --- a/docs/sources/conf.py +++ b/docs/sources/conf.py @@ -18,7 +18,7 @@ import sys, os # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) -# -- General configuration ----------------------------------------------------- +# -- General configuratiofn ----------------------------------------------------- @@ -52,8 +52,7 @@ source_suffix = '.rst' #source_encoding = 'utf-8-sig' #disable the parmalinks on headers, I find them really annoying -html_add_permalinks = None - +html_add_permalinks = u'¶' # The master toctree document. master_doc = 'toctree' diff --git a/docs/theme/docker/layout.html b/docs/theme/docker/layout.html index d6bfff79b..2b7796628 100755 --- a/docs/theme/docker/layout.html +++ b/docs/theme/docker/layout.html @@ -70,8 +70,8 @@