Docker: Clean shutdown of containers.

Stop() will send a SIGTERM to the init process of the container and give it 10 seconds to shutdown properly.
After the timeout is reached, the process will be force killed (SIGKILL).
Also available is Kill() which kills the process on the spot.
Upstream-commit: 24dac22892cc3c8f34e90222f64b87d750b5025e
Component: engine
This commit is contained in:
Andrea Luzzardi
2013-01-21 18:03:23 -08:00
parent 28140e7cb7
commit 031dbdddeb
2 changed files with 67 additions and 14 deletions

View File

@ -5,10 +5,12 @@ import (
"errors"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"syscall"
"time"
)
type Container struct {
@ -176,22 +178,57 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
func (container *Container) monitor() {
container.cmd.Wait()
exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
// Cleanup container
container.stdout.Close()
container.stderr.Close()
container.State.setStopped(container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus())
if err := container.Filesystem.Umount(); err != nil {
log.Printf("%v: Failed to umount filesystem: %v", container.Name, err)
}
// Report status back
container.State.setStopped(exitCode)
}
func (container *Container) kill() error {
// This will cause the main container process to receive a SIGKILL
if err := exec.Command("/usr/bin/lxc-stop", "-n", container.Name).Run(); err != nil {
return err
}
// Wait for the container to be actually stopped
if err := exec.Command("/usr/bin/lxc-wait", "-n", container.Name, "-s", "STOPPED").Run(); err != nil {
return err
}
return nil
}
func (container *Container) Kill() error {
if !container.State.Running {
return nil
}
return container.kill()
}
func (container *Container) Stop() error {
if container.State.Running {
if err := exec.Command("/usr/bin/lxc-stop", "-n", container.Name).Run(); err != nil {
return err
}
//FIXME: We should lxc-wait for the container to stop
if !container.State.Running {
return nil
}
if err := container.Filesystem.Umount(); err != nil {
// FIXME: Do not abort, probably already umounted?
return nil
// 1. Send a SIGTERM
if err := exec.Command("/usr/bin/lxc-kill", "-n", container.Name, "15").Run(); err != nil {
return err
}
// 2. Wait for the process to exit on its own
if err := container.WaitTimeout(10 * time.Second); err != nil {
log.Printf("Container %v failed to exit within 10 seconds of SIGTERM", container.Name)
}
// 3. Force kill
if err := container.kill(); err != nil {
return err
}
return nil
}
@ -201,3 +238,19 @@ func (container *Container) Wait() {
container.State.wait()
}
}
func (container *Container) WaitTimeout(timeout time.Duration) error {
done := make(chan bool)
go func() {
container.Wait()
done <- true
}()
select {
case <-time.After(timeout):
return errors.New("Timed Out")
case <-done:
return nil
}
return nil
}

View File

@ -98,15 +98,15 @@ func TestOutput(t *testing.T) {
}
}
func TestStop(t *testing.T) {
func TestKill(t *testing.T) {
docker, err := newTestDocker()
if err != nil {
t.Fatal(err)
}
container, err := docker.Create(
"stop_test",
"sleep",
[]string{"300"},
"cat",
[]string{"/dev/zero"},
[]string{"/var/lib/docker/images/ubuntu"},
&Config{},
)
@ -124,7 +124,7 @@ func TestStop(t *testing.T) {
if !container.State.Running {
t.Errorf("Container should be running")
}
if err := container.Stop(); err != nil {
if err := container.Kill(); err != nil {
t.Fatal(err)
}
if container.State.Running {
@ -135,7 +135,7 @@ func TestStop(t *testing.T) {
t.Errorf("Container shouldn't be running")
}
// Try stopping twice
if err := container.Stop(); err != nil {
if err := container.Kill(); err != nil {
t.Fatal(err)
}
}