
最近看到有人在讨论哔哩哔哩2019秋招技术岗的笔试题其中系统工程师这道题很有意思题目大意是问候选人如何在生产环境从零搭建一个系统并做好后续维护。乍一看这就是个开放性的聊天题但能看出面试官想考察的点其实非常深它既考动手能力又考规划意识还考你有没有真正经历过线上环境的毒打。这篇文章我就以自己多年做系统工程师和运维的经验把这道题完整拆开聊一遍。如果你正在准备系统工程师、运维开发、SRE这类岗位的面试或者你刚接手一套没人维护的老系统想从头梳理一遍搭建和维护的完整思路这篇内容可以直接当参考手册用。我会按照从需求分析、清单梳理、实际部署到长期维护的节奏来展开尽量把命令和参数也贴出来方便你对着练。1. 从笔试题看系统工程师的核心能力模型1.1 面试官到底想从“从零搭建”里看到什么“从零搭建一个系统”这句话如果没上过生产环境很容易理解成“装个操作系统配个网络装几个软件”。但真正在互联网公司里系统工程师面对的不是一台机器而是整个业务稳定性的底座。面试官问这道题其实想看的是你能不能把一个空壳的裸机变成一台能承载业务流量、能扛故障、能长期维护的服务器。我当年面试别人时最怕听到的答案就是把启动过程背一遍u盘装系统、分区、配IP、装数据库、启动服务然后就没有然后了。这种答案只能说明你“用过”Linux但证明不了你“理解”生产环境。生产环境的核心就四个词稳定、可控、可观测、可恢复。从零搭建的每一步其实都应该围绕这四个词来做决策。所以这道题表面上是“搭建系统”实际上考察的是一个人在不确定性中做工程决策的能力。机器什么配置、系统选哪个版本、磁盘怎么规划、服务怎么部署、监控怎么接入、出了问题怎么回滚这些都不是填空题而是需要根据业务场景动态权衡的选择题。1.2 系统工程师和运维的边界为什么越来越模糊这个岗位在B站这种互联网公司里其实已经不怎么区分“系统工程师”和“运维工程师”了。早期传统的运维可能只管网络、服务器、机房系统工程师偏向OS和底层调优但现在大家都叫SRE或者系统工程师日常工作早就混在一起了。原因很简单业务迭代越来越快如果系统工程师只管装系统运维只管看着监控中间就会出现巨大的真空地带。比如你装好了OS但没配内核参数业务一上线就出现句柄耗尽或者你搭好了环境但不考虑日志清理半年后磁盘被打满。这些问题都不是单一角色的锅而是“从搭建到维护”这个链条没有打通。我自己的体会是这道题就是想筛选出具备“全生命周期”视角的人。你会装机器只是第一关后面的容量规划、安全加固、自动化、监控告警、备份恢复才是真正拉开差距的地方。所以下面我讲整个流程的时候也会刻意把维护思维前置而不是等系统跑起来了再亡羊补牢。2. 系统搭建前的需求分析与方案设计2.1 先问清楚三个问题再动手很多人一上来就翻ISO镜像、找装机U盘但我建议你先压住冲动先问自己三个问题这台机器要跑什么业务预期流量和数据量有多大你手里有多少资源人力、预算、时间这三个问题直接决定整个技术选型。如果是跑一个日活几万的小型Web应用单台服务器加云数据库就够了如果是核心支付链路哪怕再小的系统也得双机热备跨机房容灾如果只是内部测试环境那连RAID都可以省定期快照反而更灵活。我见过太多事故根源根本不在某个命令敲错了而是最初设计时压根没搞清楚业务形态把测试环境的方案直接搬到了生产。举个例子我曾经帮一个团队排查数据库频繁OOM的问题排查到最后发现这台机器的swap文件压根没创建数据库一跑起来就吃满内存。你说是运维不会配置吗不是是当时上机器时没有人告诉搭建的人这是个数据库节点他就按默认模板装了个系统。所以第一件事永远是沟通需求不要上来就动手这个习惯能省掉后面90%的麻烦。2.2 选择操作系统和硬件时的“保守策略”操作系统选型是个容易被低估的决策点。很多新人喜欢追新版本觉得内核新、功能多就爽。但生产环境恰恰相反绝大多数公司会用当前主流稳定版本比如CentOS 7/8或者它的替代品Rocky Linux、AlmaLinux、Ubuntu 18.04/20.04 LTS。为什么因为新版本往往意味着新的坑而生产环境最怕“不确定”。拿我自己的例子来说曾经在一台新内核的机器上部署一套老业务的服务结果跑起来之后网络连接经常超时查了半天发现是新内核默认启用了某个拥塞控制算法和老业务的网络栈行为不匹配。最后只能换回旧内核版本的镜像重新部署。所以我的建议是选你团队最熟悉的版本而不是最新版本尤其不要在生产环境里当小白鼠。硬件选型方面CPU要关注主频和核数内存要先算业务需要再留20%-30%余量磁盘则要重点考虑IOPS和容量。一个比较实用的经验法则是如果业务是数据库或消息队列这类IO密集型优先选SSD或NVMe盘内存尽量配大如果是Web服务或计算任务CPU核数和网络带宽的重要性会更高。这些信息最好在项目启动前就确认清楚否则后面扩容等于重搭。2.3 单机、集群还是上云取决于业务容忍度搭一个系统前还得想清楚部署形态。单机部署最简单但很多人不知道单机部署的“隐藏成本”——只要你依赖这台机器它宕机你就得背锅。所以哪怕业务量再小我也建议至少做到“数据可恢复”也就是备份机制必须到位。集群部署就复杂得多涉及负载均衡、数据一致性、脑裂处理等一系列问题。我的建议是在业务没发展到一定规模之前不要盲目追求高大上的集群方案。两个节点的KeepalivedNginx做高可用或者直接用云平台提供的负载均衡和托管数据库往往比你自己搭一个看起来很厉害的K8s集群要稳得多。这里还要提一句“上云”的选择。现在很多公司已经默认上云了但云主机同样需要系统工程师做OS层面的初始化、安全加固和监控接入只不过不用再关心硬件和机房而已。不管在哪种形态下系统工程师的职责都没有变——把系统的可用性和稳定性做到最好。3. 从裸机到可用系统的完整实操流程3.1 装机与磁盘规划的标准动作假设现在你拿到一台裸机第一步不是直接开始分区而是先确认固件模式、磁盘数量和阵列卡状态。如果这台机器有多块磁盘一般建议先做硬件RAID比如两块盘做RAID 1用于系统盘多块盘做RAID 5或RAID 10用于数据盘。RAID级别选择要按业务需求来不是越高越好。RAID 0性能好但没有冗余一块盘挂了数据全没RAID 1冗余好但空间利用率只有50%RAID 5兼顾性能和空间但写性能一般而且大容量磁盘重建时间长RAID 10是性能和冗余的平衡点代价是成本高。我个人的习惯是系统盘RAID 1数据盘至少RAID 10如果公司预算紧张且业务允许丢失少量数据才考虑RAID 5。分区方案上我推荐把关键目录单独分区比如/date或/data单独划分给业务数据用。这样做的好处是日志打满数据盘时不会拖垮系统盘备份数据也不会占用根分区空间。swap分区要不要单独分如果内存充足且业务对性能敏感swap可以设小一点比如4GB避免因为swap频繁换页导致性能抖动。但也要留一点别彻底禁掉有些应用的依赖库在某些异常情况会用到swap兜底。3.2 系统初始化的关键配置项清单装完系统后进入系统初始化和配置阶段。初始化脚本里我一般会做这几类事情设置主机名和DNS、配置NTP时间同步、关闭SELinux、调整文件句柄数、优化内核参数、配置yum或apt源、创建运维用户并配置sudo、加入跳板机或堡垒机的管理范围。这里重点说几个容易被忽略的细节文件句柄数和进程数限制。这个不调高并发下必然会出问题报错通常是too many open files。调整方式是用ulimit或修改/etc/security/limits.conf比如设置* soft nofile 655350和* hard nofile 655350同时把fs.file-max也在/etc/sysctl.conf里调大。内核参数里最值得关注的是网络相关的几个。例如net.core.somaxconn默认是128并发连接稍微高一点就会触发队列溢出net.ipv4.tcp_tw_reuse和net.ipv4.tcp_fin_timeout需要根据业务场景配置手动打开一般能优化TIME_WAIT过多的问题但不能无脑套用vm.swappiness建议调整为10以下避免系统过度使用swap导致性能下降。SSH安全配置也要在这一步做好。建议修改默认端口、禁止root直接登录、启用密钥登录。虽然公司内网有堡垒机会统一管控但多一层安全措施总归不是坏事。我维护的服务器凡是没改过默认22端口又面向公网的不出一个月肯定被扫。3.3 常用基础组件与运行环境的部署思路系统和网络基础配置完成后接下来就是安装业务运行所需的基础组件。这部分的顺序和方式也很重要我最推荐的方式是用配置管理工具比如Ansible、SaltStack来统一做而不是一台一台手工敲命令。手工操作最大的问题不是慢而是每个人装的版本、路径、配置不一致后期维护成本巨大。以最常见的LAMP/LEMP环境为例安装PHP或Java运行环境时要注意版本一致性。用包管理器装的版本通常偏旧如果需要指定版本推荐使用官方二进制包或容器镜像。比如Java应用直接下载JDK 8/11/17对应版本解压后设置JAVA_HOME环境变量就行比用系统包管理工具更可控。数据库部署是基础组件里的硬骨头。MySQL初始化有几点必须注意第一步要设置root密码和删除匿名用户第二步要调整存储引擎和字符集避免建表时默认乱七八糟的编码第三步要配置binlog和自动清理策略第四步要设置慢查询日志方便后面排查SQL性能问题。如果用的是云数据库那也要确认自动备份开关是否打开并且建议设置至少7天的备份保留周期。3.4 程序部署与上线发布的标准化流程环境准备好之后程序部署和上线就是最需要规范化的环节。我见过不少团队的程序发布方式是开发把代码打包丢给运维运维手动传到服务器然后重启服务。这在只有一两台机器的时候勉强能跑一旦机器多了肯定出乱子。一个标准的程序发布流程至少应该包括这几个要素产物统一管理、版本回滚机制、灰度发布策略、发布前后的健康检查。比如用GitLab CI或Jenkins构建出产物存到Nexus或对象存储里部署时从统一地址拉取而不是到处传输jar包或tar包。发布时先摘掉一台机器的流量更新完毕后再放量确认无异常后再继续下一批。这样即使新版本有问题影响面也能控制在最小范围。同时服务一定要配置成守护进程用systemd、supervisor或者Docker的restart策略管理都可以。我不太建议直接用nohup ... 的方式启动服务因为进程一旦意外退出没人拉起。采用systemd管理的话还要配置日志输出路径和遇到OOM时是否自动重启的策略这些细节决定了系统能不能“自己恢复”。3.5 上线前的清单式自检清单上线前一定要做一次全面自检我每次都会把自检项列成清单一项项核对。这个习惯帮我挡掉过很多低级事故。自检清单大致包括时间同步是否正常执行chronyc sources -v或ntpq -p确认服务器时间偏差在100ms以内磁盘空间是否足够df -h查看所有分区特别关注根分区和数据分区剩余容量关键端口是否监听ss -lntp确认服务端口和应用预期一致防火墙是否放行检查firewalld或iptables规则避免服务通了但端口被拦权限和属主是否正确确认运行服务的是专用账号不是root日志目录是否存在且有写权限这个不起眼但没注意就会导致服务启动失败或静默丢失日志监控是否已接入至少确认CPU、内存、磁盘、网络这四项基础指标能采集到备份任务是否已创建确认crontab中备份任务已配置并手动执行过一次这个过程看起来繁琐但真的能逼着你把很多“我觉得没问题”的地方查清楚。上线不是终点恰恰是维护阶段的起点。4. 生产环境日常维护与故障排查的关键经验4.1 配置变更必须走“先备份、后变更、可回滚”系统上线之后日常维护的挑战才真正开始。一个最常见的导致故障的原因不是初始搭建做得不好而是后续变更操作失误。比如说某天你为了优化Nginx性能改了nginx.conf里某个参数结果忘记测试就直接reload全站直接502。所以我对变更管理有个铁律任何配置变更之前先备份原文件变更过程中尽量用“增加”而不是“替换”的方式变更后要立刻验证服务状态。出了问题第一步不是调参数而是恢复原状。这个“先回滚再排查”的顺序非常重要因为如果你在错误配置的基础上继续调参只会越调越乱。另外强烈建议把配置管理和自动化工具用起来。Ansible的playbook天然具备幂等性同一个任务执行两遍结果是一致的这比手工改文件安全得多。用自动化工具管理配置还顺带解决了“某台机器配置漂移”的问题。我在团队里推行过一个简单规矩谁改配置谁负责把改动纳入版本库没纳入版本库的修改一律视为不允许执行后一旦与版本库内容不一致以版本库为准自动回滚。4.2 常见故障的排查思路与命令速查平时值班最常碰到的问题基本集中在CPU、内存、磁盘、网络这四类。我把排查思路整理成一张速查表新人拿去就能用。故障现象可能原因排查命令处理建议CPU使用率过高业务负载突增、死循环、GC频繁top、pidstat -p PID 1、jstackJava应用找到占CPU高进程确认是否预期行为必要时限流或扩容内存不足/OOM内存泄漏、堆内存设置过大、无swapfree -h、dmesg | grep -i oom、ps aux --sort-%mem定位耗内存进程调整JVM参数或升级配置磁盘空间满日志未清理、临时文件堆积df -h、du -sh /* 2/dev/null清理日志和临时文件增加日志轮转策略IO等待高/响应慢磁盘性能瓶颈、慢SQL、大量随机读写iostat -x 1、iotop、数据库慢查询日志优化SQL或升级SSD必要时做读写分离网络丢包/延迟高带宽打满、网卡问题、连接数超限sar -n DEV 1、ping -f、ss -s检查带宽和连接数限制调整内核参数或扩容端口无法访问服务未启动、防火墙拦截ss -lntp、systemctl status service、firewall-cmd --list-all确认服务状态和防火墙规则检查监听地址这张表虽然简单但覆盖了生产环境90%以上的第一波故障。我自己的经验是排查故障时不要一上来就怀疑内核或网络设备先看资源监控、再看进程状态、最后才动到底层这个顺序能少走很多弯路。很多时候所谓“疑难杂症”最后发现就是日志文件把磁盘占满服务写不了日志就直接假死了。4.3 监控告警体系怎么搭才算够用监控体系是维护阶段的另一块基石。刚上线时机器少可能用crontab写个脚本发现CPU高了就发个邮件勉强够用。但系统一多这种原始方式肯定撑不住一定要上统一的监控平台。我常用的组合是Prometheus node_exporter Grafana再加Alertmanager做告警。node_exporter负责采集主机层面的指标Prometheus负责存储和查询Grafana做可视化Alertmanager把告警推到钉钉、企业微信或者邮件。这套方案组件成熟、社区活跃一个系统工程师足够维护得动。监控指标方面基础四项是必须的CPU使用率、内存使用率、磁盘使用率和inode使用率、网络流量。另外按业务重要程度再配上进程存活监控、端口存活监控、关键日志关键词监控。告警规则也要讲究不要“一告警就打电话”而要设置合理的阈值和持续时间。比如CPU使用率持续5分钟超过90%才告警避免瞬时抖动骚扰到人。4.4 备份与容灾平时用不到不代表可以不做备份是维护工作中最容易被忽视但又最关键的一环。很多人觉得云平台有快照数据库有主从就认为不需要额外备份了。但快照是整机级别的只能防坏盘和误删主从同步是逻辑复制万一出现误执行drop database的情况从库也会一起被删。所以真正的数据安全必须靠独立于运行环境之外的备份。备份方案设计要考虑三点备份内容、备份频率、备份存储位置。业务数据至少要每天全量备份一次重要数据库建议每天全量binlog增量备份文件要存到独立的存储或另一台机器上。同时要定期做恢复演练否则备份可用性完全没保障。我个人坚持“没有经过恢复演练的备份等同于没有备份”这个原则。曾经有位同事特别有自信说他们的备份任务每天都跑结果真到恢复的时候发现备份脚本从某一天开始一直静默失败备份文件全是0字节那场面真的很难收场。容灾意味着要考虑“这台机器彻底没了怎么办”。在云上可以考虑不同可用区部署双节点在自建机房至少要保证配置文件和代码有版本管理数据有异地备份。容灾的等级取决于业务的RTO恢复时间目标和RPO恢复点目标先把这两个数字定义清楚再设计容灾方案就不会过度设计也不会裸奔。4.5 把重复性工作自动化让自己从“救火队员”变成“系统优化者”最后想说一个系统工程师成长路径上的关键转折当你开始觉得日常维护都是重复劳动的时候就是应该大力投入自动化的时机了。日常巡检、日志清理、备份执行、服务重启、版本发布这些事情都可以用脚本和工具自动化。刚开始可能只是几个简单的Shell脚本慢慢可以引入Ansible、Python、CI/CD流水线。自动化并不一定要一步到位搭什么高大上的平台从一个操作梳理成脚本从脚本进化为工具从工具再进化为平台这条路是系统工程师最有价值的成长方向。我自己最大的一次效率提升就是花了两个周末把服务器初始化做成了一套脚本从拿到机器到完成全套初始化只需要十几分钟而且不会漏掉任何一项配置。以前手动装一台机器至少要半天还经常忘配NTP或者忘了关SELinux现在这些低级错误基本绝迹了。自动化不只是省时间更重要的是消除人肉操作带来的不确定性。5. 给准备面试和刚入行的同学几点实在建议这道笔试题如果出现在面试里我会建议你用“分层回答”的方式组织答案。第一层讲目标保证业务稳定运行第二层讲流程需求分析、设计、部署、维护的闭环第三层讲细节具体命令、参数、工具第四层讲反思失败案例、改进措施。大多数人只能讲到第二层能讲到第三层的人已经少一半能讲到第四层的人才是真正有生产经验的人。如果刚入行最好的学习方式不是死记硬背命令而是自己用虚拟机完整地搭一套系统从安装Linux开始到部署一个NginxMySQLPHP或Java应用然后试着把它拆了再重建直到整个过程不需要查文档。做完这一步再尝试加监控、写自动化脚本你会发现对系统工程师这个岗位的理解会完全不同。设备条件有限的话现在一台普通电脑开几个虚拟机完全没问题或者用云平台的免费额度也不失为一个好选择。环境和工具不重要重要的是亲手走完“搭建—出问题—排查—解决—复盘”的完整循环。这个循环走得越多你在真正生产环境里就会越从容。最后再分享一个小技巧搭建任何系统时都顺手把“为什么这样做”记录下来。比如你选择RAID 10而不是RAID 5理由是数据安全性要求高你关闭SELinux理由是业务组件与它有兼容性问题。这些记录将来无论是做交接、做复盘还是面对面试官的追问都是你最宝贵的财富。系统工程师这个岗位归根结底拼的不是手速而是判断力而判断力恰恰来自一次次“知道为什么这么做”的积累。