Merge pull request #14476 from crosbymichael/execid-growth-fix

Prevent uncontrolled exec config growth
This commit is contained in:
Jessie Frazelle 2015-07-09 15:36:11 -07:00
Родитель a29af0cc42 34ab8c4326
Коммит 382799a642
4 изменённых файлов: 43 добавлений и 8 удалений

Просмотреть файл

@ -835,13 +835,11 @@ func (container *Container) monitorExec(execConfig *execConfig, callback execdri
err error err error
exitCode int exitCode int
) )
pipes := execdriver.NewPipes(execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdout, execConfig.StreamConfig.stderr, execConfig.OpenStdin) pipes := execdriver.NewPipes(execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdout, execConfig.StreamConfig.stderr, execConfig.OpenStdin)
exitCode, err = container.daemon.Exec(container, execConfig, pipes, callback) exitCode, err = container.daemon.Exec(container, execConfig, pipes, callback)
if err != nil { if err != nil {
logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) logrus.Errorf("Error running command in existing container %s: %s", container.ID, err)
} }
logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode)
if execConfig.OpenStdin { if execConfig.OpenStdin {
if err := execConfig.StreamConfig.stdin.Close(); err != nil { if err := execConfig.StreamConfig.stdin.Close(); err != nil {
@ -859,7 +857,9 @@ func (container *Container) monitorExec(execConfig *execConfig, callback execdri
logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err)
} }
} }
// remove the exec command from the container's store only and not the
// daemon's store so that the exec command can be inspected.
container.execCommands.Delete(execConfig.ID)
return err return err
} }

Просмотреть файл

@ -735,6 +735,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
d.RegistryService = registryService d.RegistryService = registryService
d.EventsService = eventsService d.EventsService = eventsService
d.root = config.Root d.root = config.Root
go d.execCommandGC()
if err := d.restore(); err != nil { if err := d.restore(); err != nil {
return nil, err return nil, err

Просмотреть файл

@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"strings" "strings"
"sync" "sync"
"time"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/execdriver"
@ -21,12 +22,13 @@ type execConfig struct {
ID string ID string
Running bool Running bool
ExitCode int ExitCode int
ProcessConfig execdriver.ProcessConfig ProcessConfig *execdriver.ProcessConfig
StreamConfig StreamConfig
OpenStdin bool OpenStdin bool
OpenStderr bool OpenStderr bool
OpenStdout bool OpenStdout bool
Container *Container Container *Container
canRemove bool
} }
type execStore struct { type execStore struct {
@ -128,7 +130,7 @@ func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, erro
user = container.Config.User user = container.Config.User
} }
processConfig := execdriver.ProcessConfig{ processConfig := &execdriver.ProcessConfig{
Tty: config.Tty, Tty: config.Tty,
Entrypoint: entrypoint, Entrypoint: entrypoint,
Arguments: args, Arguments: args,
@ -221,7 +223,6 @@ func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout
execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err) execErr <- fmt.Errorf("Cannot run exec command %s in container %s: %s", execName, container.ID, err)
} }
}() }()
select { select {
case err := <-attachErr: case err := <-attachErr:
if err != nil { if err != nil {
@ -236,7 +237,7 @@ func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout
} }
func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
exitStatus, err := d.execDriver.Exec(c.command, &execConfig.ProcessConfig, pipes, startCallback) exitStatus, err := d.execDriver.Exec(c.command, execConfig.ProcessConfig, pipes, startCallback)
// On err, make sure we don't leave ExitCode at zero // On err, make sure we don't leave ExitCode at zero
if err != nil && exitStatus == 0 { if err != nil && exitStatus == 0 {
@ -248,3 +249,37 @@ func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pi
return exitStatus, err return exitStatus, err
} }
// execCommandGC runs a ticker to clean up the daemon references
// of exec configs that are no longer part of the container.
func (d *Daemon) execCommandGC() {
for range time.Tick(5 * time.Minute) {
var (
cleaned int
liveExecCommands = d.containerExecIds()
)
for id, config := range d.execCommands.s {
if config.canRemove {
cleaned++
d.execCommands.Delete(id)
} else {
if _, exists := liveExecCommands[id]; !exists {
config.canRemove = true
}
}
}
logrus.Debugf("clean %d unused exec commands", cleaned)
}
}
// containerExecIds returns a list of all the current exec ids that are in use
// and running inside a container.
func (d *Daemon) containerExecIds() map[string]struct{} {
ids := map[string]struct{}{}
for _, c := range d.containers.List() {
for _, id := range c.execCommands.List() {
ids[id] = struct{}{}
}
}
return ids
}

Просмотреть файл

@ -124,6 +124,5 @@ func (daemon *Daemon) ContainerExecInspect(id string) (*execConfig, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return eConfig, nil return eConfig, nil
} }