Add config parameter to change per-container stop timeout during daemon shutdown

This fix tries to add a flag `--stop-timeout` to specify the timeout value
(in seconds) for the container to stop before SIGKILL is issued. If stop timeout
is not specified then the default timeout (10s) is used.

Additional test cases have been added to cover the change.

This fix is related to #22471. Another pull request will add `--shutdown-timeout`
to daemon for #22471.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
Yong Tang 2016-05-26 13:34:48 -07:00
parent f302226a5b
commit e66d210891
11 changed files with 81 additions and 5 deletions

View File

@ -44,6 +44,11 @@ import (
const configFileName = "config.v2.json"
const (
// defaultStopTimeout is the timeout (in seconds) for the syscall signal used to stop a container.
defaultStopTimeout = 10
)
var (
errInvalidEndpoint = fmt.Errorf("invalid endpoint while building port map info")
errInvalidNetwork = fmt.Errorf("invalid network settings while building port map info")
@ -578,6 +583,14 @@ func (container *Container) StopSignal() int {
return int(stopSignal)
}
// StopTimeout returns the timeout (in seconds) used to stop the container.
func (container *Container) StopTimeout() int {
if container.Config.StopTimeout != nil {
return *container.Config.StopTimeout
}
return defaultStopTimeout
}
// InitDNSHostConfig ensures that the dns fields are never nil.
// New containers don't ever have those fields nil,
// but pre created containers can still have those nil values.

View File

@ -34,3 +34,27 @@ func TestContainerStopSignal(t *testing.T) {
t.Fatalf("Expected 9, got %v", s)
}
}
func TestContainerStopTimeout(t *testing.T) {
c := &Container{
CommonContainer: CommonContainer{
Config: &container.Config{},
},
}
s := c.StopTimeout()
if s != defaultStopTimeout {
t.Fatalf("Expected %v, got %v", defaultStopTimeout, s)
}
stopTimeout := 15
c = &Container{
CommonContainer: CommonContainer{
Config: &container.Config{StopTimeout: &stopTimeout},
},
}
s = c.StopSignal()
if s != 15 {
t.Fatalf("Expected 15, got %v", s)
}
}

View File

@ -708,8 +708,8 @@ func (daemon *Daemon) shutdownContainer(c *container.Container) error {
if err := daemon.containerUnpause(c); err != nil {
return fmt.Errorf("Failed to unpause container %s with error: %v", c.ID, err)
}
if _, err := c.WaitStop(10 * time.Second); err != nil {
logrus.Debugf("container %s failed to exit in 10 seconds of SIGTERM, sending SIGKILL to force", c.ID)
if _, err := c.WaitStop(time.Duration(c.StopTimeout()) * time.Second); err != nil {
logrus.Debugf("container %s failed to exit in %d second of SIGTERM, sending SIGKILL to force", c.ID, c.StopTimeout())
sig, ok := signal.SignalMap["KILL"]
if !ok {
return fmt.Errorf("System does not support SIGKILL")
@ -721,8 +721,8 @@ func (daemon *Daemon) shutdownContainer(c *container.Container) error {
return err
}
}
// If container failed to exit in 10 seconds of SIGTERM, then using the force
if err := daemon.containerStop(c, 10); err != nil {
// If container failed to exit in c.StopTimeout() seconds of SIGTERM, then using the force
if err := daemon.containerStop(c, c.StopTimeout()); err != nil {
return fmt.Errorf("Failed to stop container %s with error: %v", c.ID, err)
}

View File

@ -128,6 +128,7 @@ This section lists each version from latest to oldest. Each listing includes a
* `DELETE /containers/(name)` endpoint now returns an error of `removal of container name is already in progress` with status code of 400, when container name is in a state of removal in progress.
* `GET /containers/json` now supports a `is-task` filter to filter
containers that are tasks (part of a service in swarm mode).
* `POST /containers/create` now takes `StopTimeout` field.
### v1.24 API changes

View File

@ -284,6 +284,7 @@ Create a container
"22/tcp": {}
},
"StopSignal": "SIGTERM",
"StopTimeout": 10,
"HostConfig": {
"Binds": ["/tmp:/tmp"],
"Links": ["redis3:redis"],
@ -391,6 +392,7 @@ Create a container
- **ExposedPorts** - An object mapping ports to an empty object in the form of:
`"ExposedPorts": { "<port>/<tcp|udp>: {}" }`
- **StopSignal** - Signal to stop a container as a string or unsigned integer. `SIGTERM` by default.
- **StopTimeout** - Timeout (in seconds) to stop a container. 10 by default.
- **HostConfig**
- **Binds** A list of volume bindings for this container. Each volume binding is a string in one of these forms:
+ `host-src:container-dest` to bind-mount a host path into the
@ -580,7 +582,8 @@ Return low-level information on the container `id`
"/volumes/data": {}
},
"WorkingDir": "",
"StopSignal": "SIGTERM"
"StopSignal": "SIGTERM",
"StopTimeout": 10
},
"Created": "2015-01-06T15:47:31.485331387Z",
"Driver": "devicemapper",

View File

@ -94,6 +94,7 @@ Options:
Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes),
or `g` (gigabytes). If you omit the unit, the system uses bytes.
--stop-signal string Signal to stop a container, SIGTERM by default (default "SIGTERM")
--stop-timeout=10 Timeout (in seconds) to stop a container
--storage-opt value Storage driver options for the container (default [])
--sysctl value Sysctl options (default map[])
--tmpfs value Mount a tmpfs directory (default [])

View File

@ -101,6 +101,7 @@ Options:
or `g` (gigabytes). If you omit the unit, the system uses bytes.
--sig-proxy Proxy received signals to the process (default true)
--stop-signal string Signal to stop a container, SIGTERM by default (default "SIGTERM")
--stop-timeout=10 Timeout (in seconds) to stop a container
--storage-opt value Storage driver options for the container (default [])
--sysctl value Sysctl options (default map[])
--tmpfs value Mount a tmpfs directory (default [])
@ -620,6 +621,11 @@ or a signal name in the format SIGNAME, for instance SIGKILL.
On Windows, this flag can be used to specify the `credentialspec` option.
The `credentialspec` must be in the format `file://spec.txt` or `registry://keyname`.
### Stop container with timeout (--stop-timeout)
The `--stop-timeout` flag sets the the timeout (in seconds) that a pre-defined (see `--stop-signal`) system call
signal that will be sent to the container to exit. After timeout elapses the container will be killed with SIGKILL.
### Specify isolation technology for container (--isolation)
This option is useful in situations where you are running Docker containers on

View File

@ -496,3 +496,18 @@ exec "$@"`,
out, _ = dockerCmd(c, "start", "-a", id)
c.Assert(strings.TrimSpace(out), check.Equals, "foo")
}
// #22471
func (s *DockerSuite) TestCreateStopTimeout(c *check.C) {
name1 := "test_create_stop_timeout_1"
dockerCmd(c, "create", "--name", name1, "--stop-timeout", "15", "busybox")
res := inspectFieldJSON(c, name1, "Config.StopTimeout")
c.Assert(res, checker.Contains, "15")
name2 := "test_create_stop_timeout_2"
dockerCmd(c, "create", "--name", name2, "busybox")
res = inspectFieldJSON(c, name2, "Config.StopTimeout")
c.Assert(res, checker.Contains, "null")
}

View File

@ -68,6 +68,7 @@ docker-create - Create a new container
[**--security-opt**[=*[]*]]
[**--storage-opt**[=*[]*]]
[**--stop-signal**[=*SIGNAL*]]
[**--stop-timeout**[=*TIMEOUT*]]
[**--shm-size**[=*[]*]]
[**--sysctl**[=*[]*]]
[**-t**|**--tty**]
@ -352,6 +353,9 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
**--stop-signal**=*SIGTERM*
Signal to stop a container. Default is SIGTERM.
**--stop-timeout**=*10*
Timeout (in seconds) to stop a container. Default is 10.
**--sysctl**=SYSCTL
Configure namespaced kernel parameters at runtime

View File

@ -70,6 +70,7 @@ docker-run - Run a command in a new container
[**--security-opt**[=*[]*]]
[**--storage-opt**[=*[]*]]
[**--stop-signal**[=*SIGNAL*]]
[**--stop-timeout**[=*TIMEOUT*]]
[**--shm-size**[=*[]*]]
[**--sig-proxy**[=*true*]]
[**--sysctl**[=*[]*]]
@ -502,6 +503,9 @@ incompatible with any restart policy other than `none`.
**--stop-signal**=*SIGTERM*
Signal to stop a container. Default is SIGTERM.
**--stop-timeout**=*10*
Timeout (in seconds) to stop a container. Default is 10.
**--shm-size**=""
Size of `/dev/shm`. The format is `<number><unit>`.
`number` must be greater than `0`. Unit is optional and can be `b` (bytes), `k` (kilobytes), `m`(megabytes), or `g` (gigabytes).

View File

@ -106,6 +106,7 @@ type ContainerOptions struct {
init bool
initPath string
credentialSpec string
stopTimeout int
Image string
Args []string
@ -145,6 +146,7 @@ func AddFlags(flags *pflag.FlagSet) *ContainerOptions {
ulimits: NewUlimitOpt(nil),
volumes: opts.NewListOpts(nil),
volumesFrom: opts.NewListOpts(nil),
stopSignal: flags.String("stop-signal", signal.DefaultStopSignal, fmt.Sprintf("Signal to stop a container, %v by default", signal.DefaultStopSignal)),
}
// General purpose flags
@ -558,6 +560,9 @@ func Parse(flags *pflag.FlagSet, copts *ContainerOptions) (*container.Config, *c
if flags.Changed("stop-signal") {
config.StopSignal = copts.stopSignal
}
if flags.Changed("stop-timeout") {
config.StopTimeout = copts.flStopTimeout
}
hostConfig := &container.HostConfig{
Binds: binds,