Skip to content
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

nginx service: reload instead of restart #24476

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 25 additions & 5 deletions nixos/modules/services/web-servers/nginx/default.nix
Expand Up @@ -415,19 +415,39 @@ in
description = "Nginx Web Server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
stopIfChanged = false;
preStart =
''
mkdir -p ${cfg.stateDir}/logs
chmod 700 ${cfg.stateDir}
chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
'';
ln -sf ${configFile} /run/nginx/config
ln -sf ${cfg.package} /run/nginx/package
'';
reload = ''
# Check if the new config is valid
${cfg.package}/bin/nginx -t -c ${configFile} -p ${cfg.stateDir}

# Check if the package changed
if [[ `readlink /run/nginx/package` != ${cfg.package} ]]; then
# If it changed, we need to restart nginx. So we kill nginx
# gracefully. We can't send a restart to systemd while in the
# reload script. Nginx will be restarted by systemd automatically.
${pkgs.coreutils}/bin/kill -QUIT $MAINPID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like this to respect restartIfChanged option

exit 0
fi

# We only need to change the configuration, so update it and reload nginx
ln -sf ${configFile} /run/nginx/config
${pkgs.coreutils}/bin/kill -HUP $MAINPID
'';
restartTriggers = [ configFile ];
reloadIfChanged = true;
serviceConfig = {
ExecStart = "${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir}";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
ExecStart = "${cfg.package}/bin/nginx -c /run/nginx/config -p ${cfg.stateDir}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, RuntimeDirectory is removed when service is stopped. This effectively makes impossible to inspect nginx config after systemctl stop nginx. I propose to inject config path somehow into systemd unit. For example, as a comment. So it would be possible:

$ sudo systemctl stop nginx
$ less $(systemctl cat nginx | grep "#Configuration=" | cut -d= -f2)

Main usecase for this was to diagnose why can't it be started, but you also added nginx -t call, so this may be no longer of an urgent need.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a very good idea

Restart = "always";
RestartSec = "10s";
RestartSec = "1s";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to wait a whole second and not use the default value?

RestartSec=¶
Configures the time to sleep before restarting a service (as configured with Restart=). Takes a unit-less value in seconds, or a time span value such as "5min 20s". Defaults to 100ms.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the default should be fine. I just wanted to reduce the default time of 10s, because that would be an unnecessary wait. I'll change that.

StartLimitInterval = "1min";
RuntimeDirectory = "nginx";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Maybe also make RuntimeDirectoryMode=0750?

};
};

Expand Down
80 changes: 55 additions & 25 deletions nixos/tests/nginx.nix
@@ -1,42 +1,72 @@
# verifies:
# 1. nginx generates config file with shared http context definitions above
# generated virtual hosts config.
# 2. configuration reload works and nginx keeps running with old confi on
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo confi

# syntax errors

import ./make-test.nix ({ pkgs, ...} : {
name = "jenkins";
name = "nginx";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ mbbx6spp ];
maintainers = [ mbbx6spp fpletz ];
};

nodes = {
webserver =
{ config, pkgs, ... }:
{ services.nginx.enable = true;
services.nginx.commonHttpConfig = ''
log_format ceeformat '@cee: {"status":"$status",'
'"request_time":$request_time,'
'"upstream_response_time":$upstream_response_time,'
'"pipe":"$pipe","bytes_sent":$bytes_sent,'
'"connection":"$connection",'
'"remote_addr":"$remote_addr",'
'"host":"$host",'
'"timestamp":"$time_iso8601",'
'"request":"$request",'
'"http_referer":"$http_referer",'
'"upstream_addr":"$upstream_addr"}';
'';
services.nginx.virtualHosts."0.my.test" = {
extraConfig = ''
nodes = let
commonConfig = customCfg:
{ lib, ... }:
lib.mkMerge [
{ services.nginx.enable = true;
services.nginx.commonHttpConfig = ''
log_format ceeformat '@cee: {"status":"$status",'
'"request_time":$request_time,'
'"upstream_response_time":$upstream_response_time,'
'"pipe":"$pipe","bytes_sent":$bytes_sent,'
'"connection":"$connection",'
'"remote_addr":"$remote_addr",'
'"host":"$host",'
'"timestamp":"$time_iso8601",'
'"request":"$request",'
'"http_referer":"$http_referer",'
'"upstream_addr":"$upstream_addr"}';
'';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: do we actually need this complex log format in a test?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test came originally from #23279. I agree that we could probably simplify it a bit.

services.nginx.virtualHosts."_".extraConfig = ''
access_log syslog:server=unix:/dev/log,facility=user,tag=mytag,severity=info ceeformat;
'';
};
}
customCfg
];
in {
webserver = commonConfig {
services.nginx.virtualHosts."_".extraConfig = "return 200 works;";
};
};
webserver_new = commonConfig {
services.nginx.virtualHosts."_".extraConfig = "return 200 new-content;";
services.nginx.package = pkgs.nginxMainline;
};
webserver_invalid = commonConfig {
services.nginx.appendHttpConfig = "foo";
};
};

testScript = { nodes, ... }: let
webserver_new = nodes.webserver_new.config.system.build.toplevel;
webserver_invalid = nodes.webserver_invalid.config.system.build.toplevel;
in ''
$webserver->start;

$webserver->waitForUnit("nginx");
$webserver->waitForOpenPort("80");
$webserver->succeed("[[ `curl http://localhost/` == \"works\" ]]");

$webserver->succeed("${webserver_new}/bin/switch-to-configuration test");

$webserver->waitForUnit("nginx");
$webserver->waitForOpenPort("80");
$webserver->succeed("[[ `curl http://localhost/` == \"new-content\" ]]");

testScript = ''
startAll;
$webserver->fail("${webserver_invalid}/bin/switch-to-configuration test");

$webserver->waitForUnit("nginx");
$webserver->waitForOpenPort("80");
$webserver->succeed("[[ `curl http://localhost/` == \"new-content\" ]]");
'';
})