← 返回文章列表 专栏:Linux 与系统安全

更新于

Web 服务故障排查清单:从 DNS、TCP、TLS 到容器与磁盘的六层诊断流程

当站点出现无法访问、证书报错或 403/502 异常时,按 DNS、TCP 连通性、宿主机监听、Nginx 网关、Docker 容器与内核资源的顺序逐层定位根因。

生产环境六层故障排查链路:从 DNS、TCP、TLS 到容器与内核日志
生产环境六层故障排查链路:从 DNS、TCP、TLS 到容器与内核日志

遇到线上站点无法访问时,如果在没有排查日志和端口状态的情况下直接重启容器或服务器,不仅会丢失故障现场,遇到磁盘写满或配置文件语法错误时,重启反而会导致服务无法再次启动。

本文按自底向上的网络与系统分层,整理一套从客户端 DNS 解析、TCP 端口探测、TLS 握手核验到容器状态与宿主机磁盘检查的分步排查清单。

网站无法访问时的分层排查顺序

发生访问异常时,可按照下图所示的 L1 → L6 顺序依次执行诊断命令,逐步缩小问题范围:

网站不可达时的分层排障决策链结构图
从客户端 DNS 与 TCP 握手出发,经宿主机端口监听与本机回环 HTTP 测试,区分公网防火墙拦截与容器内部故障。

第 1 层(L1):核对客户端 DNS 解析是否指向正确 IP

在本地电脑终端使用公共递归 DNS(如 223.5.5.5 或 1.1.1.1)查询域名,排除本机 Hosts 配置、DNS 缓存陈旧或域名过期问题:

1
2
3
# 在本地电脑执行:核对 A / AAAA 记录返回的地址是否与 <服务器地址> 一致
dig +short <博客域名> A @223.5.5.5
dig +short <博客域名> AAAA @223.5.5.5
  • 常见现象:返回空值、SERVFAIL 或指向了已停用的旧服务器 IP。
  • 处理方式:前往域名 DNS 控制台核对记录值与域名状态,无需登录服务器改动容器。

第 2 层(L2):在客户端测试外部 TCP 握手与 TLS 协商

如果 DNS 解析正确,接下来在本地电脑对 <服务器地址> 的 80 / 443 端口发起带 5 秒超时的连接测试:

1
2
# 在本地电脑执行:验证 TCP 三次握手与 HTTP/HTTPS 响应头
curl -Iv --connect-timeout 5 https://<博客域名>/
  • 情况 A(返回 Connection timed out):SYN 数据包在传输途中被丢弃,通常是云厂商控制台安全组或宿主机 UFW 防火墙未放行 80/tcp、443/tcp。
  • 情况 B(返回 Connection refused):数据包已到达服务器网卡,但宿主机内核回复 RST(当前没有进程在该端口监听),继续排查第 3 层。
  • 情况 C(返回 SSL certificate problem: certificate has expired):TLS 证书过期或容器未重载最新证书,可执行 acme.sh --cron 并运行 docker exec blog-web nginx -s reload。

第 3 层(L3):登录服务器检查宿主机套接字监听(ss -tlnp)

通过 SSH 登录服务器,查看宿主机内核网络栈中 80 与 443 端口的绑定情况:

1
2
# 在服务器执行:检查监听地址是 0.0.0.0 还是仅绑定了 127.0.0.1
sudo ss -tlnp | grep -E ':(80|443)\b'
  • 常见原因:如果输出显示 127.0.0.1:80 而非 0.0.0.0:80,说明容器端口映射只绑定了本机回环地址,且外层没有配置宿主机反向代理,导致公网请求被拒绝。

第 4 层(L4):本机回环 HTTP 探测(区分外部网络与容器内部故障)

如果 ss 显示端口正在监听,在服务器本机对 127.0.0.1 发起 HTTP 请求,并带上站点 Host 头:

1
2
# 在服务器本机执行:绕过公网与外部安全组,直接测试 Nginx 容器响应
curl -sI -H "Host: <博客域名>" http://127.0.0.1/ | head -n 15
  • 若本机返回 200 OK 而外网超时:问题在云厂商安全组、CDN 回源配置或外部网络路由,容器本身运行正常。
  • 若本机返回 403 Forbidden / 404 Not Found / 500:请求已进入 Nginx 容器,通常是挂载目录权限不足(例如宿主机目录权限为 700 导致容器内 nginx worker 进程无权读取)或 /srv/blog/releases/current 软链接指向了空目录。

第 5 层(L5):检查 Docker 容器状态、退出码与挂载路径

当第 3 层无监听或第 4 层返回异常时,查看 blog-web 容器的运行状态、退出码与错误日志:

1
2
3
4
# 查看容器状态、退出码(Exit Code)与最近 50 行错误日志
docker ps -a --filter "name=blog-web"
docker inspect blog-web --format 'Status={{.State.Status}} ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}'
docker logs --tail 50 blog-web
  • ExitCode=1 且日志提示 [emerg]:通常是修改 default.conf 时引入了语法错误,导致容器启动后立即退出。
  • OOMKilled=true(或 ExitCode=137):容器或宿主机其他进程耗尽内存被内核终止,继续排查第 6 层。

第 6 层(L6):排查宿主机内核 OOM、磁盘空间与 Inode 占用

容器无法启动或日志停止写入,有时是因为宿主机底层资源耗尽:

1
2
3
4
5
6
7
8
9
# 1. 检查内核是否有 Out of Memory 终止进程记录
sudo dmesg -T | grep -iE 'out of memory|killed process' | tail -n 10

# 2. 检查磁盘块容量(df -h)与文件节点数(df -i)
df -h
df -i

# 3. 检查是否存在已被删除但仍被进程占用未释放的大文件
sudo lsof +L1

如果 df -h 显示磁盘使用率达到 100%,不要直接 rm 正在被进程写入的日志文件(进程未关闭句柄时磁盘空间不会释放),而应使用 truncate -s 0 <日志文件路径> 原地清空文件,再运行 sudo logrotate -f /etc/logrotate.d/blog-nginx。

生产环境上线核对表

站点部署上线前,可对照下表逐项核验系统安全、前端构建与容器配置:

模块 检查项 关联文章 核验命令或标准
架构与目录 固定链接与目录结构 个人技术博客架构选型 宿主目录为 /srv/blog/releases/current,文章路由固定为 /posts/<slug>/
系统与权限 目录属主与 Systemd 托管 Linux 文件系统与 Systemd 静态目录权限 755、文件 644,关键守护进程配置 Restart=on-failure
SSH 与防火墙 密钥登录与端口收敛 Ubuntu 初始化与 SSH 加固 PasswordAuthentication no,UFW 仅放行 SSH、80/tcp 与 443
网络与证书 DNS、BBR 与 HTTPS 续期 DNS、TCP BBR 与 ACME 证书 sysctl net.ipv4.tcp_congestion_control 输出 bbr,TLS 1.3 握手正常
静态构建 草稿隔离与构建校验 Astro 静态构建与发布流程 npm run check 无报错通过,草稿文件不进入 public/
主题与色彩 明暗主题与正文字体对比度 CSS 色彩变量与明暗主题 正文对比度满足 WCAG AA(>= 7.2:1),刷新页面无明暗闪烁
版面与交互 响应式双栏与代码块复制 长文双栏网格与目录交互 桌面端 Sticky 目录高亮正常,窄屏下代码块与表格无横向溢出
内容与插图 敏感信息扫描与 SVG 校验 技术写作规范与构建检查 仓库无真实公网 IP 或凭据残留,SVG 架构图包含完整 viewBox 与描述
检索与图谱 异步检索防竞态与图谱渲染 本地全文检索与知识图谱 快速输入检索词无旧结果覆盖,Canvas 图谱支持缩放、拖拽与节点筛选
Docker 基础 镜像分层与容器网络隔离 Docker 镜像、网络与数据卷 守护进程配置日志上限,容器运行在自定义 Bridge 网络中
Nginx 网关 路由匹配、真实 404 与缓存头 Nginx 进程模型与路由配置 随机不存在路径返回真实 HTTP/2 404,HTML 与静态资源采用分级缓存
只读容器托管 Compose 只读挂载与端口绑定 Docker Compose + Nginx 托管 静态产物与配置均以 :ro 只读挂载,容器常驻内存低于 25 MB
自愈与发布 容器自愈、日志切分与回滚 容器自愈、日志轮转与发布脚本 logrotate 配合 USR1 信号切分日志,发布脚本支持失败自动回滚
故障诊断 分层排障链路验证 本文 掌握 dig +trace、curl -w、ss -tlnp、docker inspect 与 dmesg -T