Live data from Hacker News

Upgrading Executable on the Fly

nginx.org

61–70 of 72 posts

Re: Upgrading Executable on the Fly

#61
post #9

Just a shout out: it's super hard to do it for UDP / QUIC / H3. Beware. (but I don't think nginx supports h3 out of the box yet)

It's fundamentally identical. How do you think one hands off a TLS session? Or any other state?

It's only a problem if your state is tangled and impossible to serialize or bundle up to hand off.

UDP is perhaps the easiest because there's nothing to do in the basic case, for example with DNS.

Re: Upgrading Executable on the Fly

#62

I am curious does anyone know why Nginx uses SIGWINCH for this? I know Apache uses WINCH as well which makes me wonder if there was some historical reason a server process wound up using a signal meant for a TTY?

It's otherwise unused in the context of a daemon. SIGHUP is similarly popular and historically designed for use with a TTY.

Re: Upgrading Executable on the Fly

#63

I am curious does anyone know why Nginx uses SIGWINCH for this? I know Apache uses WINCH as well which makes me wonder if there was some historical reason a server process wound up using a signal meant for a TTY?

It's otherwise unused in the context of a daemon. SIGHUP is similarly popular and historically designed for use with a TTY.

That's what I suspected - that it would pretty much be guaranteed to not to be needed however I was not able to find any historical UNIX lore mentioning it specifically. Cheers.

Re: Upgrading Executable on the Fly

#64
post #2

We did this for Caddy 1 too [1]. It was really cool. I am not sure how many people used this feature, so I haven't implemented it for Caddy 2 yet, and in the ~two years that Caddy 2 has been released, I've only had the request once. It's a bit tricky/tedious to do properly, but I'm willing to bring it over to Caddy 2 with a sufficient sponsorship. [1]: https://github.com/caddyserver/caddy/blob/v1/upgrade.go

Can you explain any of the technical details around this perchance? I'm super curious. I know that SO_REUSEPORT[1] exists but is that the only little trick to make this work? From what I've read with SO_REUSEPORT it can open up that port to hijacking by rogue processes, so is that fine to rely on? [1] https://lwn.net/Articles/542629/

poked around a bit of that from a previous job, here's what I remember:

1. there's a control process and worker processes

2. on upgrade, control process launches new worker processes from the new binary

3. requests are drained from old worker processes

4. most of the time nginx request handlers allocate from a per-request allocation pool, so requests mostly don't share memory

5. for the cases where there are global states, there's a separate shared memory pool that you need to allocate from (which is kind of hard to work if you are not using built-in nginx primitives)

Re: Upgrading Executable on the Fly

#65
post #16

Earlier quoted context omitted.

> an application should never be able to replace itself with "random code" to be executed. To clarify: it doesn't, nor has it ever worked that way. You have to be the one to do that (or someone with privileges to write to that file on disk). Most production setups don't give Caddy that permission. And you have to trigger the upgrade too.

Ok, this explains a lot and also clearly shows I never used this feature :) I always assumed it would be the Caddy process itself taking care of downloading the update & replacing the binary, before restarting it.

Caddy 2 can do that at least, but it's something you have to command it to do. :)

Re: Upgrading Executable on the Fly

#67
post #51
post #50

Earlier quoted context omitted.

It's the same but a bit different. Inetd is listening to a port and then for each new connection, spawning a new process, binding stdin/stout to the socket pair. The main issue was that it could lead to system resource exhaustion pretty easily if too many connections were being opened and there were no good ways to control that. With systemd, the listening socket is only bound by systemd and passed to the service. Th…

Sure, though systemd does support the inetd style setup also.

Yes despite the haters systemD is going the right direction. Just hope it doesn't crumble under its complexity.

Re: Upgrading Executable on the Fly

#68
post #27

Earlier quoted context omitted.

Just to add - Nginx normally spawns several worker processes that all process connections to the same port.

Correct but to clarify, only the master process binds to the ports. The master process creates socketpairs to the workers for interprocess communication. The workers accept connections over the shared socket. https://www.nginx.com/blog/socket-sharding-nginx-release-1-9... Page also has an example of how SO_REUSEPORT effects flow.

Oh, thanks! I didn't know that. I supposed it worked by inheriting the listening socket but I didn't check.

Re: Upgrading Executable on the Fly

#69
post #11

Earlier quoted context omitted.

Using socket option SO_REUSEPORT allows multiple processes to bind to same port.

Is there any restrictions on this option? Eg only children of the same parent process are allowed to bind to same port. Otherwise how does the packet distribution work? And how does the response from that port work?

>So long as the first server sets this option before binding its socket, then any number of other servers can also bind to the same port if they also set the option beforehand. [...] To prevent unwanted processes from hijacking a port that has already been bound by a server using SO_REUSEPORT, all of the servers that later bind to that port must have an effective user ID that matches the effective user ID used to perform the first bind on the socket.

https://lwn.net/Articles/542629/

Re: Upgrading Executable on the Fly

#70
post #37
post #2

We did this for Caddy 1 too [1]. It was really cool. I am not sure how many people used this feature, so I haven't implemented it for Caddy 2 yet, and in the ~two years that Caddy 2 has been released, I've only had the request once. It's a bit tricky/tedious to do properly, but I'm willing to bring it over to Caddy 2 with a sufficient sponsorship. [1]: https://github.com/caddyserver/caddy/blob/v1/upgrade.go

If Caddy were to support systemd socket activation, this self-restart dance is not necessary as the parent process (systemd) is holding the socket for you. And for other systems, they can use https://github.com/zimbatm/socketmaster instead. I believe this to be more elegant and robust than the nginx approach as there is no PID re-parenting issues. But I suspect that most Caddy deployments are done via docker, and tha…

If I'm running on containers, does this mean that I need to run systemd inside docker?
Post reply on HN