网络通了之后,机器要对外提供能力。本章讲四个最典型的服务:Web、DNS、时间、打印。
把"网络可用"变成"服务可用"。每个服务都有共同的骨架:
监听端口 → 读取配置 → 处理请求 → 记录日志 → 由 systemd 监护
学会一个服务的排查路径,其他服务都能套用。
nginx 现在主流用法不是"当 Web 服务器",而是反向代理 + TLS 终结 + 静态资源。
| 用法 | 说明 |
|---|---|
| 静态文件服务器 | 直接返回文件 |
| 反向代理 | 转发到后端应用(Python/Node/Java) |
| TLS 终结 | 证书、HTTPS 卸载 |
| 负载均衡 | 多后端分发 |
| API 网关(轻量) | 路由、限流 |
/etc/nginx/
├── nginx.conf # 主配置
├── conf.d/*.conf # 发行版约定的片段目录
├── sites-available/ # Debian 系:可用站点
├── sites-enabled/ # Debian 系:启用(→ available 的软链接)
├── snippets/ # 可复用片段
└── mime.types
配置的层级模型(理解嵌套关系是关键):
main(全局)
├─ events { } # 连接处理模型
└─ http { # 所有 HTTP 相关
├─ server { # 一个虚拟主机(监听某个端口/域名)
│ ├─ location / { } # 按 URI 路由
│ └─ location /api/ { }
└─ upstream backend { } # 后端服务器组
一份实用的配置(静态 + 反向代理):
# /etc/nginx/sites-available/example.com
upstream app_backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# HTTP → HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
root /var/www/example.com;
index index.html;
access_log /var/log/nginx/example.access.log;
error_log /var/log/nginx/example.error.log warn;
location /static/ {
expires 30d;
add_header Cache-Control "public, immutable";
}
location /api/ {
proxy_pass http://app_backend/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
}启用站点:
$ sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
$ sudo nginx -t # ← 语法检查,改完必做
$ sudo systemctl reload nginx # 平滑重载(不断连接)
$ sudo systemctl restart nginx # 重启(会断连接)| 陷阱 | 说明 |
|---|---|
proxy_pass http://host/ 尾斜杠 |
有无尾斜杠语义不同:有则替换 location 前缀,无则拼接原 URI |
location 匹配优先级 |
=(精确)> ^~(前缀优先)> ~/~*(正则)> 普通前缀 |
server_name 不匹配 |
落到 default_server,可能返回错误站点 |
root vs alias |
root 拼接 URI,alias 替换 location 部分 |
| reload 不生效 | 配置加载成功但未 reload;或 upstream 有缓存 |
nginx -T打印合并后的完整配置(含 include 展开)。当你不确定实际生效的配置是什么时,用这个而不是人肉追 include。
$ sudo nginx -t # ① 语法
$ sudo nginx -T | grep -A5 proxy_pass # ② 实际生效的配置
$ systemctl status nginx # ③ 服务状态
$ sudo journalctl -u nginx -n 50 # ④ 服务日志
$ sudo tail -f /var/log/nginx/error.log # ⑤ 错误日志 ← 最有用
$ sudo tail -f /var/log/nginx/access.log # ⑥ 访问日志| 症状 | 原因 |
|---|---|
| 502 Bad Gateway | 后端没起来 / 端口错 / SELinux(setsebool -P httpd_can_network_connect 1) |
| 403 Forbidden | 目录权限、SELinux 标签(restorecon)、缺 index |
| 404 | root/alias 配错、路径拼接问题 |
| 504 Timeout | 后端太慢,调 proxy_read_timeout |
| 413 Request Entity Too Large | client_max_body_size 太小(上传大文件常见) |
| 端口冲突起不来 | ss -tlnp | grep :80,常见是 apache 或另一个 nginx |
SELinux 是 502 的隐形元凶:nginx 连本地后端属于"网络连接",被 SELinux 默认策略阻止。
ausearch -m AVC -ts recent能看到。
| 软件 | 定位 | 适用 |
|---|---|---|
| dnsmasq | 极轻量,集成 DHCP + DNS + TFTP | 局域网、家庭路由、开发环境 |
| unbound | 轻量递归/缓存解析器 | 本地递归解析、隐私优先 |
| BIND9 | 全功能权威 + 递归 | 企业级、需要权威区文件 |
# /etc/dnsmasq.conf
interface=eth0
dhcp-range=192.168.1.100,192.168.1.200,12h
dhcp-option=3,192.168.1.1 # 网关
dhcp-option=6,192.168.1.1 # DNS
domain=lan
address=/app.lan/192.168.1.10 # 静态 DNS 记录
server=8.8.8.8 # 上游
cache-size=1000
log-queries # 排障时开动态 DNS 记录(读 /etc/hosts 风格的文件,改完发 HUP 即生效):
addn-hosts=/etc/dnsmasq-hosts$ sudo systemctl reload-or-restart dnsmasq
$ dig @192.168.1.1 app.lan # 验证
$ sudo journalctl -u dnsmasq -f# /etc/unbound/unbound.conf.d/local.conf
server:
interface: 127.0.0.1
access-control: 127.0.0.0/8 allow
do-daemonize: yes
hide-identity: yes
hide-version: yes
qname-minimisation: yes
# 转发模式(或完全递归)
forward-zone:
name: "."
forward-addr: 223.5.5.5
forward-addr: 1.1.1.1$ unbound-checkconf
$ sudo systemctl restart unbound
$ dig @127.0.0.1 example.com
$ sudo unbound-control stats_noreset | head2.4 客户端视角的 DNS(回到 07)
分清"服务端"和"客户端":本章讲的是怎么提供 DNS 服务。客户端怎么使用 DNS(
resolv.conf、nsswitch.conf、systemd-resolved)在 07 章。
时间不对会导致:TLS 证书校验失败、集群认证失败、日志顺序混乱、make 不重新编译、Kerberos 直接拒绝。
ntpd 已基本被 chrony 取代(对不稳定网络、休眠唤醒的机器表现更好)。
$ sudo apt install chrony # 或 dnf install chrony
$ systemctl status chronyd
$ chronyc tracking # 当前同步状态与偏差 ← 主要排障命令
$ chronyc sources -v # 时间源及状态(`^*` = 当前使用的源)
$ chronyc sourcestats # 统计
$ chronyc makestep # 立即跳变修正(默认缓慢调整)# /etc/chrony/chrony.conf
pool 2.debian.pool.ntp.org iburst
# 或用国内源
# server ntp.aliyun.com iburst
driftfile /var/lib/chrony/chrony.drift
makestep 1.0 3 # 前 3 次同步允许直接跳变
rtcsync
allow 192.168.1.0/24 # 允许局域网客户端同步(当作内网 NTP 服务器)| 方案 | 特点 |
|---|---|
systemd-timesyncd |
极简 SNTP 客户端,够用,默认在多数桌面系统 |
ntpd |
经典 NTP 实现,功能全但笨重 |
ntpdate |
一次性同步,已废弃(会跳变时间,可能损害服务) |
gpsd + chrony |
无外网时用 GPS 授时 |
$ timedatectl # 一站式看时间、时区、同步状态 ← 先看这个
$ timedatectl set-timezone Asia/Shanghai
$ timedatectl set-ntp true # 启用网络时间同步
$ date -R
$ sudo hwclock --show # 硬件时钟(RTC)
$ sudo hwclock --systohc # 系统时间写入硬件钟(UTC 建议)双系统时间错 8 小时的根因:Windows 默认把硬件钟当本地时间,Linux 默认当 UTC。解决:让 Linux 用本地时间
timedatectl set-local-rtc 1(不推荐),或让 Windows 用 UTC(改注册表,推荐)。
# chrony 服务端加 allow;客户端指向它
$ chronyc clients # 看谁在同步你注意:NTP 用 UDP 123。防火墙要放行,且要允许已建立连接的返回流量(
conntrack处理)。只放行入站 123 通常够。
CUPS(Common Unix Printing System)是 Linux/Unix 的事实标准打印架构,也支持 macOS。
应用 → CUPS(cupsd,端口 631)→ 驱动(filter/PPD/IPP Everywhere)→ 打印机
$ systemctl status cups
$ lpstat -p -d # 打印机列表与默认打印机
$ lpstat -t # 全部状态
$ lp -d printer file.pdf # 打印
$ lpoptions -d printer # 设默认
$ lpadmin -p HP -E -v ipp://192.168.1.20/ipp/print -m everywhere # 添加打印机
$ cancel -a printer # 取消所有任务
$ cupsctl --remote-admin # 允许远程管理(⚠️ 安全风险)Web 管理界面:http://localhost:631
| 时代 | 方式 |
|---|---|
| 老 | 厂商提供二进制的 PPD + filter 驱动 |
| 现在 | IPP Everywhere / driverless:打印机自己宣布能力,无需专有驱动 |
| 过渡 | gutenprint、foomatic(开源驱动库) |
$ lpinfo -v # 发现可用设备/后端
$ lpinfo -m | grep -i "everywhere" # 可用驱动模型
$ sudo systemctl restart cups$ sudo tail -f /var/log/cups/error_log # ← 主要日志
$ sudo cupsctl --debug-logging # 开详细日志
$ lpstat -t
$ sudo journalctl -u cups -n 50| 症状 | 原因 |
|---|---|
| 任务卡住不动 | 打印机离线、IP 变了、防火墙拦了 631/9100 |
| 无法发现网络打印机 | 未装 avahi(mDNS)、防火墙拦 5353 |
| 打印乱码 | 驱动/PPD 不匹配,改用 IPP Everywhere |
| 权限拒绝 | 用户在 lpadmin 组?lp/lpadmin 组配置 |
网络打印机的两个入口:CUPS(631)和打印机的直接 RAW 端口(9100)。转发打印给其他机器时常常是 9100,排查要分别测。
任何网络服务的排查都可以套这个模板:
# ① 进程在跑吗
$ systemctl status <service>
# ② 在监听吗(端口 + 监听地址)
$ ss -tulnp | grep <port>
# ③ 配置语法对吗
$ <service> -t / --check-config
# ④ 日志说什么
$ journalctl -u <service> -n 100 ; tail -f /var/log/<service>/*
# ⑤ 本机能连吗
$ curl -v http://127.0.0.1:<port>/
# ⑥ 防火墙/MAC 放行了吗
$ sudo nft list ruleset ; getenforce ; sudo aa-status
# ⑦ 权限与文件属主对吗
$ ls -l /var/log/<service>/ ; ps -o user= -C <service>跨主机访问不通的检查顺序(从近到远):
| 检查点 | 命令 |
|---|---|
监听地址是否 0.0.0.0/:: |
ss -tlnp |
| 本机防火墙 | iptables -L / nft list ruleset / ufw status |
| SELinux/AppArmor | getenforce / aa-status |
| 上游防火墙/安全组 | 云控制台 / 交换机 ACL |
| 中间链路 | mtr、tcpdump 两端同时抓包 |
| 应用层 | curl -v、看服务端 access log |
两端同时抓包的技巧:在客户端
tcpdump -i any host server,服务端tcpdump -i any host client。看包到没到,一眼分辨是"没送到"还是"送了没回"。这是网络排障最有效的手段。
nginx -t只是语法检查,配置逻辑错误它查不出来。reload不一定生效,有些改动(如换监听端口、改events)必须restart。- DNS 服务端和客户端是两回事,别混着调。
- 时间同步不是装完就准,要
chronyc tracking确认偏差收敛。 - CUPS 的 631 和打印机的 9100 是两个不同入口。
- 改完配置不重启服务 —— 排障时最常见的浪费。
- TLS 证书管理(Let's Encrypt / acme.sh / 自签 CA)
- nginx 高级:限流、缓存、gzip/brotli、WebSocket 代理
- HAProxy / Traefik / Caddy 对比
- BIND9 权威区文件与 DNSSEC 签名
- 邮件服务器(postfix + dovecot + SPF/DKIM/DMARC)
- 文件共享:Samba / NFS / WebDAV
- 反向代理后的真实客户端 IP(
X-Forwarded-For与real_ip模块) - 内网服务发现(mDNS/avahi、Consul、etcd)
- 打印服务的容器化(CUPS in Docker)