Merge pull request #7 from chriskuehl/update-docs
Update docs to explain process group support
This commit is contained in:
commit
b7ee80f0cf
4 changed files with 94 additions and 20 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ dist/
|
|||
*.deb
|
||||
*.egg-info
|
||||
.tox
|
||||
build/
|
||||
|
|
31
CONTRIBUTING.md
Normal file
31
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
Contributing to dumb-init
|
||||
========
|
||||
|
||||
`dumb-init` is primarily developed by [Yelp](https://yelp.github.io/), but
|
||||
contributions are welcome from everyone!
|
||||
|
||||
Code is reviewed using GitHub pull requests. To make a contribution, you should:
|
||||
|
||||
1. Fork the GitHub repository
|
||||
2. Push code to a branch on your fork
|
||||
3. Create a pull request and wait for it to be reviewed
|
||||
|
||||
|
||||
## Releasing new versions
|
||||
|
||||
`dumb-init` uses [semantic versioning](http://semver.org/). If you're making a
|
||||
contribution, please don't bump the version number yourself—we'll take care
|
||||
of that after merging!
|
||||
|
||||
The process to release a new version is:
|
||||
|
||||
1. Run integration tests (`make itest`). Travis doesn't run these, so it's a
|
||||
good idea to make sure they still pass.
|
||||
2. Update the version in `setup.py`
|
||||
3. Update the Debian changelog with `dch -v {new version}`.
|
||||
4. Commit the changes and tag the commit like `v1.0.0`.
|
||||
5. `git push --tags origin master`
|
||||
6. Run `rm -rf dist && python setup.py sdist` to create a source distribution
|
||||
7. Run `twine dist/*` to upload the new version to PyPI
|
||||
8. Run `make builddeb` and upload the resulting Debian package to a new
|
||||
[GitHub release](https://github.com/Yelp/dumb-init/releases)
|
77
README.md
77
README.md
|
@ -5,31 +5,31 @@ dumb-init
|
|||
|
||||
|
||||
`dumb-init` is a simple process designed to run as PID 1 inside Docker
|
||||
containers and proxy signals to a single child process.
|
||||
containers and proxy signals to child processes.
|
||||
|
||||
In Docker containers, a process typically runs as PID 1, which means that
|
||||
signals like TERM will just bounce off your process unless it goes out of its
|
||||
way to handle them (see the "Why" section below). This is a big problem with
|
||||
scripts in languages like Python, Bash, or Ruby, and can lead to leaking Docker
|
||||
scripts in languages like Python, Ruby, or Bash, and can lead to leaking Docker
|
||||
containers if you're not careful.
|
||||
|
||||
|
||||
## Why you need a signal proxy
|
||||
|
||||
Normally, when processes are sent a signal like `TERM`, the Linux kernel will
|
||||
try to trigger any custom handlers the process has registered for that signal.
|
||||
When processes are sent a signal on a normal Linux system, the kernel will
|
||||
first check for any custom handlers the process has registered for that signal,
|
||||
and otherwise fall back to default behavior (for example, killing the process
|
||||
on `TERM`).
|
||||
|
||||
If the process hasn't registered custom handlers, the kernel will fall back to
|
||||
default behavior for that signal (such as killing the process, in the case of
|
||||
`TERM`).
|
||||
However, if the process receiving the signal is PID 1, it gets special
|
||||
treatment by the kernel; if it hasn't registered a handler for the signal, the
|
||||
kernel won't fall back to default behavior, and nothing happens. In other
|
||||
words, if your process doesn't explicitly handle these signals, a `TERM` will
|
||||
have no effect at all.
|
||||
|
||||
However, processes which run as PID 1 get special treatment by the kernel, and
|
||||
default signal handlers won't be applied. If your process doesn't explicitly
|
||||
handle these signals, a `TERM` will have no effect at all.
|
||||
|
||||
For example, if you have Jenkins jobs that do `docker run my-container script`,
|
||||
sending TERM to the `docker run` process will typically kill the `docker run`
|
||||
command, but leave the container running in the background.
|
||||
A common example is CI jobs that do `docker run my-container script`: sending
|
||||
TERM to the `docker run` process will typically kill the `docker run` command,
|
||||
but leave the container running in the background.
|
||||
|
||||
|
||||
## What `dumb-init` does
|
||||
|
@ -39,9 +39,33 @@ single process, and then proxies all received signals to that child process.
|
|||
|
||||
Since your actual process is no longer PID 1, when it receives signals from
|
||||
`dumb-init`, the default signal handlers will be applied, and your process will
|
||||
behave as you would expect.
|
||||
behave as you would expect. If your process dies, `dumb-init` will also die.
|
||||
|
||||
If your process dies, `dumb-init` will also die.
|
||||
|
||||
### Process group behavior
|
||||
|
||||
In its default mode, `dumb-init` establishes a process group rooted at the
|
||||
child, and sends signals to the entire process group. This is useful if you
|
||||
have a poorly-behaving child (such as a shell script) which won't normally
|
||||
signal its children before dying.
|
||||
|
||||
This can actually be useful outside of Docker containers in regular process
|
||||
supervisors like [daemontools][daemontools] or [supervisord][supervisord] for
|
||||
supervising shell scripts. Normally, a signal like SIGTERM received by a shell
|
||||
isn't forwarded to subprocesses; instead, only the shell process dies. With
|
||||
dumb-init, you can just write shell scripts with dumb-init in the shebang:
|
||||
|
||||
#!/usr/bin/dumb-init /bin/sh
|
||||
my-web-server & # launch a process in the background
|
||||
my-other-server # launch another process in the foreground
|
||||
|
||||
Ordinarily, a TERM sent to the shell would leave those processes running. With
|
||||
dumb-init, your subprocesses will receive the same signals your shell does.
|
||||
|
||||
If you'd like for signals to only be sent to the direct child, you can set the
|
||||
environment variable `DUMB_INIT_PROCESS_GROUP=0` when running `dumb-init`. In this
|
||||
mode, dumb-init is completely transparent; you can even string multiple
|
||||
together (like `dumb-init dumb-init echo 'oh, hi'`).
|
||||
|
||||
|
||||
## Installing inside Docker containers
|
||||
|
@ -55,6 +79,9 @@ If you have an internal apt server, uploading the `.deb` to your server is the
|
|||
recommended way to use `dumb-init`. In your Dockerfiles, you can simply
|
||||
`apt-get install dumb-init` and it will be available.
|
||||
|
||||
Debian packages are available from the [GitHub Releases tab][gh-releases], or
|
||||
you can run `make builddeb` yourself.
|
||||
|
||||
|
||||
### Option 2: Installing the `.deb` package manually
|
||||
|
||||
|
@ -62,13 +89,20 @@ If you don't have an internal apt server, you can use `dpkg -i` to install the
|
|||
`.deb` package. You can choose how you get the `.deb` onto your container
|
||||
(mounting a directory or `wget`-ing it are some options).
|
||||
|
||||
One possibility is with the following commands in your Dockerfile:
|
||||
|
||||
```bash
|
||||
RUN wget https://github.com/Yelp/dumb-init/releases/download/v0.1.0/dumb-init_0.1.0_amd64.deb
|
||||
RUN dpkg -i dumb-init_*.deb
|
||||
```
|
||||
|
||||
|
||||
### Option 3: Installing from PyPI
|
||||
|
||||
dumb-init can be installed [from PyPI](https://pypi.python.org/pypi/dumb-init)
|
||||
using pip. Since dumb-init is written in C, you'll want to first install a C compiler
|
||||
(on Debian/Ubuntu, `apt-get install gcc` is sufficient), then just `pip install
|
||||
dumb-init`.
|
||||
using pip. Since dumb-init is written in C, you'll want to first install a C
|
||||
compiler (on Debian/Ubuntu, `apt-get install gcc` is sufficient), then just
|
||||
`pip install dumb-init`.
|
||||
|
||||
|
||||
## Usage
|
||||
|
@ -88,3 +122,8 @@ humane signals like TERM.
|
|||
* [Docker and the PID 1 zombie reaping problem (Phusion Blog)](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/)
|
||||
* [Trapping signals in Docker containers (@gchudnov)](https://medium.com/@gchudnov/trapping-signals-in-docker-containers-7a57fdda7d86)
|
||||
* [pgctl](https://github.com/Yelp/pgctl)
|
||||
|
||||
|
||||
[daemontools]: http://cr.yp.to/daemontools.html
|
||||
[supervisord]: http://supervisord.org/
|
||||
[gh-releases]: https://github.com/Yelp/dumb-init/releases
|
||||
|
|
|
@ -42,6 +42,9 @@ void print_help(char *argv[]) {
|
|||
fprintf(stderr,
|
||||
"Usage: %s COMMAND [[ARG] ...]\n"
|
||||
"\n"
|
||||
"dumb-init is a simple process designed to run as PID 1 inside Docker\n"
|
||||
"containers and proxy signals to child processes.\n"
|
||||
"\n"
|
||||
"Docker runs your processes as PID1. The kernel doesn't apply default signal\n"
|
||||
"handling to PID1 processes, so if your process doesn't register a custom\n"
|
||||
"signal handler, signals like TERM will just bounce off your process.\n"
|
||||
|
@ -58,7 +61,7 @@ void print_help(char *argv[]) {
|
|||
"weird things (this is basically a requirement for doing things sanely in\n"
|
||||
"Docker anyway).\n"
|
||||
"\n"
|
||||
"By default, dumb-init starts a process group and kills all processes in it.\n"
|
||||
"By default, dumb-init starts a process group and signals all processes in it.\n"
|
||||
"This is usually useful behavior, but if for some reason you wish to disable\n"
|
||||
"it, run with DUMB_INIT_PROCESS_GROUP=0.\n",
|
||||
argv[0]
|
||||
|
|
Loading…
Reference in a new issue