Skip to content

Commit cddac88

Browse files
author
Bryan Latten
authored
Nginx: using cont-finish.d for more graceful shutdown (#67)
* Nginx: using cont-init.d for more graceful shutdown First suppress initial TERM/HUP, then use cont-finish.d as shutdown script * README: added drain timing explanation
1 parent 188e4c7 commit cddac88

File tree

8 files changed

+52
-46
lines changed

8 files changed

+52
-46
lines changed

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
FROM behance/docker-base:2.6-ubuntu-18.04
22
MAINTAINER Bryan Latten <[email protected]>
33

4+
# Use in multi-phase builds, when an init process requests for the container to gracefully exit, so that it may be committed
5+
# Used with alternative CMD (worker.sh), leverages supervisor to maintain long-running processes
46
ENV CONTAINER_ROLE=web \
57
CONTAINER_PORT=8080 \
68
CONF_NGINX_SITE="/etc/nginx/sites-available/default" \
79
CONF_NGINX_SERVER="/etc/nginx/nginx.conf" \
8-
NOT_ROOT_USER=www-data
10+
NOT_ROOT_USER=www-data \
11+
S6_KILL_FINISH_MAXTIME=55000
912

1013
# Using a non-privileged port to prevent having to use setcap internally
1114
EXPOSE ${CONTAINER_PORT}

Dockerfile-alpine

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ ENV CONTAINER_ROLE=web \
77
CONTAINER_PORT=8080 \
88
CONF_NGINX_SITE="/etc/nginx/sites-available/default" \
99
CONF_NGINX_SERVER="/etc/nginx/nginx.conf" \
10-
NOT_ROOT_USER=www-data
10+
NOT_ROOT_USER=www-data \
11+
S6_KILL_FINISH_MAXTIME=55000
1112

1213
# Using a non-privileged port to prevent having to use setcap internally
1314
EXPOSE ${CONTAINER_PORT}

Dockerfile-centos

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
FROM behance/docker-base:2.6-centos
22
MAINTAINER Bryan Latten <[email protected]>
33

4+
# Use in multi-phase builds, when an init process requests for the container to gracefully exit, so that it may be committed
5+
# Used with alternative CMD (worker.sh), leverages supervisor to maintain long-running processes
46
ENV CONTAINER_ROLE=web \
57
CONTAINER_PORT=8080 \
68
CONF_NGINX_SITE="/etc/nginx/sites-available/default" \
79
CONF_NGINX_SERVER="/etc/nginx/nginx.conf" \
8-
NOT_ROOT_USER=nginx
10+
NOT_ROOT_USER=nginx \
11+
S6_KILL_FINISH_MAXTIME=55000
912

1013
# Using a non-privileged port to prevent having to use setcap internally
1114
EXPOSE ${CONTAINER_PORT}

README.md

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,59 +17,48 @@ See parent(s) [docker-base](https://github.com/behance/docker-base) for addition
1717

1818
### Expectations
1919

20-
Applications using this as a container parent must copy their html/app into the `/var/www/html` folder
21-
NOTE: Nginx is exposed and bound to an unprivileged port, `8080`
20+
- Applications must copy their html/app into the `/var/www/html` folder
21+
- NOTE: Nginx is exposed and bound to an unprivileged port, `8080`
2222

2323

2424
### Security
2525

26-
For Ubuntu-based variants, a convenience script is provided for security-only package updates. To run: `/bin/bash -e /security_updates.sh`
26+
See parent [configuration](https://github.com/behance/docker-base#security)
2727

2828

2929
### Environment Variables
3030

3131
Variable | Example | Description
3232
--- | --- | ---
33-
`SERVER_MAX_BODY_SIZE` | `SERVER_MAX_BODY_SIZE=4M` | Allows the downstream application to specify a non-default `client_max_body_size` configuration for the `server`-level directive in `/etc/nginx/sites-available/default`
34-
`SERVER_INDEX` | `SERVER_INDEX index.html index.html index.php` | Changes the default pages to hit for folder and web roots
35-
`SERVER_APP_NAME` | `SERVER_APP_NAME='view'` | Gets appended to the default logging format
36-
`SERVER_GZIP_OPTIONS` | `SERVER_GZIP_OPTIONS=1` | Allows default set of static content to be served gzipped
37-
`SERVER_SENDFILE` | `SERVER_SENDFILE=off` | Allows runtime to specify value of nginx's `sendfile` (default, on)
38-
`SERVER_KEEPALIVE` | `SERVER_KEEPALIVE=30` | Define HTTP 1.1's keepalive timeout
39-
`SERVER_WORKER_PROCESSES` | `SERVER_WORKER_PROCESSES=4` | Set to the number of cores in the machine, or the number of cores allocated to container
40-
`SERVER_WORKER_CONNECTIONS` | `SERVER_WORKER_CONNECTIONS=2048` | Sets up the number of connections for worker processes
41-
`SERVER_CLIENT_HEADER_BUFFER_SIZE` | `SERVER_CLIENT_HEADER_BUFFER_SIZE=16k | [docs](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_buffer_size)
42-
`SERVER_LARGE_CLIENT_HEADER_BUFFERS` | `SERVER_LARGE_CLIENT_HEADER_BUFFERS=8 16k` | [docs](http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers)
43-
`SERVER_CLIENT_BODY_BUFFER_SIZE` | `SERVER_CLIENT_BODY_BUFFER_SIZE=128k` | [docs](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size)
44-
`SERVER_LOG_MINIMAL` | `SERVER_LOG_MINIMAL=1` | Minimize the logging format, appropriate for development environments
45-
`S6_KILL_FINISH_MAXTIME` | `S6_KILL_FINISH_MAXTIME=1000` | Wait time (in ms) for zombie reaping before sending a kill signal
46-
`S6_KILL_GRACETIME` | `S6_KILL_GRACETIME=500` | Wait time (in ms) for S6 finish scripts before sending kill signal
33+
SERVER_MAX_BODY_SIZE | SERVER_MAX_BODY_SIZE=4M | Allows the downstream application to specify a non-default `client_max_body_size` configuration for the `server`-level directive in `/etc/nginx/sites-available/default`
34+
SERVER_INDEX | SERVER_INDEX index.html index.html index.php | Changes the default pages to hit for folder and web roots
35+
SERVER_APP_NAME | SERVER_APP_NAME='view' | Gets appended to the default logging format
36+
SERVER_GZIP_OPTIONS | SERVER_GZIP_OPTIONS=1 | Allows default set of static content to be served gzipped
37+
SERVER_SENDFILE | SERVER_SENDFILE=off | Allows runtime to specify value of nginx's `sendfile` (default, on)
38+
SERVER_KEEPALIVE | SERVER_KEEPALIVE=30 | Define HTTP 1.1's keepalive timeout
39+
SERVER_WORKER_PROCESSES | SERVER_WORKER_PROCESSES=4 | Set to the number of cores in the machine, or the number of cores allocated to container
40+
SERVER_WORKER_CONNECTIONS | SERVER_WORKER_CONNECTIONS=2048 | Sets up the number of connections for worker processes
41+
SERVER_CLIENT_HEADER_BUFFER_SIZE | SERVER_CLIENT_HEADER_BUFFER_SIZE=16k | [docs](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_buffer_size)
42+
SERVER_LARGE_CLIENT_HEADER_BUFFERS | SERVER_LARGE_CLIENT_HEADER_BUFFERS=8 16k | [docs](http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers)
43+
SERVER_CLIENT_BODY_BUFFER_SIZE | SERVER_CLIENT_BODY_BUFFER_SIZE=128k | [docs](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size)
44+
SERVER_LOG_MINIMAL | SERVER_LOG_MINIMAL=1 | Minimize the logging format, appropriate for development environments
45+
S6_KILL_FINISH_MAXTIME | S6_KILL_FINISH_MAXTIME=55000 | The maximum time (in ms) a script in /etc/cont-finish.d could take before sending a KILL signal to it. Take into account that this parameter will be used per each script execution, it's not a max time for the whole set of scripts. This value has a max of 65535 on Alpine variants.
46+
S6_KILL_GRACETIME | S6_KILL_GRACETIME=500 | Wait time (in ms) for S6 finish scripts before sending kill signal
4747

4848

4949
### Startup/Runtime Modification
5050

51-
To inject changes just before runtime, shell scripts (ending in .sh) may be placed into the
52-
`/etc/cont-init.d` folder. For example, the above environment variables are used to drive nginx configuration at runtime.
53-
As part of the process manager, these scripts are run in advance of the supervised processes. @see https://github.com/just-containers/s6-overlay#executing-initialization-andor-finalization-tasks
51+
- Environment variables are used to drive nginx configuration at runtime
52+
- See [here](https://github.com/behance/docker-base#startupruntime-modification) for more advanced options
5453

54+
### Shutdown Behavior
5555

56-
### Advanced Modification
57-
58-
More advanced changes can take effect using the run.d system. Similar to the `/etc/cont-init.d/` script system, any scripts (ending in .sh) in the `/run.d/` folder will be executed ahead of the S6 initialization.
59-
60-
- If run.d script terminates with a non-zero exit code, container will stop, terminating with the script's exit code, unless...
61-
- If script terminates with exit code of $SIGNAL_BUILD_STOP (99), this will signal the container to stop cleanly. This can be used for multi-stage builds
62-
56+
Graceful shutdown is handled as part of the [existing](https://github.com/behance/docker-base#shutdown-behavior) S6 termination process, using a `/etc/cont-finish.d` script.
57+
Nginx will attempt to drain active workers, while rejecting new connections. The drain timeout is controlled by `S6_KILL_FINISH_MAXTIME`, which corresponds to the length of time the supervisor will wait for the script to run during shutdown. This value defaults to 55s, which deliberately `less` than an downstream load balancers default max connection length (60s). Each upstream's timeout must be less than the downstream, for sanity and lack of timing precision.
6358

6459
### Long-running processes (workers + crons)
6560

66-
This container image can be shared between web and non-web processes. An example use case would be
67-
a web service and codebase that also has a few crons and background workers. To reuse this container for
68-
those types of workloads:
69-
70-
`docker run {image_id} /worker.sh 3 /bin/binary -parameters -that -binary -receives`
71-
72-
Runs `3` copies of `/bin/binary` that receives the parameters `-parameters -that -binary -receives`
61+
- See parent [configuration](https://github.com/behance/docker-base#long-running-processes-workers--crons) on reusing container for other purposes.
7362

7463

7564
### Container Organization
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/execlineb -P
2+
3+
foreground { nginx -s quit }
4+
echo "[finish nginx] shutting down gracefully"
File renamed without changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
# Validate that env config overrides are acceptable to nginx
4+
# Suppress output, unless it fails
5+
nginx -t &> /dev/null
6+
7+
# Quick and dirty, if the above has failed, re-run it without suppressing output
8+
if [ $? == 1 ]; then
9+
nginx -t
10+
fi

container/root/etc/services-available/nginx/run

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
#!/usr/bin/execlineb -P
22

3-
# @see https://github.com/just-containers/s6-overlay/issues/41#issuecomment-99366363
3+
# TERM broadcast is ignored in order to shut down gracefully,
4+
# Shutdown implemented in /etc/cont-finish.d
45

5-
# Nginx uses SIGQUIT to gracefully stop serving traffic.
6-
trap -x
6+
trap
77
{
88
term
99
{
10-
foreground
11-
{
12-
nginx -s quit
13-
}
14-
echo [sigterm-nginx] graceful shutdown complete
10+
echo [run nginx] TERM received
1511
}
1612
hup
1713
{
18-
echo [signhup-nginx] received, ignored
14+
echo [run nginx] HUP received
1915
}
2016
}
2117

0 commit comments

Comments
 (0)