本轮 服务器运维面试题库(带答案)
【面试题】1. Nginx的proxy_pass后面带不带斜杠有什么区别?实际配置中容易踩什么坑?
【标准答案】这是非常容易踩坑的地方。核心区别在于URI的拼接规则:
带斜杠(proxy_pass http://backend/;):属于带URI的proxy_pass,Nginx会把location匹配到的部分替换掉,只把剩余路径拼到proxy_pass后面。比如 location /api/ 匹配到 /api/user/list,实际转发给后端的是 /user/list,/api/ 被剥离了。
不带斜杠(proxy_pass http://backend;):属于不带URI的proxy_pass,请求路径原样传递,/api/user/list 转发过去还是 /api/user/list。
踩坑场景:前后端分离项目,前端请求 /api/getData,后端实际接口是 /getData。如果proxy_pass不带斜杠,后端收到的是 /api/getData,接口404;带斜杠才能正确转发为 /getData。
面试加分点:可以说清楚"带URI"和"不带URI"的判断标准——proxy_pass后面只要跟了路径(哪怕只是一个/),就算带URI,都会触发location替换逻辑。
【面试题】2. Tomcat的NIO、APR、NIO2三种Connector线程模型分别是什么?生产环境怎么选?
【标准答案】三种模型的核心区别:
BIO(已废弃):一个请求一个线程,高并发下线程数爆炸,Tomcat 8.5后已移除。
NIO:基于Java NIO,用少量的Acceptor线程接受连接,Poller线程轮询事件,Worker线程处理业务。默认模型,通用性最好,JDK即可运行。
APR:调用Apache Portable Runtime本地库,底层用C实现的epoll/sendfile等,静态文件处理性能最优,但需要安装APR native库(libtcnative),部署复杂度高。
NIO2:基于JDK 7+的AIO(异步IO),理论上异步模型更高效,但Linux下AIO底层仍然是epoll模拟,实际性能提升有限。
生产环境选择:大多数场景直接用NIO就够了,性能和兼容性平衡最好。如果项目有大量静态资源且服务器是Linux,可以考虑APR,但要评估运维成本——每个节点都要装native库,升级JDK或Tomcat时还要重新编译。NIO2一般不推荐,实际收益不大。
面试加分:可以说出配置方式,<Connector protocol="org.apache.coyote.http11.Http11NioProtocol" />,以及通过maxThreads、acceptCount等参数调优。
【面试题】3. Linux服务器上load average这三个数字分别代表什么?什么值算异常?
【标准答案】uptime或top看到的load average是三个数字,分别代表过去1分钟、5分钟、15分钟的系统平均负载。系统负载 = 处于running状态 + 处于uninterruptible sleep状态(通常是等磁盘IO)的进程数。
判断标准看CPU核数:
- 单核:load 1.0表示满载,超过1.0开始排队,>2算严重过载
- 4核:load 4.0满载,>4开始排队
- N核:load N满载
三个数字的对比关系很重要:
- 1min > 5min > 15min:负载在上升,可能有突发流量
- 1min < 5min < 15min:负载在下降,高峰可能已过
- 三个数接近:负载稳定
实际排查思路:load高但CPU使用率不高 → 大概率是磁盘IO瓶颈(D状态进程多);load高且CPU也高 → CPU密集型任务或进程数过多。用vmstat看b列(等待IO的进程数)和r列(运行队列)来确认。
【面试题】4. 如何排查Nginx 502 Bad Gateway错误?完整的排查思路是什么?
【标准答案】502表示Nginx作为反向代理时,无法从上游服务器(如PHP-FPM、Tomcat)获得有效响应。排查思路:
第一步:查上游服务是否存活systemctl status php-fpm 或 netstat -tlnp | grep 9000,确认后端进程在跑、端口在监听。
第二步:查Nginx error日志tail -f /var/log/nginx/error.log,关键信息:
Connection refused→ 后端没启动或端口不对Connection timed out→ 后端处理太慢,超时了upstream sent too big header→ 响应头太大,需调proxy_buffer_size和fastcgi_buffer_sizerecv() failed (104: Connection reset by peer)→ 后端进程崩溃
第三步:查上游服务日志 看PHP-FPM慢日志、Tomcat catalina.out,是否有OOM、段错误等。
第四步:检查资源限制ulimit -n查文件描述符限制,php-fpm.conf中pm.max_children是否够用,如果所有worker都忙,新请求直接502。
第五步:检查网络连通性curl http://127.0.0.1:9000从Nginx服务器上直接访问上游,确认网络没问题。
常见修复:
- 调大
pm.max_children和pm.max_spare_servers - 增加
proxy_read_timeout和proxy_connect_timeout - 调大
fastcgi_buffer_size到8k或16k
【面试题】5. Keepalived + Nginx高可用方案中,如果Nginx进程挂了但Keepalived还在,VIP不会漂移怎么办?
【标准答案】这是经典的生产事故场景。默认情况下Keepalived只检查自身进程存活,不管Nginx死没死。Nginx挂了但Keepalived还在,VIP不会漂移,请求打到死掉的Nginx上,服务就挂了。
解决方案:写一个Nginx健康检查脚本,让Keepalived定期执行:
#!/bin/bash
if [ $(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1/) != "200" ]; then
systemctl stop nginx
exit 1
fi
exit 0Keepalived配置中加vrrp_script和track_script:
vrrp_script chk_nginx {
script "/etc/keepalived/check_nginx.sh"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
track_script {
chk_nginx
}
}关键参数:weight -20表示检查失败时优先级减20,fall 3连续失败3次才触发,rise 2连续成功2次才恢复,避免抖动。
还有一招双保险:脚本里检测到Nginx挂了先尝试重启,重启失败再退出让VIP漂移。
【面试题】6. Docker容器的资源限制怎么做?如果不限制会有什么后果?
【标准答案】Docker通过Linux Cgroups实现资源限制,核心参数:
内存限制:
-m 512m或--memory=512m:限制最大内存--memory-swap=1g:限制内存+Swap总量--memory-reservation=256m:软限制,内存紧张时才回收
CPU限制:
--cpus=1.5:限制使用1.5个CPU核心--cpuset-cpus=0,2:绑定到第0和第2号CPU核心--cpu-shares=512:相对权重,默认1024,只在CPU争抢时生效
IO限制:
--device-read-bps /dev/sda:10mb:读速率限制--device-write-iops /dev/sda:100:写IOPS限制
不限制的后果:一个容器跑疯(比如内存泄漏、死循环),会吃光宿主机资源,导致其他容器和宿主机服务全部受影响,甚至触发OOM Killer随机杀进程。生产环境见过最典型的就是跑批处理任务的容器把内存吃满,宿主机上数据库被OOM Killer干掉。
面试加分:提到docker stats实时监控,以及Kubernetes中对应的是resources.limits和resources.requests,requests保证最低资源,limits保证最高上限。
【面试题】7. iptables和firewalld是什么关系?生产服务器防火墙用哪个好?
【标准答案】两者不是替代关系,firewalld是iptables的前端管理工具:
iptables:直接操作内核netfilter的规则链,底层实现。规则是线性的,从上到下匹配,规则多了性能下降。修改规则要整个链重载。
firewalld:基于iptables(或nftables)的上层封装,引入了zone(区域)和service(服务)的概念。支持运行时修改规则立即生效且不影响已有连接,底层用iptables-save/restore增量更新。
关系:firewalld最终还是调用iptables(或nftables)执行规则,可以理解为firewalld = iptables的"高级管理界面"。
生产选择:
- CentOS 7+ 默认firewalld,语法更清晰,
firewall-cmd --add-service=http --permanent比 iptables 写一串规则直观得多 - 老系统或Docker/K8s环境推荐iptables,因为Docker/K8s自己会生成大量iptables规则,和firewalld可能冲突
- Ubuntu默认用ufw,也是iptables的前端
实际建议:如果服务器上跑Docker或K8s,用iptables更可控;纯Web服务器用firewalld更方便。别同时开两个,会冲突。
【面试题】8. LVS的NAT、DR、TUN三种模式原理和区别是什么?各自适用什么场景?
【标准答案】LVS(Linux Virtual Server)三种转发模式:
NAT模式:
- 原理:客户端请求到VIP,Director修改目标IP为RealServer IP,转发给后端;后端响应回Director,Director再把源IP改回VIP返回客户端
- 特点:请求和响应都过Director,Director容易成为瓶颈
- 优点:RealServer可以是任何OS,不需要特殊配置
- 缺点:Director性能瓶颈,一般能撑10-20台RealServer
- 场景:后端服务器数量少、流量不大的场景
DR模式(Direct Routing):
- 原理:Director只修改MAC地址(二层数据链路层转发),请求直接到RealServer;RealServer响应直接回客户端,不走Director
- 特点:只有请求过Director,响应直连客户端,性能最高
- 优点:Director不是瓶颈,可挂上百台RealServer
- 缺点:Director和RealServer必须在同一网段(二层可达),RealServer需要配置VIP在lo接口上且抑制ARP响应
- 场景:大规模集群,绝大多数生产环境的首选
TUN模式(IP Tunneling):
- 原理:Director把原始请求封装在IP隧道里发给RealServer,RealServer解封装处理后直接回客户端
- 特点:请求走隧道,响应直连
- 优点:RealServer可以跨网段,甚至跨机房
- 缺点:需要所有RealServer支持IP隧道协议,配置复杂
- 场景:跨机房、跨地域的负载均衡
生产环境90%用DR模式,性能好配置也不算复杂。NAT模式适合小规模或快速验证,TUN模式基本只在跨机房场景用到。
【面试题】9. Prometheus + Grafana监控体系中,如何设计一个合理的告警规则?有哪些关键指标?
【标准答案】好的告警设计核心原则:精准告警、减少误报、分级处理。
关键监控指标分层:
基础设施层:CPU使用率 > 80%、内存使用率 > 85%、磁盘使用率 > 90%、磁盘IO延迟 > 50ms、网络丢包率 > 0.1%
应用层:HTTP 5xx率 > 1%、P99响应时间 > 500ms、QPS突降 > 30%、连接池使用率 > 80%
中间件层:MySQL慢查询 > 10/min、Redis内存 > 80%、MQ消息积压 > 10000
告警规则设计要点:
- 用
for持续时间避免毛刺误报:for: 3m表示连续3分钟超阈值才告警 - 分级告警:P0(电话+短信,服务不可用)、P1(短信+IM,核心指标异常)、P2(IM通知,需关注)
- 用表达式组合减少误报:
CPU > 80% AND load5 > CPU核数比单看CPU更准确 - 加annotations说明:
description: "实例 {{ $labels.instance }} CPU使用率 {{ $value }}%"
典型告警规则示例:
- alert: HighCPU
expr: 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 3m
labels:
severity: P1
annotations:
summary: "CPU使用率过高"
description: "{{ $labels.instance }} CPU {{ $value }}%"面试加分:提到告警疲劳问题——如果天天收到无关告警,人就会麻木,真正故障反而忽略。要定期review告警规则,删掉无效告警,调整阈值。
【面试题】10. 如何对Spring Boot内嵌Tomcat进行性能调优?关键参数有哪些?
【标准答案】Spring Boot内嵌Tomcat调优分三个层面:
一、application.yml中Tomcat线程池调优
server:
tomcat:
threads:
max: 500 # 最大工作线程数,默认200
min-spare: 20 # 最小空闲线程数
max-connections: 10000 # 最大连接数,默认8192
accept-count: 200 # 等待队列长度,默认100
connection-timeout: 5000 # 连接超时msmax-threads的值参考公式:CPU密集型 = CPU核数+1,IO密集型 = CPU核数 × 2 × (1 + 等待时间/计算时间)。一般Web应用IO多,200-500之间根据压测结果调。
二、JVM参数优化
java -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -jar app.jar- Xms和Xmx设一样,避免堆内存动态扩缩的开销
- 优先用G1收集器,停顿时间可控
MaxGCPauseMillis=200目标最大GC停顿200ms
三、连接器层面优化
server:
tomcat:
max-http-header-size: 8KB # 请求头大小限制
max-swallow-size: 2MB # 请求体最大吞入量实战经验:
- 如果接口响应快(<100ms),
max-threads可以适当开大 - 如果有慢接口,线程数开再大也没用,等待队列会爆,要从业务层优化
accept-count设太大会让用户等更久才报错,不如适当设小让快速失败- 生产必加:
server.tomcat.accesslog.enabled=true,出问题能查到请求日志
面试加分:提到用/actuator/metrics端点监控Tomcat线程池状态,tomcat.threads.busy接近tomcat.threads.max时说明要扩容了。