Skip to content

服务器运维面试题库 ​

生成时间:2026-05-24 22:23


【面试题】1. Nginx的location匹配规则优先级是怎样的?请详细说明并举例。 ​

【标准答案】Nginx的location匹配有四种类型,优先级从高到低依次是:

  1. 精确匹配 = /path:URI完全一致才命中,优先级最高,一旦匹配直接返回,不再继续。适合做精确路由,比如 = /health 做健康检查,一次匹配就结束,性能最优。

  2. 前缀匹配(最高优先) ^~ /path:匹配前缀后,如果后面没有更长的前缀匹配,就直接使用它,不会再去检查正则。适合已知不需要正则的静态路径,比如 ^~ /static/,避免正则匹配的性能开销。

  3. 正则匹配 ~ 区分大小写 / ~* 不区分大小写:按照配置文件中出现的顺序依次匹配,第一个命中的就使用。正则之间是"先到先得",所以正则的顺序很重要。

  4. 普通前缀匹配 /path:优先级最低。Nginx会记住最长前缀匹配,但还会继续检查是否有正则匹配。如果有正则命中,正则优先;如果没有,才用最长前缀。

记忆口诀:精确 > 前缀优先 > 正则 > 普通前缀。面试中重点强调两点:一是正则按书写顺序匹配,写在前面的优先;二是普通前缀虽然优先级低,但Nginx会选择最长的那个前缀。


【面试题】2. Linux系统中load average三个数值分别代表什么?什么情况下需要告警? ​

【标准答案】load average三个数分别是1分钟、5分钟、15分钟的系统平均负载,表示单位时间内处于可运行状态(R状态)和不可中断睡眠状态(D状态,通常是IO等待)的进程数。

理解关键点:

  • 这个值不是CPU使用率,而是排队等待运行的进程数量
  • 判断标准是和CPU核数对比。单核系统load=1表示刚好满载,4核系统load=4表示满载
  • 三个数的趋势反映系统状态:1分钟>5分钟>15分钟说明负载在上升,反过来说明负载在下降

告警阈值设定:

  • 一般经验:load超过核数的70%关注,超过核数告警,超过核数2倍严重告警
  • 比如4核机器:load>2.8关注,>4告警,>8严重
  • 还要看趋势:如果1分钟值远高于15分钟值,说明是突发负载,可能自行恢复;如果三者接近且持续高,说明是持续压力,需要介入

面试加分点:要区分CPU密集型高load和IO密集型高load。CPU密集型top看%us高,IO密集型top看%wa高,处理方式完全不同——CPU密集型要优化代码或扩核,IO密集型要优化磁盘或加缓存。


【面试题】3. 如何排查Nginx 502 Bad Gateway错误?请给出完整的排查思路。 ​

【标准答案】502说明Nginx作为反向代理时,无法从上游服务拿到有效响应。排查步骤:

第一步:确认上游服务是否存活

  • systemctl status php-fpm / systemctl status tomcat 看服务状态
  • netstat -tlnp | grep 端口 看端口是否在监听
  • 如果服务挂了,先看服务日志找崩溃原因再重启

第二步:检查Nginx到上游的连通性

  • curl http://127.0.0.1:上游端口/ 在Nginx机器上直接请求上游
  • 检查upstream配置的地址和端口是否正确
  • 如果上游在另一台机器,检查防火墙和网络连通性

第三步:检查上游服务是否过载

  • 上游进程数是否打满:php-fpm的pm.max_children、tomcat的线程池
  • 看上游日志是否有超时或拒绝连接的记录
  • ss -s 看连接数是否异常

第四步:检查Nginx配置

  • proxy_read_timeout、proxy_connect_timeout是否设置过小
  • upstream的max_fails和fail_timeout是否把上游标记为down了
  • proxy_buffer_size是否不够导致响应头过大

第五步:查Nginx error.log

  • tail -f /var/log/nginx/error.log 看502对应的具体错误信息
  • connect() failed (111: Connection refused) → 上游没启动
  • connect() failed (110: Connection timed out) → 上游无响应
  • upstream sent too big header → buffer太小

面试重点:不要一上来就重启,要先定位根因。502的本质是Nginx连不上上游或上游返回了无效响应,沿着这条线排查就不会走偏。


【面试题】4. Keepalived+Nginx高可用方案的工作原理是什么?脑裂问题如何解决? ​

【标准答案】工作原理: Keepalived基于VRRP协议实现VIP(虚拟IP)漂移。两台Nginx服务器分别作为MASTER和BACKUP,Keepalived会持续发送VRRP广播包(组播224.0.0.18)。MASTER优先级高的持有VIP,BACKUP监听广播。当MASTER宕机,BACKUP收不到广播,在约定时间后接管VIP,实现自动故障转移。

关键配置点:

  • vrrp_instance中state MASTER/BACKUP、priority值决定角色
  • virtual_ipaddress配置VIP
  • track_script可以配置健康检查脚本,检测Nginx进程是否存活,如果Nginx挂了就主动降低优先级让VIP漂走

脑裂问题: 脑裂是指MASTER和BACKUP都认为自己持有VIP,同时写入流量。原因是两者之间网络不通但各自都还活着。

解决方案:

  1. 冗余心跳线:除了主用VRRP组播通道,增加一条独立的交叉网线做备用心跳
  2. 仲裁机制:引入第三方节点或网关做仲裁,两节点抢VIP时由仲裁决定归属
  3. 检测脚本:在notify_master回调中检查对方是否真的挂了,如果对方还活着就放弃接管
  4. 防火墙规则:确保VRRP协议(协议号112)不被防火墙拦截,这是最常见的脑裂原因
  5. 日志监控:监控Keepalived状态切换日志,脑裂发生时及时告警人工介入

面试加分:实际生产中,最简单有效的防脑裂手段是确保VRRP组播包不被阻断,同时用track_script检测Nginx进程状态。如果条件允许,用双VIP互为主备(A是VIP1的MASTER、VIP2的BACKUP,B反过来),既防脑裂又充分利用资源。


【面试题】5. iptables和firewalld有什么区别?生产环境推荐用哪个? ​

【标准答案】核心区别:

  • iptables是传统的Linux防火墙工具,直接操作内核netfilter规则链,规则按顺序匹配,规则多时性能线性下降
  • firewalld是CentOS7+的默认防火墙管理工具,底层仍然可以调用iptables/nftables,但引入了"zone"和"service"的抽象层,规则管理更灵活

关键差异:

  1. 规则组织方式:iptables是扁平的规则列表;firewalld用zone(trusted/public/dmz等)分组,不同网络接口可以绑不同zone
  2. 规则生效方式:iptables每条规则立即生效;firewalld修改后需要reload才生效(也可以--permanent即时生效),支持运行时规则和永久规则分离
  3. 性能:iptables规则多时遍历慢;firewalld底层用nftables时性能更好(批量处理)
  4. 易用性:firewalld用service和zone封装,开HTTP就是firewall-cmd --add-service=http,不用记端口号

生产建议:

  • CentOS7+推荐firewalld,管理方便,zone隔离更安全
  • 如果规则量非常大(上千条)或需要精细控制包处理流程,用iptables/nftables更合适
  • 两者不要混用,会冲突。选一个用到底
  • 不管用哪个,核心原则:默认拒绝,只开必要端口,限制来源IP

面试强调:不要陷入"哪个更好"的争论,重点是理解底层都是netfilter/nftables,选型看团队习惯和运维复杂度。能用firewalld解决的就别手写iptables规则。


【面试题】6. Tomcat的Connector三种线程模型(BIO/NIO/APR)有什么区别?生产环境怎么选? ​

【标准答案】三种模型对比:

  1. BIO(Blocking IO):Tomcat 8.5之前默认。每个请求占用一个线程,线程在读写时阻塞。并发一高线程数暴增,上下文切换开销大。已淘汰,不推荐。

  2. NIO(Non-blocking IO):Tomcat 8.5+默认。使用Java NIO,一个线程通过Selector管理多个连接,读写不阻塞。用少量线程处理大量连接,适合高并发长连接场景。配置protocol="org.apache.coyote.http11.Http11NioProtocol"。

  3. APR(Apache Portable Runtime):通过JNI调用本地C库实现,本质是使用操作系统的epoll。性能最好,但需要安装APR库和tomcat-native。配置protocol="org.apache.coyote.http11.Http11AprProtocol"。

生产选型:

  • 绝大多数场景用NIO就够了,开箱即用,性能已经很好
  • 对性能极致要求(单机万级并发以上)且运维能力强时用APR,但要承担额外依赖的运维成本
  • BIO绝对不要用

NIO模式调优关键参数:

  • maxThreads="500":最大工作线程数,不是越大越好,一般200-800,CPU密集型少设,IO密集型多设
  • minSpareThreads="50":最小空闲线程,避免突发流量时现创建线程
  • acceptCount="200":连接队列长度,线程满时连接排队等待
  • connectionTimeout="20000":连接超时,防止慢连接占资源

面试要点:选NIO默认即可,重点会调参数。APR性能虽好但增加了部署复杂度,除非有明确的性能瓶颈证据,否则不值得引入。


【面试题】7. Linux系统中如何排查内存泄漏?请给出完整的排查步骤。 ​

【标准答案】第一步:确认是否存在内存泄漏

  • free -h 看内存使用趋势,关注available值是否持续下降
  • top 按M排序,看哪个进程内存持续增长
  • vmstat 1 看si/so是否有持续swap,内存不足时系统会swap

第二步:定位泄漏进程

  • ps aux --sort=-%mem | head -20 找内存占用最高的进程
  • top -p PID 持续观察特定进程的RES(物理内存)和VIRT(虚拟内存)
  • 如果是Java应用:jstat -gcutil PID 1000 看各代内存变化,Old区持续增长回收不掉就是泄漏

第三步:分析泄漏原因

  • Java应用:jmap -dump:format=b,file=heap.hprof PID 导出堆dump,用MAT或VisualVM分析,找占用最大的对象和GC Roots引用链
  • C/C++应用:用valgrind valgrind --leak-check=full ./program 直接检测泄漏点
  • Go应用:pprof 工具,go tool pprof http://localhost:6060/debug/pprof/heap
  • 通用方法:pmap -x PID 看进程内存映射,/proc/PID/smaps 看详细内存段

第四步:临时止损

  • 如果内存快打满了,先systemctl restart 服务名重启恢复
  • 设置cron定期检查内存,超阈值自动重启(应急方案)
  • 调大swap作为缓冲(治标不治本)

第五步:根因修复

  • 代码层面:未关闭的资源(连接/流)、集合对象只增不删、ThreadLocal未清理、缓存无过期策略
  • 配置层面:JVM堆是否太小、GC策略是否合适

面试关键:排查内存泄漏的核心是"持续监控+对比趋势",不是看一个时间点的快照。要先确认趋势,再定位进程,再分析代码,不要一上来就重启了事。


【面试题】8. Nginx限流配置怎么做?limit_req和limit_conn的区别和使用场景是什么? ​

【标准答案】limit_req——限制请求速率(限频) 基于令牌桶算法,控制单位时间内的请求数。

nginx
# http块中定义限流规则
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

# server或location中应用
location /api/ {
    limit_req zone=api_limit burst=20 nodelay;
    limit_req_status 429;
}
  • rate=10r/s:每个IP每秒最多10个请求
  • burst=20:允许突发20个请求排队
  • nodelay:突发请求不延迟处理,超出的直接拒绝
  • 不加nodelay:突发请求会排队延迟处理,不会直接拒绝

limit_conn——限制并发连接数 控制同一时刻的并发连接数。

nginx
# http块中定义
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

# location中应用
location /download/ {
    limit_conn zone=conn_limit 5;
    limit_conn_status 503;
}

表示每个IP最多5个并发连接。$binary_remote_addr比$remote_addr更省内存(IPv4只占4字节)。

区别和使用场景:

  • limit_req:防高频刷接口、防爬虫、API速率限制。关注的是"多快"
  • limit_conn:防并发下载、防连接耗尽。关注的是"多少个"
  • 两者可以同时用,互不冲突

面试加分点:

  • 限流维度可以换:$binary_remote_addr按IP限,$server_name按站点限,自定义变量按API Key限
  • 限流后的处理:返回429/503,或配置error_page 429 /429.html给友好提示
  • 分布式环境下单机限流不够,需要Redis+Lua或网关层统一限流

【面试题】9. Prometheus+Grafana监控体系中,如何设计一个完整的监控告警方案? ​

【标准答案】监控指标设计——USE方法(Utilization/Saturation/Errors):

  1. 主机层:CPU使用率、load average、内存使用率、磁盘使用率、磁盘IO使用率和等待、网络带宽、文件描述符使用数
  2. 应用层:QPS、响应时间P50/P95/P99、错误率、活跃连接数、线程池使用率、JVM堆内存和GC
  3. 中间件层:MySQL连接数/慢查询/QPS、Redis命中率和内存、MQ消息堆积量、Nginx请求量和状态码分布
  4. 业务层:订单量、支付成功率、核心接口耗时(根据业务定制)

告警分级:

  • P0致命:服务不可用、核心功能故障 → 电话+短信,5分钟响应
  • P1严重:性能严重下降、资源即将耗尽 → 短信+IM,15分钟响应
  • P2警告:指标异常但未影响服务 → IM通知,工作时间处理
  • P3提示:常规信息,仅记录

告警规则设计原则:

  • 告警要有价值,宁缺毋滥。一条告警必须对应一个明确的处理动作
  • 用for持续时间避免毛刺误报:for: 5m表示持续5分钟才告警
  • 多条件组合减少误报:CPU>80% AND load>核数*2 才告警,比单条件更准
  • 告警聚合:同一服务的相关告警合并,避免风暴

Prometheus关键配置:

yaml
# alertmanager配置告警路由和抑制
route:
  group_by: ['alertname', 'cluster']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'ops-team'

Grafana看板设计:

  • 总览大屏:全红绿状态,一眼看系统健康度
  • 主机详情:单机所有指标
  • 应用详情:单个服务的调用链和性能
  • 告警历史:过去24h告警趋势

面试要点:监控不是堆指标,是设计一套"发现问题-定位问题-解决问题"的闭环。告警太多=没有告警,控制告警质量比覆盖面更重要。


【面试题】10. Docker容器资源限制怎么做?一个容器把宿主机资源吃光了怎么处理? ​

【标准答案】Docker资源限制参数:

  1. CPU限制:
  • --cpus=2:限制最多使用2个CPU核心
  • --cpuset-cpus=0,2:绑定到第0和第2个CPU核心
  • --cpu-shares=512:CPU权重(相对值,只在争抢时生效)
  1. 内存限制:
  • -m 4g 或 --memory=4g:限制最大内存4G
  • --memory-swap=8g:限制内存+swap总共8G
  • --memory-reservation=2g:软限制,内存紧张时才生效
  1. IO限制:
  • --device-read-bps /dev/sda:10mb:读速度限制10MB/s
  • --device-write-iops /dev/sda:100:写IOPS限制100

容器吃光宿主机资源的处理:

紧急处理:

  • docker stats 找到异常容器
  • docker update --memory=2g --cpus=1 容器ID 在线调整资源限制(不用重启)
  • 如果容器已经卡死:docker kill 容器ID 强制停止

根因分析:

  • docker logs 容器ID 看日志,是否应用异常导致资源暴涨
  • docker exec 容器ID top 进入容器看进程,定位具体哪个进程
  • 如果是Java应用,检查是否JVM堆设置过大(-Xmx超过了容器内存限制)

预防措施:

  1. 所有容器必须设置资源限制:这是铁律。没有资源限制的容器就是定时炸弹
  2. K8s中用requests和limits:
    yaml
    resources:
      requests:  # 保底资源
        memory: "512Mi"
        cpu: "500m"
      limits:    # 上限资源
        memory: "2Gi"
        cpu: "2000m"
  3. 设置OOM优先级:--oom-score-adj值越高越先被杀,核心服务设低值
  4. 监控告警:容器CPU/内存使用率超过80%就告警

面试关键:强调"预防>处理"。每个容器上线时就必须配好资源限制,这是运维底线。出问题后再加限制是被动的,而且docker update虽然能在线调整,但部分参数(如swap限制)修改后需要重建容器。