搜狐畅游运维开发笔试复盘:从Shell脚本到故障排查与监控设计

搜狐畅游运维开发笔试复盘:从Shell脚本到故障排查与监控设计 1. 2019年这套笔试题的科目构成与难度风向先交代一下背景。搜狐畅游的校招运维开发工程师岗笔试并不是只考Linux命令和网络基础它比传统的纯运维卷子多了一个非常明显的信号——运维开发的开发二字不是白给的。我那一年拿到的卷子整体时间90分钟题型分布大概是选择题/填空题占40%编程题占20%故障场景排查题占20%架构设计/方案题占20%。这个比例放在今天看可能不算稀奇但在2019年很多公司招运维还在拿会敲命令、会装系统、会配Nginx当标准畅游已经明显在往懂开发的运维方向筛人了。从难度上看这套题属于典型的看着都会动手就卡型。选择题和填空题是真实的Linux、网络基础题不算偏但有几个选项是平时不真正折腾过服务器的人根本分辨不出来的。编程题也不难Shell和Python二选一或者都要写但它在题目里埋了一个很容易忽略的效率坑。场景排查题和架构设计题是拉开差距的地方——不背八股只考你平时有没有真的在线上处理过问题。所以我的建议是如果你是冲着运维工程师去投递先把这套题的岗位方向和传统运维做个切割。传统运维的笔试题重心放在LNMP环境搭建、iptables规则、Nginx配置、ansible部署这些执行层的东西上而畅游的卷子更像是在考察遇到问题你怎么定位、怎么通过代码或脚本去自动化解决。Linux基础和网络是底层但真正决定你能不能进面试的是后一半——脚本设计、故障排查思路、模块化意识。2. 选择题里的高频暗坑文件权限、进程状态、网络命令的细节2.1 文件权限与umask的计算题这些年几乎所有大厂的运维笔试题都会带一两道权限计算畅游也不例外。题目大概是这样的当前umask是027创建一个新文件后它的默认权限是多少很多人背过结论——文件是666减去umask目录是777减去umask——但一碰到umask有奇数位就会出错。重点拆一下这个坑umask 027对应的是owner、group、other三部分。新文件的默认权限规则是666 ~umask不是直接做减法。把027转成二进制000020107111所以umask的二进制是000 010 111取反后是111 101 000再和110 110 110666做按位与得到110 100 000也就是640。如果直接按666-027639来算group少了一个读权限other多了一个执行权限两个地方全错。目录则可以直接用777 - 027 750因为umask全是偶数的情况下减法和按位与结果一样。记住这个文件用与运算目录用减法的规律这种题就稳了。另外补充一个容易被忽略的点umask只有在创建文件时才生效chmod或者chown之后是不受umask控制的。还有ACL权限存在时用ls -l看到的权限最后会多一个加号这道题如果问该文件是否还能被other组读取你得先区分传统权限和ACL否则就会错判。2.2 进程状态里那些看起来一样的字母有一道选择题给了几个进程状态码——R、S、D、Z、T、I——问哪个状态表示进程正在等待不可中断的I/O。答案是D状态不可中断睡眠。这个题本身不难但它延伸出来的东西很有价值D状态进程在top里频繁出现时意味着系统磁盘或网络I/O有瓶颈这种进程kill不掉只能等I/O恢复或者重启系统。还有一个细节2019年的卷子里有一道题问僵尸进程为什么不能被kill -9杀掉我猜很多考生看到僵尸两个字就选了因为它已经死了所以杀不掉但严格说僵尸进程本身已经结束它只是在等待父进程调用wait()来回收资源。kill -9对僵尸进程无效是因为它已经没有内核执行体可以接收信号了。正确做法是kill父进程让init/systemd去收养并清理或者检查父进程为什么不调用wait。顺带说一个日常排查经验如果你在线上服务器看到大量Z状态进程大概率是父进程代码有缺陷——没有正确回收子进程。这在Python的multiprocessing、Shell里并发跑子命令、Java的Runtime.exec()场景里都很常见。笔试只考状态含义但面试官大概率会追问一句你线上遇到过吗怎么处理提前准备一个真实案例会加分。2.3 网络排查命令netstat、ss、tcpdump、lsof的区分这组选择题出现的频率非常高基本套路是给一个场景让你选命令。例如线上服务端口监听了但连接数异常高怎么快速看到每个IP的连接数首选ss -antp | awk {print $5} | sort | uniq -c | sort -rn而不是netstat。原因是ss直接读内核socket信息不依赖/proc/net/tcp的解析连接量大的时候性能远好于netstat。如果题目问的是查看某个进程打开了哪些网络连接答案往往是lsof -p -i或lsof -i :端口。这里有个2019年普遍存在的误区很多教程还在推荐netstat -ano但新版本的CentOS/RHEL已经默认不装net-tools了生产环境里ss才是原配。笔试如果考到这个知识点本质不是考哪个命令更高级而是考你对时代变迁的感知——旧命令、新命令、什么时候用哪个心里有没有数。tcpdump考得不多但会有一道选项题问要抓访问80端口的所有HTTP流量表达式怎么写。标准答案是tcpdump -i eth0 -nn tcp port 80注意这里不能写http这个关键词因为tcpdump的http过滤器在部分版本上并不存在或者匹配的是payload层内容。想抓HTTP的Host头或者URL得用-A或-X看包内容或者加-w落盘后再分析。这背后的逻辑是tcpdump是抓包工具不是协议分析工具它在OSI第三/四层干活到了第七层就是边界。3. 编程题复盘Shell提取游戏日志 Python统计在线峰值3.1 题目形态与真实场景的映射畅游的编程题没有出算法考的是和业务日志强相关的两类场景。第一道Shell题给定一个游戏服务器访问日志某个入口网关的access log格式大概是IP 时间 请求路径 状态码 响应耗时要求统计出当天Top 10的请求来源IP并输出每个IP的请求次数。这道题哪怕不写Shell用awk一行流也能过但它有一层隐含考点当日志文件超过1GB甚至几个GB时你写的脚本还能不能跑完。你不说但面试官希望你主动考虑。我的解法是分两步先用awk做统计再排序取前10。awk {count[$1]} END {for (ip in count) print count[ip], ip} access.log | sort -rn | head -10。但这里有个小坑awk的数组是保存在内存里的如果IP基数极大比如几十万个独立IP内存占用会比较高。在实际场景中更稳妥的做法是先按IP做初步分组或者直接用sort配合uniq -c让外部排序来兜底。笔试环境下不需要过度设计但你可以在注释里写一句如果内存不足可以换用hash分桶再合并面试官一般都会注意到。第二道Python题给定一个记录了所有玩家登录登出时间戳的文件文件格式是uid, login_ts, logout_ts要求统计全天在线人数峰值和峰值出现的时间段。这道题不涉及到什么高深算法核心是差分数组/事件驱动的思路把所有login时间记作1logout时间记作-1然后按时间排序依次累加用当前值和历史最大值做比较就能在O(n log n)复杂度内算出在线峰值。我当时看到这道题还挺兴奋因为这就是典型的活动开始1、活动结束-1、随时更新max的模型和直播弹幕峰值、接口QPS峰值是一样的套路。但真正让我觉得有价值的是它背后的应用场景游戏运维日常要关注同时在线人数CCU如果CCU突增一台数据库或网关可能扛不住如果CCU掉得异常快可能说明服务器出了问题或者版本更新劝退了玩家。会写这个脚本对应的就是在监控系统里做一个按时间段聚合在线数的数据模块。3.2 Shell脚本里的防御式写法编程题评分不只看结果还会看脚本健壮性。我给自己定的及格线是能处理空文件、能兼容字段缺失、输出不夹带无关信息。比如一条日志可能是乱序的或者有一行的某个字段没值在Shell里就得注意字段编号不能直接$NF就当成耗时因为一条异常日志的字段数可能只有计划的一半。我写的时候习惯先做一步字段数检查awk NF5 { ... } file。这算是一个小细节但很多考生会忽略导致整个统计结果偏差。Python题同理要处理时间戳格式不统一、uid重复比如断线重连的情况。断线重连这块特别重要如果玩家先掉线再登录但没有logout记录你的统计就会多算一个人。笔试题里虽然没有把数据做成脏数据但你在答案里主动写了异常数据处理逻辑会明显比其他候选人高一个档次。另外我在写完脚本后会顺手用time跑一下把耗时写在注释里比如处理200万行耗时3.2秒。这个行为不一定加分但它传递了一个信息——你是真的在意线上性能的人而不是只要跑出结果就行的学生思维。4. 故障排查题线上数据库连接数被打满你的排查链路是什么4.1 连不上数据库的时候第一步先别慌畅游的卷子里有一道场景题几乎可以说是每届必考某个游戏区服的玩家突然全部无法登录后端服务报Too many connections给出你的排查步骤。这道题没有标准答案但它考察的是一个运维开发工程师的排查方法论——你的第一反应是打开搜索引擎还是沉着地看数据。我的排查链路如下先看数据库当前连接数和连接来源。执行SHOW PROCESSLIST;、SHOW STATUS WHERE Variable_nameThreads_connected;立刻确认连接数是否真的打满以及打满它的都是哪些IP、哪些账号。看连接状态是哪一阶段。是通过了鉴权在sleep还是正在执行慢SQL还是state显示Waiting for table metadata lock这三类状态对应完全不同的解法。看是不是应用从连接池出去的连接没收回。最常见的就是Java应用或Python应用里的连接池最大连接数配置过大比如应用开了100个实例每个实例连接池100数据库最大连接数却只配了1000瞬间就能打满。这也是我最先看应用层的原因。如果Threads_connected已经达到max_connections我不建议一上来就直接改max_connections把上限调大因为那通常只是把问题延后真正的原因没找到明天同一时间还会再次打满。正确的操作是先临时把max_connections调大一点比如从1000调到2000注意内存上限让服务先恢复再立刻查慢查询日志、processlist定位到底是谁把连接占着不释放。4.2 如何区分慢SQL导致和连接泄露导致这道场景题的进阶追问点是如果你在processlist里看到大量time很大的SQL分析方向是什么如果你看到大量state为Sleep、time几十秒上百秒的连接又是怎么处理前者是典型的SQL性能问题——缺少索引、单表数据量过大、复杂join、锁等待。处理方式explain看执行计划强制加上合适索引或者跟开发商量改写SQL必要时用pt-query-digest分析慢查询日志把Top SQL抓出来。后者则是连接池或代码层面的问题——某个线程拿连接后没有归还。处理方式去应用日志里找堆栈看是哪个接口在持有连接不释放或者把连接池的maxLifetime、idleTimeout调小逼它回收。还有一个我特别想强调的细节排查时别只盯着数据库本身。2019年那会儿Redis已经大面积使用如果数据库连接被打满是因为缓存雪崩——大量请求绕过Redis直接打到MySQL那你光查MySQL是永远查不完的。所以排查链路里一定要有一环是看缓存命中率、看上游调用链把视野拉到整个调用链路而不是盯着一个单点。提示线上故障处理的第一原则是先恢复再根因。不要试图在一个正在过载的数据库上做长时间的性能分析先通过扩容、限流、改配置等方式把火扑灭再降级到安全环境里慢慢复盘这样才是成熟运维的做事方式。4.3 复盘报告怎么写才显得专业这类题目有时候还会附加一问故障处理完之后需要输出一份复盘报告你怎么写我见过很多候选人的回答只写了我修好了数据库调大了连接数完事——这种答案基本是0分。一份合格的故障复盘至少要包含四个部分故障时间线与操作记录几点发现、几点定位、几点恢复、中间做了哪些操作、根因分析不只是连接数打满要挖到为什么打满、临时措施与长期改进项、谁来跟进和截止时间。笔试里写清楚这几块面试官就能看出你有很好的生产意识而不只是会敲命令。5. 设计题给一款MMO游戏项目组设计监控与发布体系5.1 监控体系怎么分层指标怎么选这套笔试题的最后一道大题是给一个MMO游戏项目组设计一套监控体系。MMO的特点非常鲜明服务器数量多、逻辑复杂、玩家在线时长大、对延迟极敏感、维护窗口有时间限制。所以监控体系不能只是装一个Zabbix添加几台主机而是要覆盖基础设施、中间件、应用、业务四个层面。我的答题思路分四层基础设施层CPU、内存、磁盘、网络、硬件告警用node_exporter或Zabbix Agent采集。中间件层MySQL连接数、慢查询、主从延迟、Redis命中率、内存碎片、阻塞命令、Kafka/RabbitMQ堆积量、消费延迟。应用层Java/Python进程的GC、线程池、接口RT、错误率、服内玩家心跳延迟。这部分更推荐用Prometheus Grafana做指标采集和可视化因为它的多维标签体系很适合按区服、按玩法维度聚合。业务层在线人数CCU、登录成功率、支付成功率、副本进入失败率、包体下载失败率。业务层指标通常需要开发配合埋点不能全靠黑盒探测。分层设计的好处是故障发生时你可以快速固定范围如果基础设施层没有明显异常就去看中间件层如果中间件也正常就看应用层和业务层。这种漏斗式排查路径在游戏运维里非常实用因为MMO的故障往往是发生在某一层而不是全局崩盘。5.2 告警分级与自愈别让告警变成噪音我在设计题里特意加了一个告警分级小节。很多候选人会把Zabbix/Prometheus的告警规则写得密密麻麻完全没有分级结果就是运维兄弟半夜被无关紧要的告警轰炸真正重要的故障反而被淹没。我的分级方案是级别示例响应目标P0核心数据库连接打满、全部区服不可登录立即响应5分钟介入P1单个区服在线人数异常下降、支付成功率90%15分钟响应P2磁盘使用率80%、某个接口RT翻倍工作时间处理观察确认P3某后台报表任务延迟记录跟踪后续优化P0和P1必须有电话/短信强告警P2和P3走IM群通知就够。这里有一个关键思想告警是为了让人做出决策不是为了制造噪音。如果一条告警从来没有人认真处理过它就不配存在。自愈部分2019年的笔试题没有那么前沿但我会答上面对常规问题可以先通过脚本自动处理。比如发现某个游戏服负载持续偏高时可以自动拉起同配置的新服做分流发现磁盘空间不足时自动清理过期的日志归档。但自愈必须带熔断机制——如果自动化脚本连续失败三次必须停止操作并升级人工否则就会变成故障放大器。5.3 灰度发布和回滚方案游戏版本的运维难点设计题里另一部分考察发布体系因为游戏维护和发版是家常便饭。游戏发版有个特殊难点客户端更新是异步的玩家可能还有人卡在旧版本而服务器端必须要做到新旧版本同时兼容一段时间同时游戏内配置表经常和代码一起发又要保证数据一致性。我的回答框架是先灰度再全量。先在一个小区服上发新版本观察日志、错误率、玩家反馈验证稳定后再扩展到更多区服。2019年K8s已经比较成熟可以用Deployment的滚动更新策略先提高maxUnavailable和maxSurge的步长来控制灰度粒度。配置和代码分离。游戏策划经常改数值不可能每次都发二进制包。配置中心Apollo或自研负责动态下发配置数据库变更用专门的migration脚本管理保证可重复执行、可回滚。回滚预案。发布前就准备好回滚到上一个版本的镜像/包数据库变更要做前滚兼容设计——新代码在老表结构上也能跑老代码在新表结构上也不能崩。这个双可运行原则是游戏运维最容易栽跟头的地方。发布窗口控制。MMO一般在凌晨低峰期维护但整个发布过程要尽可能缩短。把检查项备份、配置校验、DB迁移检查提前做成脚本避免到了半夜才手忙脚乱。设计题不要求写出能跑的代码它真正考察的是你有没有见过一个复杂的系统是怎么被安全地变更的。能写出灰度、回滚、兼容、备份这几个关键词背后的逻辑这道题就已经稳了。6. 校招备考的真实建议与个人体会最后聊点备考层面的东西这部分可能比具体某道题更值得你收藏。运维开发这个岗位绝对不是运维开发两张皮。我见过很多候选人Linux基础很扎实但让他写一个自动化脚本就暴露了——要么是满屏的临时变量、硬编码路径、没有函数封装要么是脚本一遇到异常输入就崩。搜狐畅游的笔试题虽然不会直接考软件工程但你在编程题和设计题里展现的代码组织能力面试官一眼就能看出来。我的建议是备考阶段把日常的运维操作尽量脚本化和工具化不要满足于手动敲命令能通而是想想如果这条命令要每天跑、要在100台服务器上跑我应该怎么封装。多练场景题少背八股。2019年是运维模板化考题还比较流行的时候很多人还在背iptables五链四表TCP三次握手四次挥手的八股但这套题明显更偏重线上出了问题你怎么去判断。我当时给自己做训练的方法是收集真实的生产故障案例公司内部、开源社区、技术博客不看解法只凭自己的知识储备一步步写排查方案然后再对照别人的复盘找差距。这个习惯回头看特别有用因为畅游的故障排查题本质上就是在模拟真实的生产事故。笔试时学会展示过程。很多人做设计题只写结论——用Zabbix做监控用K8s做发布——然后就没了。但笔试题的得分点往往在你的思考过程里。我习惯在卷子上写清楚为什么选型A而不是B如果遇到这个边界情况怎么处理系统的瓶颈会在哪里。哪怕字迹丑、框架乱只要思路清晰面试官是能看出来的。当年的遗憾与收获。我自己当年在这套题上栽过一个小跟头Shell统计题的日志格式里有个字段是HTTP状态码响应耗时混在一起中间用冒号分割我没仔细看直接拿$NF当耗时用了导致最后统计结果差了好几个数量级。笔试后复盘才意识到这就是真实日志解析的常态——字段永远没有你想象的那么规整。这个教训到现在都刻在我脑子里处理任何日志之前先head几行看看格式再动手写处理逻辑。这比什么都重要。