`if` or `include` for mode-specific `server` directives?

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

`if` or `include` for mode-specific `server` directives?

redflag
I compile Nginx mainline from source and update every release. I run a small
fleet of open source project and some small business Linux servers with
multiple websites per server. There are occasions when a site is taken down
for maintenance (typically minutes or hours of downtime out of peak hours),
or is under development for extended periods, and also when 'normal'
production status is happening. I use a different directory for each mode,
so there is separation of files etc. A typical setup looks like this:

/var/www/sites/example.com/subdomain/live/ (production mode)
/var/www/sites/example.com/subdomain/holding/ (under construction, coming
soon)
/var/www/sites/example.com/subdomain/maintenance/ (back soon)

At present, each site is managed by a monolithic
`subdomain.example.com.conf` file with its own `server` block and associated
directives. I manually change the `root` for each state when a site changes
mode. The current modes are:

* live (production) state serves files as usual, no changes.
* holding (under construction) state presents HTTP 503 Service Unavailable
status with relatively long Retry-After response header.
* maintenance (back soon) state presents HTTP 503 Service Unavailable status
with relatively short Retry-After response header.

Each site has a unique variable namespace and its own mode (live, holding,
maintenance), like this:

server {
    set $fqdn_abcd1234 'subdomain.example.com';
    set $status_abcd1234 'live';
    ...
}

I would like to change the operation to include some extra, browser-friendly
functionality and to make better use of site variables.

Two of the three modes above have a Retry-After header, and each of those
has a different time value. This gives me a few potential routes to contend
with, and I would be grateful for your advice on feedback on what is
considered current best practice.

== `if` in the `server` block ==

I will readily admit I have never used `if`, having been scared away by If
Is Evil. My (untested) approach looks like this:

server {
    set $fqdn_abcd1234 'subdomain.example.com';
    set $status_abcd1234 'live';
    root /var/www/sites/example.com/subdomain/$status_abcd1234/;
    if ($status_abcd1234 = 'holding') {
        return 503;
        add_header Retry-After 604800 always;
    }
    if ($status_abcd1234 = 'maintenance') {
        return 503;
        add_header Retry-After 10800 always;
    }
}

So, two inlined `if` inside a monolithic `server` block file.

== Stub `server` excerpts for each mode ==

Rather than include `root` and `if` checks in the monolithic `server` block
file, I am considering outboarding each mode to its own stub file as a
sidecar to the main `server` block file, like this:

* `subdomain.example.com--live.conf`
* `subdomain.example.com--holding.conf`
* `subdomain.example.com--maintenance.conf`

Each site mode stub would include the relevant `return` and `add_header`
directives as required, plus any other mode-specific things, and be called
by an `include` directive in the main `server` block file. No `if` involved
in this route.

== Something else I haven't thought of ==

You folks are much smarter than me. I am certain I've missed something,
whether it's obvious or not. What else might I be able to do in this
scenario, please?

Given the above routes, which is (subjectively / objectively) better from an
Nginx point of view?

Thank you for reading, and best wishes to you from sunny Cornwall, United
Kingdom.

Pete

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,288057,288057#msg-288057

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: `if` or `include` for mode-specific `server` directives?

redflag
Reply | Threaded
Open this post in threaded view
|

Re: `if` or `include` for mode-specific `server` directives?

Francis Daly
In reply to this post by redflag
On Sat, May 16, 2020 at 05:24:42AM -0400, petecooper wrote:

Hi there,

> At present, each site is managed by a monolithic
> `subdomain.example.com.conf` file with its own `server` block and associated
> directives. I manually change the `root` for each state when a site changes
> mode.

> Each site has a unique variable namespace and its own mode (live, holding,
> maintenance), like this:
>
> server {
>     set $fqdn_abcd1234 'subdomain.example.com';
>     set $status_abcd1234 'live';
>     ...
> }

Your current system is that you edit the conf file to change some "set"
variable values, and reload nginx.

I suggest that you'll be happier in the long run using a templating
language, or macro-substituting language, external to nginx; along with
"source" conf files that are to have the substitutions applied; and
change the value there and regenerate the nginx conf parts, and then
reload nginx.

That language (your own shell scripts; m4; or whatever you like the
look of) will be able to write exactly the "root/return/add_header"
or whatever statements that you want based on the variable values that
you set.

It is a significant change to your setup right now; but it means that
your running nginx config will have exactly what you want, without needing
to worry about levels of indirection of run-time variable substitution.


"set" to a static string is using nginx-conf like a macro language.

"When all you have is a hammer, everything looks like a thumb."

I suspect you'll be better off using a real macro language -- you can
do more things with it, and the running nginx will have less work to do
on every request.

> == `if` in the `server` block ==
>
> I will readily admit I have never used `if`, having been scared away by If
> Is Evil.

"If Is Evil" is limited to "when used in location context"; so you're
ok to do what you suggest here.

It just is not as efficient as it could be.

> == Stub `server` excerpts for each mode ==
>
> Rather than include `root` and `if` checks in the monolithic `server` block
> file, I am considering outboarding each mode to its own stub file as a
> sidecar to the main `server` block file, like this:

This is better -- presumably you will edit the "include" line and reload
nginx (or may use symlinks; and change where the symlink points and
reload nginx) when you want to change mode.

If you can avoid the run-time variable substitution altogether, this
is good.

If your "mode"-conf files have lots of parts that are very similar apart
from a mode-related word, then you may be ok with manually updating all
of them when you want to add something else; an external thing that just
generates the three "mode" files could avoid that manual-duplication step.

> Given the above routes, which is (subjectively / objectively) better from an
> Nginx point of view?

Objectively, using run-time variable substitution means that nginx does
more work on every request than it would do without that.

And nginx testing one or two extra "if" statements on every request
means that nginx does more work than it would do without that.

It's not a lot of extra work; and the machine-time saved over a year
may not make up for the person-time taken in changing to a system that
avoids those things.

Good luck with it,

        f
--
Francis Daly        [hidden email]
_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx
Reply | Threaded
Open this post in threaded view
|

Re: `if` or `include` for mode-specific `server` directives?

redflag
Hi Francis.

Francis Daly Wrote:
-------------------------------------------------------

> I suggest that you'll be happier in the long run using a templating
> language, or macro-substituting language, external to nginx; along
> with
> "source" conf files that are to have the substitutions applied; and
> change the value there and regenerate the nginx conf parts, and then
> reload nginx.
> [...]
> It is a significant change to your setup right now; but it means that
> your running nginx config will have exactly what you want, without
> needing
> to worry about levels of indirection of run-time variable
> substitution.

Excellent. Thank you. You're absolutely correct, this is a big step change
for me and will require some r&d, but I like the sounds of it.

> > == `if` in the `server` block ==
> >
> > I will readily admit I have never used `if`, having been scared away
> by If
> > Is Evil.
>
> "If Is Evil" is limited to "when used in location context"; so you're
> ok to do what you suggest here.
>
> It just is not as efficient as it could be.

Message received and understood on the `if` context, thank you.

> > == Stub `server` excerpts for each mode ==
> >
> > Rather than include `root` and `if` checks in the monolithic
> `server` block
> > file, I am considering outboarding each mode to its own stub file as
> a
> > sidecar to the main `server` block file, like this:
>
> This is better -- presumably you will edit the "include" line and
> reload
> nginx (or may use symlinks; and change where the symlink points and
> reload nginx) when you want to change mode.

Correct - edit, check config, then reload.
 
> If you can avoid the run-time variable substitution altogether, this
> is good.

This is useful, too. I have zero reliance on any `set` variables at a site
identification level, and I would prefer to reduce the workload Nginx has to
undertake generally for the sake of more human time when it comes to mode
switching.

> If your "mode"-conf files have lots of parts that are very similar
> apart
> from a mode-related word, then you may be ok with manually updating
> all
> of them when you want to add something else; an external thing that
> just
> generates the three "mode" files could avoid that manual-duplication
> step.

My intention is for the mode-related stubs to just contain the unique parts:
`root`, the `add_header` and anything else that crops up. The vast majority
of site directives are deliberately (at least currently) included in the
main `server` block file, the outboarding would be as little as possible
directive-wise.

> Objectively, using run-time variable substitution means that nginx
> does
> more work on every request than it would do without that.
>
> And nginx testing one or two extra "if" statements on every request
> means that nginx does more work than it would do without that.
>
> It's not a lot of extra work; and the machine-time saved over a year
> may not make up for the person-time taken in changing to a system that
> avoids those things.

...and I have my next lockdown project!

Thank you very much for your informative and useful reply, I really
appreciate it.

With best wishes,

Pete

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,288057,288061#msg-288061

_______________________________________________
nginx mailing list
[hidden email]
http://mailman.nginx.org/mailman/listinfo/nginx