-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reloading Nginx on schedule with entrypoint script (think LetsEncrypt certs) #509
base: master
Are you sure you want to change the base?
Conversation
P.S. After taking a second look... This solution is still not solid enough. Inotifyd, however, would serve the purpose perfectly.
EDIT: In NFS use case I made and tested code, but since that actually answers different scenarios (e.g. reloading on configuration change, or even running 20-envsubst-on-templates.sh on template change), it will be separate PR. |
That looks like a giant hack to be honest rather than a solution :-) The problems with that approach is that we're introducing some poor-man process management into a container; while I generally agree there are use-cases (such as the one in this PR), it's a bit out of scope for this image. There is no way to check if the configuration was applied, and a mere fact of sending a signal is not enough to figure out the process state. Also, we're supposed to follow official guidelines and best practices and this doesnt exactly fall under the As for the actual implementation, I think that instead of reloading every X minutes (which means more and more long-lived workers are using more CPU/RAM if e.g. you have websockets), I would rather create a flag file in a certbot hook, and check for this file when waking up from sleep. Also, maybe it'll be enought redefine the CMD/command instead of adding another script to have this while loop active during the container lifetime? |
Thank you, it is indeed poor-man process, an attempt to solves a use case that spawned a lot of wild workarounds with some standard approach (if PR would be accepted). That aside, I came up with Using CMD or ENTRYPOINT is something I will test, as well as flag file (note, Nginx will then run under shell). But that will be you helping me with just another workaround for the use case. Next day, next guy will have to search for it again. If we decide that "flag file" is better than below As a result of our conversation, I want a solution, that you and other maintainers would approve. I wonder, if it's possible at all, taking in account So,
|
Yeah, I think the "init-replacement-but-not-really" approach is something we will not accept in the image in any case. However, I see no problem with a good documentation (e.g. providing an entrypoint script in a gist, or another repository) a link to which we can put into https://hub.docker.com/_/nginx. As you've said, not sure if inotify will work on weird filesystems or subsystems such as WSL2, or Docker on Mac, or virtualbox mounts (those are known to have issues with e.g. sendfile()), so my suggestion about creating files was merely a way to use the dumbest way possible to communicate through containers. Maybe inotify is good enough, though, I don't really have an opinion. |
That's what I thought. I'll come up with something we can document. Don't blame me, if it becomes popular! ;) |
You can execute With Docker/Podman, I don't see why you couldn't run a command in the nginx container(s) after the files are updated by the ACME client in a volume. If you are accessing from a distant machine, there is a tool called ssh (and if you don't trust the machine running the ACME client, you can pull the certificates from another trusted machine). Your use-case of NFS where inotify doesn't work seems very specific, I'd rather upload copies of secrets using scp/rsync then ssh into the machines for reloading nginx, with an automated configuration manager such as salt, rather than using NFS for sharing secrets. I had several issues in production with unexpected disconnections due to network outage, which was not as easy to monitor as pushing files to several machines. (I even considered the case of using Kubernetes and avoiding cert-manager, you can use shareProcessNamespace to communicate with nginx from a sidecar container.) |
I noticed this thread a while ago, and since I agree that the reload task is out of scope for the main Nginx image I just wanted to notify this thread that I have been tinkering with the "nginx+certbot in one image" solution mentioned above. |
It is a hack, but you can use the healthcheck to accomplish this:
Or something similar. |
I'm doing the same hack :) however I think having the inotify package available, would already be a big boon. (see #791). With regards to the init discussion made before by @thresheek here #509 (comment) I think dumb-init's readme (iirc tini says the same), they strongly 'urge' to use atleast tini/dumb-init as an init system, especially for something like nginx, with all these signals and multiple workers jumping around. And since the image by default already launches some commands (albeit before jumping to nginx), it's only fair to add an init solution (as I proposed in #791. |
A small update with updated working script as a help for people searching for a workaround for given problem (e.g. various Nginx deployments that use centralized NFS share for LE certificates):
ARG NGINX_VERSION
FROM nginx:${NGINX_VERSION}
...
COPY ./docker-entrypoint.d/*.sh /docker-entrypoint.d/
RUN chmod +x /docker-entrypoint.d/*.sh
#!/bin/sh
set -e
[ "${NGINX_ENTRYPOINT_NGINX_RELOAD_EVERY_X_HOURS:-}" ] || exit 0 # exit early if necessary variable is not given
entrypoint_log() {
if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
echo "$@"
fi
}
ME=$(basename $0)
if [ $(echo "$NGINX_ENTRYPOINT_NGINX_RELOAD_EVERY_X_HOURS > 0" | bc) == 0 ]; then
entrypoint_log "$ME: Provide integer or floating point number greater that 0. See 'man sleep'."
exit 1
fi
start_background_reload() {
entrypoint_log "$ME: Reloading Nginx every $NGINX_ENTRYPOINT_NGINX_RELOAD_EVERY_X_HOURS hour(s)"
while :; do
entrypoint_log "$ME: Sleeping ..."
sleep ${NGINX_ENTRYPOINT_NGINX_RELOAD_EVERY_X_HOURS}h
entrypoint_log "$ME: Reloading Nginx ..."
nginx -s reload
done &
}
start_background_reload A quick test: source .env
docker build --build-arg NGINX_VERSION=$NGINX_VERSION --tag mynginxtest . && docker run -e NGINX_ENTRYPOINT_NGINX_RELOAD_EVERY_X_HOURS=0.002 mynginxtest # 0.002h ~=7.2sec |
I have the following in my compose file:
where the reload check is
which in short goes over all config files, calculates the checksum of the whole shebang and stores it for later reference. If there's a diff, and we have a valid nginx config, we'll reload. with
to ensure I have the entrypoint needed. |
@oliv3r I have a suggestion for the checksum part. You can use the output of I came across this since I'm also looking for a solid solution for my own nginx container image. |
When I first wrote this script, To illustrate my concern, We have a valid nginx config, which we store the crc value of. Great. But now, behind the scenes the config is changed, expected behavior, but it is invalid. if But I'm definitely going to check this out, use the output of
Yeah, In the end, we only care about whether the certificate has been changed. To go more complex, I'd hope that nginx would fix this internally. E.g. by having an inotify watch on the certificate, and parse the expiry date etc or something. Doing this with scripts (like this one) is already a clude imo. So the only failure case here could be that we replace an up-to-date working certificate, with an older expired certificate. Operator error for sure, but would be nice to catch this. In the end, the operator would just be confused why his certificate isn't loading. Besides, if all is automated (lets-encrypt) this rare usecase shouldn't even be possible. KISS :p
I've been using this for 2 years now, and am quite happy with it. Your suggestion about |
My first exploration is not positive. |
The script is a no-op by default, you would need to enable its logic by
setting an NGINX_ENTRYPOINT_RELOAD_EVERY_X_HOURS variable to integer/float value greater than 0 (argument to
sleep
)The script then puts an infinite loop in backgound, reloading Nginx on a schedule.
Example use case:
This PR solves following questions, e.g.:
Alternatives (or workarounds) would be:
Tested with:
Resulting in successful reload:
Value validation test:
P.S. Kudos for idea goes to https://dev.to/koddr/how-to-dockerize-your-static-website-with-nginx-automatic-renew-ssl-for-domain-by-certbot-and-deploy-it-to-digitalocean-4cjc
P.P.S Followed example of
30-tune-worker-processes.sh
commit 2b06409 by @thresheekTODO! describe parameter in https://github.com/docker-library/docs/tree/master/nginx