Skip to content
Permalink
Browse files

Add init process for zombie fighting

This adds a small C binary for fighting zombies.  It is mounted under
`/dev/init` and is prepended to the args specified by the user.  You
enable it via a daemon flag, `dockerd --reaper`, as it is disable by
default for backwards compat.

You can test this by running a process like this as the pid 1 in a
container and see the extra zombie that appears in the container as it
is running.

```c

int main(int argc, char ** argv) {
	pid_t pid = fork();
	if (pid == 0) {
		pid = fork();
		if (pid == 0) {
			exit(0);
		}
		sleep(3);
		exit(0);
	}
	printf("got pid %d and exited\n", pid);
	sleep(20);
}
```

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Add docker-init to build scripts

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
  • Loading branch information...
crosbymichael committed Jun 27, 2016
1 parent b42ab41 commit 393515d28dbdd360977b04e5767e1d0933b88a6b
@@ -251,6 +251,16 @@ RUN set -x \
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
&& rm -rf "$GOPATH"

ENV GRIMES_COMMIT f207601a8d19a534cc90d9e26e037e9931ccb9db
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/grimes.git "$GOPATH/grimes" \
&& cd "$GOPATH/grimes" \
&& git checkout -q "$GRIMES_COMMIT" \
&& make \
&& cp init /usr/local/bin/docker-init \
&& rm -rf "$GOPATH"

# Wrap all commands in the "docker-in-docker" script to allow nested containers
ENTRYPOINT ["hack/dind"]

@@ -207,6 +207,16 @@ RUN set -x \
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
&& rm -rf "$GOPATH"

ENV GRIMES_COMMIT f207601a8d19a534cc90d9e26e037e9931ccb9db
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/grimes.git "$GOPATH/grimes" \
&& cd "$GOPATH/grimes" \
&& git checkout -q "$GRIMES_COMMIT" \
&& make \
&& cp init /usr/local/bin/docker-init \
&& rm -rf "$GOPATH"

# Wrap all commands in the "docker-in-docker" script to allow nested containers
ENTRYPOINT ["hack/dind"]

@@ -205,6 +205,16 @@ RUN set -x \
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
&& rm -rf "$GOPATH"

ENV GRIMES_COMMIT f207601a8d19a534cc90d9e26e037e9931ccb9db
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/grimes.git "$GOPATH/grimes" \
&& cd "$GOPATH/grimes" \
&& git checkout -q "$GRIMES_COMMIT" \
&& make \
&& cp init /usr/local/bin/docker-init \
&& rm -rf "$GOPATH"

ENTRYPOINT ["hack/dind"]

# Upload docker source
@@ -218,6 +218,16 @@ RUN set -x \
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
&& rm -rf "$GOPATH"

ENV GRIMES_COMMIT f207601a8d19a534cc90d9e26e037e9931ccb9db
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/grimes.git "$GOPATH/grimes" \
&& cd "$GOPATH/grimes" \
&& git checkout -q "$GRIMES_COMMIT" \
&& make \
&& cp init /usr/local/bin/docker-init \
&& rm -rf "$GOPATH"

# Wrap all commands in the "docker-in-docker" script to allow nested containers
ENTRYPOINT ["hack/dind"]

@@ -226,6 +226,16 @@ RUN set -x \
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
&& rm -rf "$GOPATH"

ENV GRIMES_COMMIT f207601a8d19a534cc90d9e26e037e9931ccb9db
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/grimes.git "$GOPATH/grimes" \
&& cd "$GOPATH/grimes" \
&& git checkout -q "$GRIMES_COMMIT" \
&& make \
&& cp init /usr/local/bin/docker-init \
&& rm -rf "$GOPATH"

# Wrap all commands in the "docker-in-docker" script to allow nested containers
ENTRYPOINT ["hack/dind"]

@@ -80,6 +80,16 @@ RUN set -x \
&& cp bin/ctr /usr/local/bin/docker-containerd-ctr \
&& rm -rf "$GOPATH"

ENV GRIMES_COMMIT f207601a8d19a534cc90d9e26e037e9931ccb9db
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/crosbymichael/grimes.git "$GOPATH/grimes" \
&& cd "$GOPATH/grimes" \
&& git checkout -q "$GRIMES_COMMIT" \
&& make \
&& cp init /usr/local/bin/docker-init \
&& rm -rf "$GOPATH"

ENV AUTO_GOPATH 1
WORKDIR /usr/src/docker
COPY . /usr/src/docker
@@ -35,6 +35,7 @@ type Config struct {
Runtimes map[string]types.Runtime `json:"runtimes,omitempty"`
DefaultRuntime string `json:"default-runtime,omitempty"`
OOMScoreAdjust int `json:"oom-score-adjust,omitempty"`
Reaper bool `json:"reaper,omitempty"`
}

// bridgeConfig stores all the bridge driver specific
@@ -91,6 +92,7 @@ func (config *Config) InstallFlags(flags *pflag.FlagSet) {
flags.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), "add-runtime", "Register an additional OCI compatible runtime")
flags.StringVar(&config.DefaultRuntime, "default-runtime", stockRuntimeName, "Default OCI runtime for containers")
flags.IntVar(&config.OOMScoreAdjust, "oom-score-adjust", -500, "Set the oom_score_adj for the daemon")
flags.BoolVar(&config.Reaper, "reaper", false, "Run an init in the container to reap zombie processes")

config.attachExperimentalFlags(flags)
}
@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
@@ -593,6 +594,19 @@ func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container)
cwd = "/"
}
s.Process.Args = append([]string{c.Path}, c.Args...)
if daemon.configStore.Reaper {
s.Process.Args = append([]string{"/dev/init", c.Path}, c.Args...)
path, err := exec.LookPath("docker-init")
if err != nil {
return err
}
s.Mounts = append(s.Mounts, specs.Mount{
Destination: "/dev/init",
Type: "bind",
Source: path,
Options: []string{"bind", "ro"},
})
}
s.Process.Cwd = cwd
s.Process.Env = c.CreateDaemonEnvironment(linkedEnv)
s.Process.Terminal = c.Config.Tty
@@ -255,15 +255,15 @@ bundle() {
source "$SCRIPTDIR/make/$bundle" "$@"
}

copy_containerd() {
copy_binaries() {
dir="$1"
# Add nested executables to bundle dir so we have complete set of
# them available, but only if the native OS/ARCH is the same as the
# OS/ARCH of the build target
if [ "$(go env GOOS)/$(go env GOARCH)" == "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" ]; then
if [ -x /usr/local/bin/docker-runc ]; then
echo "Copying nested executables into $dir"
for file in containerd containerd-shim containerd-ctr runc; do
for file in containerd containerd-shim containerd-ctr runc init; do
cp `which "docker-$file"` "$dir/"
if [ "$2" == "hash" ]; then
hash_files "$dir/docker-$file"
@@ -7,3 +7,4 @@ DOCKER_CONTAINERD_BINARY_NAME='docker-containerd'
DOCKER_CONTAINERD_CTR_BINARY_NAME='docker-containerd-ctr'
DOCKER_CONTAINERD_SHIM_BINARY_NAME='docker-containerd-shim'
DOCKER_PROXY_BINARY_NAME='docker-proxy'
DOCKER_INIT_BINARY_NAME='docker-init'
@@ -12,5 +12,5 @@ set -e
export BINARY_SHORT_NAME="$DOCKER_PROXY_BINARY_NAME"
export SOURCE_PATH='./vendor/src/github.com/docker/libnetwork/cmd/proxy'
source "${MAKEDIR}/.binary"
copy_containerd "$DEST" 'hash'
copy_binaries "$DEST" 'hash'
)
@@ -12,4 +12,5 @@ rm -rf "$DEST"
install_binary "${DEST}/${DOCKER_CONTAINERD_CTR_BINARY_NAME}"
install_binary "${DEST}/${DOCKER_CONTAINERD_SHIM_BINARY_NAME}"
install_binary "${DEST}/${DOCKER_PROXY_BINARY_NAME}"
install_binary "${DEST}/${DOCKER_INIT_BINARY_NAME}"
)
@@ -53,8 +53,8 @@ for d in "$CROSS/"*/*; do
cp -L "$d/$PROXY_BINARY_FULLNAME" "$TAR_PATH/${DOCKER_PROXY_BINARY_NAME}${BINARY_EXTENSION}"
fi

# copy over all the containerd binaries
copy_containerd $TAR_PATH
# copy over all the extra binaries
copy_binaries $TAR_PATH

if [ "$IS_TAR" == "true" ]; then
echo "Creating tgz from $BUILD_PATH and naming it $TGZ"
@@ -75,40 +75,6 @@ func (s *DockerSuite) TestEventsUntag(c *check.C) {
}
}

func (s *DockerSuite) TestEventsContainerFailStartDie(c *check.C) {
_, _, err := dockerCmdWithError("run", "--name", "testeventdie", "busybox", "blerg")
c.Assert(err, checker.NotNil, check.Commentf("Container run with command blerg should have failed, but it did not"))

out, _ := dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c))
events := strings.Split(strings.TrimSpace(out), "\n")

nEvents := len(events)
c.Assert(nEvents, checker.GreaterOrEqualThan, 1) //Missing expected event

actions := eventActionsByIDAndType(c, events, "testeventdie", "container")

var startEvent bool
var dieEvent bool
for _, a := range actions {
switch a {
case "start":
startEvent = true
case "die":
dieEvent = true
}
}

// Windows platform is different from Linux, it will start container whatever
// so Windows can get start/die event but Linux can't
if daemonPlatform == "windows" {
c.Assert(startEvent, checker.True, check.Commentf("Start event not found: %v\n%v", actions, events))
c.Assert(dieEvent, checker.True, check.Commentf("Die event not found: %v\n%v", actions, events))
} else {
c.Assert(startEvent, checker.False, check.Commentf("Start event not expected: %v\n%v", actions, events))
c.Assert(dieEvent, checker.False, check.Commentf("Die event not expected: %v\n%v", actions, events))
}
}

func (s *DockerSuite) TestEventsLimit(c *check.C) {
var waitGroup sync.WaitGroup
errChan := make(chan error, 17)
@@ -2402,30 +2402,6 @@ func (s *DockerSuite) TestRunExposePort(c *check.C) {
c.Assert(out, checker.Contains, "invalid range format for --expose")
}

func (s *DockerSuite) TestRunUnknownCommand(c *check.C) {
out, _, _ := dockerCmdWithStdoutStderr(c, "create", "busybox", "/bin/nada")

cID := strings.TrimSpace(out)
_, _, err := dockerCmdWithError("start", cID)

// Windows and Linux are different here by architectural design. Linux will
// fail to start the container, so an error is expected. Windows will
// successfully start the container, and once started attempt to execute
// the command which will fail.
if daemonPlatform == "windows" {
// Wait for it to exit.
waitExited(cID, 30*time.Second)
c.Assert(err, check.IsNil)
} else {
c.Assert(err, check.NotNil)
}

rc := inspectField(c, cID, "State.ExitCode")
if rc == "0" {
c.Fatalf("ExitCode(%v) cannot be 0", rc)
}
}

func (s *DockerSuite) TestRunModeIpcHost(c *check.C) {
// Not applicable on Windows as uses Unix-specific capabilities
testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
@@ -1227,11 +1227,11 @@ func (s *DockerSuite) TestRunPidsLimit(c *check.C) {
testRequires(c, pidsLimit)

file := "/sys/fs/cgroup/pids/pids.max"
out, _ := dockerCmd(c, "run", "--name", "skittles", "--pids-limit", "2", "busybox", "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "2")
out, _ := dockerCmd(c, "run", "--name", "skittles", "--pids-limit", "4", "busybox", "cat", file)
c.Assert(strings.TrimSpace(out), checker.Equals, "4")

out = inspectField(c, "skittles", "HostConfig.PidsLimit")
c.Assert(out, checker.Equals, "2", check.Commentf("setting the pids limit failed"))
c.Assert(out, checker.Equals, "4", check.Commentf("setting the pids limit failed"))
}

func (s *DockerSuite) TestRunPrivilegedAllowedDevices(c *check.C) {
@@ -59,7 +59,6 @@ func DefaultSpec() specs.Spec {
Options: []string{"nosuid", "noexec", "nodev"},
},
}

s.Process.Capabilities = []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",

0 comments on commit 393515d

Please sign in to comment.
You can’t perform that action at this time.