记一次Nginx异常重启失败

一大早收到几个服务同时不可用的告警,一查又是 Nginx 出现启动失败,手动重启后恢复正常,这个情况过去的一年里,也出了一次,同样的时间,于是好好排查下。直接说结论,两个原因:一是系统底层更新,引发多服务重启,包括 nginx 在内。二是 nginx 一个 upstream 用了公网域名,解析失败,导致启动失败。

下面是过程,首先看 Nginx 日志,精简后如下:

[emerg] host not found in upstream "webapi.xxx.com" in /etc/nginx/sites-enabled/com.xxx.conf
nginx: configuration file /etc/nginx/nginx.conf test failed

Nignx 相关的配置,很早就没动过,比较迷惑的是手动执行 nginx -t 却都是 ok 的,唯独这次事故中出现了 test failed,仔细一看,上多了一条 host not found,或许刚好那个点就解析失败了,因为是遗留配置,没有使用,直接删除。

接下来就好玩了,哪个“缺德” Job 会在凌晨6点重启 Nginx?甚至这个时间点重启了好几个重要服务。印象中也就是 acme 刷新证书配置了 reload,一查时间对不上,crontab中也没有 Job 能去操作 Nginx。没有头绪的时候问了下 GPT,给出了一个关键点:系统定时服务,果然就有个时间很吻合,结果如下:

~# systemctl list-timers --all

LAST 2026-02-04 06:03:03 CST
UNIT apt-daily-upgrade.timer
ACTIVATES apt-daily-upgrade.service

看看这个 apt-daily-upgrade 究竟干了什么:

~# journalctl -u apt-daily-upgrade.service --since "2026-02-04 06:00:00" --until "2026-02-04 06:06:00" --no-pager -o short-iso

systemd[1]: Starting apt-daily-upgrade.service - Daily apt upgrade and clean activities...
apt.systemd.daily[4037440]: /usr/bin/unattended-upgrade:567: DeprecationWarning: This process (pid=4037440) is multi-threaded, use of fork() may lead to deadlocks in the child.

日志不多,但明显去了 unattended-upgrade,继续:

~# sudo grep -n "2026-02-04 06:0" /var/log/unattended-upgrades/unattended-upgrades.log | head -n 200

Starting unattended upgrades script
47:2026-02-04 06:03:13,556 INFO Packages that will be upgraded: inetutils-telnet libc-bin libc-dev-bin libc-devtools libc6 libc6-dev libpng16-16t64 locales mysql-client-core-8.0 telnet
49:2026-02-04 06:04:28,538 INFO All upgrades installed

升级了几个底层库,这时候意识到,应该就是这里引发了上层重启,继续:

~# sudo sed -n '1,200p' /var/log/unattended-upgrades/unattended-upgrades-dpkg.log

Restarting services...
systemctl restart aegis.service aliyun.service chrony.service containerd.service cron.service fwupd.service ilogtaild.service multipathd.service mysql.server.service nginx.service php8.3-fpm.service polkit.service redis-server.service rsyslog.service ssh.service systemd-journald.service systemd-networkd.service systemd-resolved.service systemd-udevd.service tuned.service udisks2.service uuidd.service
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.

好家伙,重启了这么多服务。

这次事故里,“触发器”是自动升级导致的服务重启;“根因”是 Nginx 配置模式的脆弱性,upstream 配置在启动期就要求域名可解析,DNS/解析链路只要在重启瞬间抖动一下,Nginx 就起不来,于是任何一次重启(自动升级、人工重启、机器迁移、网络切换)都可能把隐患放大成故障。

SSL证书更换为Let’s Encrypt

  StartSSL的免费证书总不太稳定,尤其在Chrome下表现更是离奇,这不最近在将macOS Chrome升级到最新版的时候,花图影铺的一个二级域名证书就被列为不信任了,但根域名又是正常的,而在火狐或Safari或iOS 10中都是正常的,真是莫名其妙。正好最近Let’s Encrypt比较火,干脆趁这个国庆节将证书换一下。

1. 下载安装证书

git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt/
./letsencrypt-auto certonly --standalone --email admin@snapast.com -d snapast.com -d www.snapast.com -d abc.snapast.com

  这里Let’s Encrypt简化了安装步骤,只需要确保域名解析到本机服务器,并且未占用服务器的80和443端口即可创建成功,默认位置在/etc/letsencrypt/live/snapast.com里面,fullchain.pem即是公钥,包含证书链,privkey.pem私钥。

  需要注意的是,有二级域名未解析到本机的不可以,比如使用了CDN,临时改一下问题不大,本机有nginx之类的占用80端口的,也得临时停一下,反正很快,网络好的情况下几秒钟就可以了。如果站点流量大到几分钟都不能停,那想必也没必要用Let’s Encrypt了,毕竟一次申请的有效期也才三个月。

2. nginx配置

ssl_certificate                 /etc/letsencrypt/live/snapast.com/fullchain.pem;
ssl_certificate_key             /etc/letsencrypt/live/snapast.com/privkey.pem;

  配置好nginx,reload一下就生效了,CDN后台的证书也一并替换掉,大功告成。

wordpress主机迁移

  告别使用两年多的 hellohost 主机,搬到杭州某主机,国内访问速度快了不少。hellohost 主机的 SSH 流量特征被识别,关闭了转发功能,已无多大用处了。正好下个月到期,故不再使用。

  迁移很方便,数据库没多大问题,用 navicat 的数据传输功能很方便将数据库同步过来,记得在 wp_options 表中搜下有关绝对路径的配置,改为新的路径,通常都是一些 plugin 留下的。

  文件打个 tar.gz 包 scp 到新目录下,递归修改拥有者为新主机的 http server 运行用户,并且 wp-content 及子目录权限为置为 777,这样可以在后台自动升级,修改 wp-config.php 数据库连接为新环境。

  麻烦一点的是以前主机用的是 apache 做为 http server,新主机为 nginx,url rewrite 规则不太一样,在 location / 中用 try_files 将请求参数转发到 index.php 上即可,部分代码在文章后面。

  真心觉得nginx好用,配置简单,可作为前端代理,通过域名转发各种后端服务,无比强大。


location / {
index index.html index.php index.htm;
try_files $uri $uri/ /index.php?$args;
}