Skip to content

本轮 服务器运维面试题库(带答案) ​

【面试题】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_size
  • recv() 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定期执行:

bash
#!/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 0

Keepalived配置中加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

告警规则设计要点:

  1. 用for持续时间避免毛刺误报:for: 3m表示连续3分钟超阈值才告警
  2. 分级告警:P0(电话+短信,服务不可用)、P1(短信+IM,核心指标异常)、P2(IM通知,需关注)
  3. 用表达式组合减少误报:CPU > 80% AND load5 > CPU核数比单看CPU更准确
  4. 加annotations说明:description: "实例 {{ $labels.instance }} CPU使用率 {{ $value }}%"

典型告警规则示例:

yaml
- 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线程池调优

yaml
server:
  tomcat:
    threads:
      max: 500          # 最大工作线程数,默认200
      min-spare: 20      # 最小空闲线程数
    max-connections: 10000  # 最大连接数,默认8192
    accept-count: 200    # 等待队列长度,默认100
    connection-timeout: 5000  # 连接超时ms

max-threads的值参考公式:CPU密集型 = CPU核数+1,IO密集型 = CPU核数 × 2 × (1 + 等待时间/计算时间)。一般Web应用IO多,200-500之间根据压测结果调。

二、JVM参数优化

bash
java -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -jar app.jar
  • Xms和Xmx设一样,避免堆内存动态扩缩的开销
  • 优先用G1收集器,停顿时间可控
  • MaxGCPauseMillis=200目标最大GC停顿200ms

三、连接器层面优化

yaml
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时说明要扩容了。