之前整理顺丰科技2019秋招运维工程师笔试客观题的时候,我本来只是想快速过一遍帮同事筛筛重点,结果越看越觉得有意思。那套题放在今天依然没有过时——LVS、Nginx、Keepalived、MySQL、Redis、Zabbix、Kubernetes同时出现在一张卷子里,正好踩在传统运维向云原生运维切换的交界点上。你会发现出题人不是单纯考你“会不会用某个命令”,而是考“在真实生产环境里,你能不能快速判断问题、定位根因、做出正确的技术选型”。
这套题适合三类人看:准备运维岗位笔试面试的应届生或转岗同学,想系统梳理自己知识边界的在职运维,以及那些“会用但不理解原理”的开发同学。我结合题目里反复出现的知识点,以及这些年自己踩过的坑,把考点拆开,聊聊为什么这么考、怎么答才能拿分、这些题放到生产环境里到底有什么用。
1. 顺丰那套笔试题的背后,藏着一张能力地图
1.1 运维工程师要学什么:一份“超纲”考察清单
很多人看到“客观题”三个字,第一反应是背概念。但顺丰这套题真正想考察的,是你在资源有限、时间有限的情况下,能不能用最短路径把一台机器、一个集群、一套业务系统稳住。所以你会发现它考察的维度很宽:操作系统、计算机网络、数据库、中间件、容器、监控告警、CI/CD、故障排查,甚至还有一部分架构设计和成本意识。
这些知识点串起来就是一张能力地图。我自己把它分成五层:最底层是Linux操作系统,包括进程、内存、磁盘、文件系统、内核参数、系统调用;第二层是网络,TCP/IP协议栈、HTTP、DNS、负载均衡、iptables;第三层是数据层,MySQL、Redis、消息队列;第四层是容器与编排,Docker、Kubernetes,以及最新的容器运行时;最上面一层是工程化能力,监控、日志、告警、CI/CD、容量规划、故障复盘。
为什么一张笔试卷要覆盖这么多面?因为运维岗位的特殊性在于,它是稳定性的第一责任人。开发可以只关心业务代码,但运维必须从内核到应用层都能兜住。哪怕你现在是做云原生方向的运维,天天跟Kubernetes和容器打交道,底层依然是Linux、网络、存储这些老知识。现在连具身智能应用运维这类新方向都开始出现,但追根溯源,还是要把这张基础地图画扎实。
1.2 笔试客观题考的不是记忆,是“推理链”
我最早看到这类笔试的题目时,以为背一背题库就能过,后来发现完全不是一回事。客观题它虽然是选择题,但很多题的解题过程比答案本身重要。举个例子,题目可能给你一段iptables规则,问某个源IP的流量最终会走到哪个链、被哪条规则处理。这种题你光记住“iptables有INPUT、OUTPUT、FORWARD”是不够的,你得理解规则匹配顺序、默认策略、连接跟踪状态,甚至要会推理某个包在不同状态下的走向。
再比如考TCP连接状态,题目给你一段netstat或ss的输出,问你“大量TIME_WAIT到底是不是故障”,这题考的不是定义,而是你是否清楚TIME_WAIT出现的时机、为什么主动关闭方会进入这个状态、什么场景下需要关注它、什么场景下它只是正常现象。这种“基于现象反推原理”的出题方式,本质上就是在模拟线上故障的真实状态——给你一堆监控数据和日志,你要能判断出哪里出了问题。
所以我建议所有准备笔试的人改变一个思路:不要把知识点当名词解释背,要把每个知识点变成一个推理链。拿到一道题,先问自己三个问题:这个现象是怎么产生的?它会导致什么后果?如果要解决,有哪些手段?能回答这三个问题,客观题基本就稳了。
2. 高频考点拆解:把每一类题吃透
2.1 Linux与网络:稳定性的地基
Linux这一块,笔试里出现频率最高的往往是这几类:系统负载相关命令(top、uptime、vmstat)的输出解读、free命令中buffer和cache的区别、df -h和df -i的区别、僵尸进程的产生与处理、文件描述符限制(ulimit -n)、软硬链接的区别、crontab时间格式、systemd服务管理,以及一些常见内核参数的含义。别看这些点简单,每一道都能挖出深坑。
比如uptime输出的load average有三个数,分别代表1分钟、5分钟、15分钟的平均负载。很多人背了“超过CPU核数就是负载过高”,但实际判断时要结合趋势:单看1分钟高可能是瞬时抖动,15分钟也高才是持续性问题。再比如buffer和cache,buffer是块设备的缓冲,cache是文件内容的缓存,free显示“available”的列才是真实可用的内存,不能简单用free -m看剩余内存来判断是否吃紧。
网络部分,TCP三次握手和四次挥手是必考的,但我建议大家把重点放在状态迁移上。半连接队列溢出会导致丢包,全连接队列溢出会导致accept失败,TIME_WAIT大量堆积会影响新连接建立。这些状态背后对应的是生产环境中的真实故障:接口超时、负载均衡后端不可用、连接数被打满。HTTP部分要分清502、503、504分别代表什么:502是网关从上游收到无效响应,504是网关等了太久没等到上游响应,很多人把这两个混淆,面试官一眼就能看出来你有没有真做过线上排查。
还有iptables,这也是题目里的常客。要搞清楚四个表五个链的关系,要知道DNAT和SNAT分别在什么场景用,要知道DROP和REJECT的区别——DROP让客户端一直等,REJECT直接拒绝会让客户端快速感知失败。具体什么时候用DROP什么时候用REJECT,取决于你希望外部看到什么表现,没有绝对正确答案。
2.2 数据库与中间件:业务数据的生命线
MySQL的考点集中在索引、事务、锁、主从复制。索引部分最核心的是搞清楚InnoDB为什么用B+树而不是B树或红黑树:B+树把所有数据都放在叶子节点,非叶子节点只存索引键值,树的高度更低、磁盘IO次数更少,而且叶子节点通过链表串联,范围查询非常高效。还有最左前缀原则,联合索引(a, b, c)能被哪些查询用到,是笔试选择题的经典送分题,也是实际建表时最容易踩坑的地方。
事务这块要理解ACID,以及MySQL InnoDB默认隔离级别是可重复读(REPEATABLE READ),通过MVCC解决普通读的幻读问题,通过间隙锁解决当前读的幻读问题。这里的区分很重要,很多人背了“可重复读不会出现幻读”,但实际是MVCC和Next-Key Lock共同作用的结果。
Redis的考点比较固定:五种基本数据类型和适用场景、为什么单线程还这么快、过期键删除策略、内存淘汰策略、RDB和AOF两种持久化的优缺点、缓存穿透、缓存击穿、缓存雪崩的区别和应对方案。这里最容易混的是缓存穿透、击穿和雪崩三个词,我的记忆方法是:穿透是“查了一个根本不存在的数据,打到DB”,击穿是“某一个热点key刚好过期,大量请求同时打到DB”,雪崩是“大面积key同时过期或Redis宕机,请求全量打到DB”。三种情况的应对方式也不同。
Nginx在题目里出现得也很多,重点看它的进程模型和负载均衡算法。Nginx是master-worker多进程模型,worker通过事件驱动处理连接,所以worker进程数通常设置为CPU核数。负载均衡算法中,轮询适合请求处理时间相对均衡的场景,least_conn适合短连接且请求耗时差异大的场景,ip_hash可以解决会话保持问题但要考虑用户IP变化导致的不均衡。还有location匹配优先级:精确匹配(=)优先级最高,然后是前缀匹配(^~)、正则匹配(~,按顺序),最后是普通前缀匹配。这个规则笔试直接考优先级排序,实际配置时也经常因为这个踩坑。
下面这个表是我整理的高频判断正误点,准备笔试时可以拿来自测。
| 说法 | 判断 | 说明 |
|---|---|---|
| Redis单线程,所以不能用多核 | 错误 | Redis是单线程处理命令,但可以部署多个实例利用多核,新版本也有多线程IO |
| MySQL只有用了索引就一定能加速查询 | 错误 | 索引区分度过低、查询回表过多、索引失效都会导致全表扫描更快 |
| TIME_WAIT状态一定说明系统有问题 | 错误 | 主动关闭方进入TIME_WAIT是正常机制,大量堆积才需要关注 |
| 容器里可以随便kill 1号进程 | 错误 | 1号进程是容器内init进程,kill后整个容器会退出 |
| Nginx worker_processes设置越大越好 | 错误 | 通常设为CPU核数,过大反而增加上下文切换开销 |
| Docker镜像层越多越好 | 错误 | 层多会增大镜像体积和拉取时间,尽量合并RUN命令 |
2.3 容器与Kubernetes:从资源管理到应用编排
容器相关的题目在当年的试卷里占比已经很重,放在今天更是核心中的核心。Docker部分主要考镜像分层机制、namespace和cgroup的作用。简单说,namespace负责让容器看到独立的进程、网络、文件系统视图,cgroup负责限制容器使用的CPU、内存等资源。两者配合实现了“隔离+限制”,这也是容器的一切基础。
Kubernetes部分要掌握它的核心组件和调度逻辑。kube-apiserver是集群的入口,所有请求都要经过它;etcd保存集群状态;controller-manager负责维持期望状态;scheduler负责把Pod调度到合适的节点;kubelet负责节点上的Pod生命周期管理;kube-proxy负责Service的流量转发。这里面有许多笔试选择题的素材:比如etcd用的是Raft协议保证一致性,kubelet通过watch机制感知Pod变化,Pod调度时会考虑节点资源、亲和性、污点和容忍度。
还有Pod的探针,livenessProbe和readinessProbe的区别也是高频题:liveness失败会重启容器,readiness失败会从Service后端摘掉。这个知识点在笔试里是送分题,但到了生产环境,很多人配置探针时搞反了,结果Pod一直在重启,业务流量的稳定性被自己搞崩了。
资源限制也是必考题。request和limit的区别必须说清楚:request是调度时的依据,limit是运行时的上限。CPU是可压缩资源,超限会被限流;内存是不可压缩资源,超限会触发OOM Kill。这里常考的一个场景是“为什么Pod一直在重启?”答内存limit设置过小触发了OOM Kill是很常见的根因。
2.4 监控、日志与故障排查:运维的“最后一道防线”
这套题里还有相当一部分监控和排查相关的内容。2019年的题目里出现Zabbix很符合当时的行业现状,但放到现在,Prometheus + Grafana已经成为主流。不过监控体系的设计思想是相通的:采集层、存储层、展示层、告警层要分开;告警要分级、要避免风暴;监控不只是看指标,还要有日志和链路追踪。
我当时刚接触监控体系时有个误区,以为装上监控工具、配几个告警规则就完事了。后来在大促准备期间发现,监控指标定得不好,告警要么天天炸、要么关键故障不报。指标设计应该围绕用户可感知的维度来定:错误率、响应时间、吞吐量、饱和度。也就是Google SRE书里讲的那四个黄金信号。这四个维度覆盖了“快不快、稳不稳、满不满、通不通”,比单纯盯着CPU和内存要实用得多。
日志方面,典型的采集链路是Filebeat采集日志推送到Kafka,Logstash或Fluentd做清洗转换,写入Elasticsearch,最后Kibana展示。笔试可能会考其中一个环节组件的职责,比如“Kafka在这里的作用是什么”,答案是缓冲和解耦,防止日志峰值直接打垮ES。
故障排查的思路也很重要。我的总结是“从全局到局部”:先看系统负载和整体状态,再定位到进程,然后看网络和日志,最后定位到代码或配置。千万别一开始就钻进日志里找答案,那样很容易被噪声干扰。这套思路不仅笔试有用,生产环境的每一次故障处理都靠它。
3. 必考题型与岗位实操的联动
3.1 Kubernetes如何调用containerd:一道“原理到实体”的综合题
最近不少人在问,当我们在Kubernetes里创建Pod时,kubelet到底是怎么调用containerd把容器跑起来的?这个问题如果出成笔试客观题,考得就是你对整个调用链的理解。结合当前Kubernetes默认使用containerd作为运行时的情况,这条链路的每一步都有“实体”对应。
当你执行kubectl run创建Pod时,请求先打到kube-apiserver,经过认证、鉴权、准入控制,把Pod对象写入etcd。kubelet通过watch机制从apiserver那里感知到本节点有新Pod需要创建,接下来就进入了容器运行时的环节。kubelet并不直接操作容器,它通过CRI(Container Runtime Interface,容器运行时接口)这个gRPC协议,把请求发给容器运行时。容器运行时可以是containerd、CRI-O,也可以是以前常用的Docker。
在containerd这一侧,它内置了一个CRI Plugin,专门负责把kubelet发过来的CRI请求转换成containerd自己的操作。containerd准备好镜像、创建好容器所需的元数据后,会为每个容器启动一个containerd-shim进程。shim的真正作用,是把容器进程和containerd主进程解耦——即使containerd主进程重启,已经跑起来的容器也不会受影响。然后containerd-shim调用runc,runc是真正干活的OCI运行时,它负责创建命名空间、配置cgroup、挂载文件系统,最后启动容器进程。
所以完整的调用链是:kubectl → kube-apiserver → etcd → kubelet → CRI请求 → containerd(CRI Plugin)→ containerd-shim → runc → 容器进程。
笔试里这个知识点可以出成排序题,比如“以下哪个是Pod创建流程的正确顺序”;也可以出概念匹配题,比如“CRI的作用是什么”。实操中排查问题时,可以用crictl命令查看容器状态,比如crictl ps、crictl logs、crictl inspect。如果kubelet报错说连不上运行时,多半是/run/containerd/containerd.sock这个socket文件有问题。当年我们排查过一个Pod一直Pending的问题,最后就是containerd版本和Kubernetes版本不兼容,CRI接口版本对不上,kubelet根本没法跟containerd通信。
3.2 从笔试设计题到生产实践:如何从零搭建一套系统
有些笔试除了客观题,还会给一道综合性设计题,主题往往就是“如何从零搭建一个系统并保证后续稳定”。这类题看着难,其实只要按生命周期拆解,思路就清晰了:需求评估、资源规划、系统初始化、部署架构、配置管理、监控告警、备份容灾、后续维护。
先说需求评估。你要先搞清楚要支撑的业务是什么类型:CPU密集型还是IO密集型?QPS大概多少?数据量多大?可用性目标是多少?这些数字直接决定你后续的选型。比如可用性目标99.9%,一年允许的不可用时间是8.77小时;如果是99.99%,就只允许52.6分钟。这个目标不同,高可用方案的成本天差地别。
然后是系统初始化。生产服务器的初始化一定要脚本化、标准化。我自己的做法是:先创建运维用户,配置SSH密钥登录并禁用密码登录,然后统一系统时区(用Asia/Shanghai)、配置chrony时间同步、调整内核参数(比如net.ipv4.tcp_fin_timeout、net.core.somaxconn、fs.file-max)、设置sysctl持久化。这里很多内容对应到笔试里的内核参数题,那时候只是背选项,真正上线时就知道每个参数背后都是一类连接问题。
接下来是部署架构。小型业务我推荐用高可用但不过度设计的方案:两台负载均衡(Nginx或云LB)做入口高可用,后端挂Kubernetes集群跑应用,数据库用MySQL主从,缓存用Redis主从加哨兵。如果用Kubernetes,应用通过Deployment部署,配置requests和limits,配置livenessProbe和readinessProbe,Service暴露内部访问,Ingress承接外部流量。发布策略用滚动更新,设置maxSurge和maxUnavailable,确保发布过程中服务不中断。
监控告警这块,Prometheus + Grafana + Alertmanager是云原生时代的标准组合。节点层用node-exporter,Kubernetes层用kube-state-metrics,业务指标由应用自己暴露。告警要分层,紧急告警立即通知,警告级告警进工单,避免所有问题都打电话导致告警疲劳。日志链路用Filebeat采集,Kafka缓冲,ES存储,Kibana展示,至少能查7-30天的日志。
最后别忘了备份和容灾。MySQL要定时全量备份加binlog增量,备份文件同步到异地对象存储;etcd是Kubernetes集群的大脑,必须定期做快照。备份不是目的,恢复才是,所以要定期演练还原流程。后续的日常维护就围绕着容量监控、版本升级、漏洞修复、大促前的压测和预案来做了。
4. 笔试中常见的坑与避雷指南
4.1 客观题本身的“题目陷阱”
刷过几套运维笔试题后你会发现,很多客观题是专门挖了坑的。最常见的陷阱是绝对化选项,比如“只要配置了keepalived就一定能保证高可用”“Docker容器隔离了所有资源所以绝对安全”。凡是出现“只要”“一定”“完全”“所有”这类绝对化表述的选项,大部分都要打个问号,因为生产环境没有绝对的事情。
第二个陷阱是“只改一个词”。比如选项把“连接跟踪状态”写成“连接跟踪包”,把“半连接队列”写成“全连接队列”,看起来很像,意思完全不一样。做题时一定要把每个选项的关键词圈出来,用笔画掉干扰信息。
第三个陷阱是概念混淆。比如把可重复读(REPEATABLE READ)和可串行化(SERIALIZABLE)当作同一个隔离级别;把iptables的PREROUTING和FORWARD链的功能互换。这种混淆题特别能区分“背过”和“理解过”。
判断技巧上,我有一个习惯:先把每个选项的结论反转,看它是否还成立。比如选项说“TCP主动断开方会进入TIME_WAIT”,我反转成“被动断开方会进入TIME_WAIT”,发现不对,就知道原选项在考主动和被动的区别。这个方法在做概念判断题时特别高效。
4.2 备考误区:别把真题当题库背
很多同学复习笔试时会陷入一个误区:把往年的真题当题库刷,记住答案就去考试了。实际上,哪怕是同一家公司的笔试,每年的考题都不会完全重复,但背后的知识点是稳定的。与其背答案,不如建一张知识树,把题目归类到“操作系统”“网络”“数据库”“容器”等分支下,每道错题标注出它考的是哪个节点,然后针对薄弱分支看一遍系统性的资料。
备考时还有一个更重要的动作:给每个知识点补上“生产场景”。比如你复习到“TCP三次握手”,不要只记“SYN、SYN+ACK、ACK”,想想这个机制在生产里有什么体现——半连接队列满了会发生什么?SYN Flood攻击是怎么回事?nginx配置里的backlog参数跟什么有关?你把这个知识点的来龙去脉都串起来,笔试时无论题目怎么变,你都能从原理出发推出来。
我见过一些同学把精力花在背“题库原题”上,结果笔试时碰到一个稍微换个场景的题就懵了。反而是那种每条命令都亲手敲过、每个原理都能讲给别人听的同学,不管题目怎么出都稳。运维这个岗位,说到底拼的是“理解”,不是“记忆”。
4.3 答题时的现场经验
笔试现场的时间分配也有讲究。客观题普遍是60道题60分钟或90分钟,平均一道题一分钟左右。我的策略是先快速浏览一遍整张卷子,把一眼就会的题先做掉,拿到基础分;然后回头啃需要计算的题,比如IP地址划分、掩码计算、负载均值趋势判断;最后再处理那些特别拿不准的题。
这里有个原则:不要在一道题上死磕超过3分钟。碰到没思路的题先标记,回头再看。做计算题时,即使题目只要答案,我也会在草稿纸上写出完整计算过程,避免因为某个数字算错导致连环失误。网络相关的题目,如果有画图的空间,把三次握手、数据包走向画出来,思路会清晰很多。
对于完全不会的题,不要空着。客观题空着肯定没分,蒙一个还有概率得分。蒙题的时候优先排除明显不合理的选项,比如跟题干无关的、绝对化的、和已知知识点矛盾的。我自己的经验是,答题时保持答题节奏比正确率更重要,如果前面卡太久,后面心态容易崩,很多原本会做的题也会做错。
5. 一个实战排查案例:当考题变成线上事故
5.1 事故现场:服务假死,负载告警
有一年线上一个小型业务集群突然告警,现象是应用响应变得特别慢,从正常的几十毫秒飙升到几秒,前端开始出现502错误。我看了一眼监控面板,系统CPU使用率并不高,但load average已经飙到CPU核数的好几倍,数据库的线程数也处于高位。第一反应是“系统卡死,但CPU不高”,这组合很反常,直觉告诉我问题大概率出在IO等待或者网络连接上。
这种场景在笔试里不会直接用运行中的系统来考,但考点覆盖了所有该准备的:TCP状态、MySQL连接数、系统负载、僵尸进程。当时我先把相关命令在脑子里过了两遍:先看top确认负载构成,再看IO状态,最后看网络连接和数据库进程。整个排查过程其实就是笔试那套知识点的实弹演练。
5.2 排查链路还原
先跑top,看到load average三个数字分别是12.0、8.5、4.0,说明压力是从大约15分钟前开始上升的。CPU的us和sy都不高,但wa列有接近30%,这说明系统确实在等IO。刚开始我以为是磁盘出了问题,但iostat显示磁盘利用率不高,那wait的来源是什么?我继续看内存,swap几乎没有使用,排除了内存换页导致IO的说法。
接下来看网络层,用ss -tan统计连接状态,结果发现TIME_WAIT状态的连接超过两万个,同时处于SYN_SENT和LAST_ACK状态的连接也在快速增长。数据库那头show processlist发现大量连接处于Sleep状态,连接数已经逼近max_connections上限。这时根因逐渐清晰了:某个应用实例的连接池配置超过了数据库承受能力,请求高峰期把数据库连接全部占满,新的请求排队等待连接,连带拖慢了整个业务链路。
处理过程:先把连接数紧急调大,重启问题应用让连接池重建,然后修改应用连接池参数,增加最大连接数并设置合理的连接超时;同时在操作系统层调整了net.ipv4.tcp_fin_timeout,加快TIME_WAIT的回收效率。这几步做完,系统负载很快回落到正常水位,502也消失了。这个案例里涉及的每个知识点,都能在那套笔试客观题里找到对应:TCP状态分析、MySQL连接管理、内核参数调优、进程状态判断。只不过笔试时是选择题,现场是实战题。
5.3 如果再来一次,我一定提前准备什么
经历了这次事故后,我的体会是:笔试里那些“死记硬背”的知识点,在关键时刻都是救命的。如果当时我能把TCP状态机的变化理解得更透彻,可能在看到TIME_WAIT暴涨的瞬间就反应过来了,不用绕那么大一圈。如果对MySQL连接池的处理机制更熟悉,可能更早想到是连接泄漏而不是简单调大参数。
准备笔试时,我强烈建议每个人用自己的话把每个知识点讲一遍,讲不通的地方就是没理解透的地方。比如“为什么TIME_WAIT需要维持2MSL”“为什么连接池会泄漏”“为什么内核参数不能照搬网上的推荐值”,这些问题如果能不看资料就回答出来,笔试就是走个过场。
我现在还有一个习惯,会把笔试合集中的题目按“知识点标签”重新整理,每个标签对应一段生产环境经验。比如“TCP状态”这个标签下面,记录的不只是TIME_WAIT和CLOSE_WAIT的机制,还有我在线上遇到过的具体故障现象、处理命令、踩坑教训。这样一来,这套笔试客观题就变成了一张活的知识索引,不管以后遇到什么类型的故障,都能在里面找到对应的排查方向。这也是我写这篇文章的初衷——把一套考试题,变成一套真正能落地、能沉淀、能复用的运维方法论。